So, my kids' math teacher sent home a YouTube video on place values and ascending order. It was good, but watching it wasn't doing much. So over the weekend I built them a game instead.
It's called Tumbling Towers — source on GitHub.
The Concept
You're dealt two random cards, say 2 and 7. You can swap them to form 27 or 72, then drop that number into an empty slot in a tower. The rule: all blocks must be in ascending order from bottom to top. If you can't place either combination without breaking that order, the tower falls and the game ends.
As levels go on, the tower gets taller. You start having to think about spacing and leave enough room between numbers, or you'll paint yourself into a corner. It's one thing to know 72 > 27. It's another to decide where 27 belongs when you've already got 15 at the bottom and 60 in the middle.
The Stack
I wanted it working fast, so I used what I know:
- React 19 & Vite 6
- TypeScript
- Tailwind CSS (v4): Went with a retro pixel-art look and custom utility classes like
.pixel-borderwithout leaving the component - Motion: When the tower falls, the blocks scatter. Both kids cheered the first time it happened, which told me the animation was worth it!
- Local Storage for a high-score tracker
The Logic
The core validation checks that a block maintains ascending order when placed:
function isValidPlacement(tower: (number | null)[], index: number, number: number) {
if (tower[index] !== null) return false;
for (let i = 0; i < index; i++) {
if (tower[i] !== null && tower[i] >= number) return false;
}
for (let i = index + 1; i < tower.length; i++) {
if (tower[i] !== null && tower[i] <= number) return false;
}
return true;
}
Before each turn, we also check whether the game is already over, testing both digit combinations against every empty slot:
function canPlaceAnywhere(tower: (number | null)[], digits: [number, number]) {
const num1 = digits[0] * 10 + digits[1];
const num2 = digits[1] * 10 + digits[0];
for (let i = 0; i < tower.length; i++) {
if (tower[i] === null) {
if (isValidPlacement(tower, i, num1) || isValidPlacement(tower, i, num2)) {
return true;
}
}
}
return false;
}
The Result
They've been playing it a lot. The thing I wasn't expecting: they started speaking their reasoning out loud. "I shouldn't make 72. I only have one slot left at the top. I'll make 27 and put it near the bottom." That's more than I got from the video.
Play Tumbling Towers here: https://swindon.github.io/tumbling-towers/
Have you ever built something to teach your kids a concept? I'd love to hear about it in the comments. And if you found this useful, I drink too much coffee ☕
Top comments (0)