DEV Community

Scott Windon
Scott Windon

Posted on • Edited on

How I Built A Game to Help My Kids Learn

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 Towerssource 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-border without 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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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)