DEV Community

Cover image for Tower Before Dusk: I Built a Puzzle Game for Humans and AI
Daniel Balcarek
Daniel Balcarek Subscriber

Posted on

Tower Before Dusk: I Built a Puzzle Game for Humans and AI

June Solstice Game Jam Submission

This is a submission for the June Solstice Game Jam

It's interesting how the most exciting ideas always arrive when I have basically no time to work on them.

A few weeks earlier, I had finished my submission for the GitHub challenge by bringing an old WinForms game back to life. That project turned out to be a lot of fun. Then Sylwia Laskowska published a great article about Google's WebMCP. The idea fascinated me, but I wasn't sure where I could actually use it. Then the June Solstice Game Jam was announced. The idea hit me like a lightning bolt: What if I made a game that both humans and AI could play?

Let's do it.

What I Built

I created a puzzle game with a solstice theme called Tower Before Dusk. The goal is simple: reach your home tower before sundown. Every action costs time. Every step brings sunset a little closer. Rivers block your path, rocks force detours and the only way across water is to collect enough wood and build bridges. Move too much, collect unnecessary resources, or choose the wrong path, and night will arrive before you make it home.

The challenge isn't just solving the puzzle. It's solving it efficiently.

And apparently, that's difficult for both humans and AI.

Video Demo

In this demo, Gemini 3.1 Flash-Lite tries to solve the level using the exposed game tools. It fails, then I restart the level and solve it manually. That failure is part of the point: the tools worked, but reasoning through the puzzle was still hard for the lightweight model.

Code

GitHub logo Gramli / tower-before-dusk

A TypeScript puzzle game demonstrating WebMCP, where humans and AI solve the same challenges under the same rules.

Tower Before Dusk

TypeScript Vite Canvas

Tower Before Dusk is a tile-based puzzle game about reaching the tower before sunset. Plan each route carefully: every move spends daylight, trees provide wood, and water can only be crossed by building bridges.

The game is built as a modern browser app with TypeScript, HTML canvas, and Vite. It also exposes a small model-context interface so an assistant can read the current map and submit a complete action plan for replay in the UI.

Features

  • Three handcrafted puzzle levels with different tower layouts and move limits
  • Daylight system that tracks the move budget from morning through sunset
  • Trees that are collected automatically for wood when entered
  • Bridge building over water, consuming two wood per water tile
  • Rocks, water, bridges, towers, and sprite-based terrain rendering
  • Responsive canvas scaling for different browser sizes
  • Keyboard-driven play with restart and help shortcuts
  • HUD for level name, wood count, moves usedโ€ฆ

How I Built It

Since WebMCP was completely new to me, I didn't want to jump straight into building a game without understanding how it worked first.

So I generated a simple Vite application and experimented with a tiny counter tool:

const incrementCounterTool = {
  name: "incrementCounter",
  description: "Increments the counter by a specified value.",
  inputSchema: {
    type: "object",
    properties: { value: { type: "number" } },
  },
  execute: async ({ value }: { value: number }) => {
    const counter  = document.getElementById('counter') as HTMLElement;
    if (counter) {
      const currentValue = parseInt(counter.innerText, 10) || 0;
      counter.innerText = (currentValue + value).toString();
    }
  },
  annotations: {
    readOnlyHint: false,
    untrustedContentHint: true
  },
};
Enter fullscreen mode Exit fullscreen mode

When the AI successfully incremented the counter and I saw the value changing in the browser, I knew I could continue.

Of course, my game would be a little more complicated than a counter. At first, I considered letting the AI inspect the game state after every move, but then I realized I would burn through tokens incredibly fast. So I came up with another approach.

Instead of playing move by move, the AI would receive the entire game state, understand the rules, and generate one complete plan to reach the goal, but then another thought appeared:

"How do I make it look like the AI is actually playing?"

The answer was surprisingly simple. The AI would return a sequence of actions and my game loop would replay them with a short delay between moves. From the player's perspective, it would look like the AI was thinking and playing in real time.

Even better, it fit perfectly with the game's architecture, because human players already interact through keyboard actions that modify the game state.

With that idea in mind, I built the MVP.

I did it the "old-fashioned" way: player first. (Almost like mobile-first, except with fewer trendy conference talks.)

I also have to admit that I stole some core ideas from my previous EasterGame project. At this point, I'm starting to suspect I accidentally built the beginnings of a tiny puzzle game engine.

The first playable level looked like this:

Level MVP

The game worked, You could reach the tower and win. It was finally time to bring AI into the picture.

Based on the original idea, I created two MCP tools:

  • getGameState
  • submitPlan

getGameState provides the complete state of the current level, including objectives, rules, available actions, and the visible map:

export const gameState: GameState = {
  objective:  "Reach G before sunset using as few moves as possible. Do not collect unnecessary wood.",

  legend: {
    P: "player start position",
    ".": "land / walkable tile",
    W: "wood / walkable tile, can be collected",
    "~": "water / blocked unless player has enough wood",
    R: "rock / blocked tile",
    B: "bridge / walkable tile created after entering water",
    G: "goal / walkable tile",
  },

  rules: {
    map:
      "visibleMap is an array of map rows from top to bottom. The first symbol in each row is x=0, and rows start at y=0. Symbols are separated by spaces for readability.",

    movement:
      "The player can move one tile up, down, left, or right. Each movement costs 1 move.",

    rock:
      "Rock tiles marked R are blocked and cannot be entered.",

    wood:
      "Tree tiles marked W are walkable, but entering W automatically collects the tree. This costs 1 extra move, adds 1 wood, and removes W from the map. Because collecting wood costs an extra move, avoid W unless the wood is needed to cross water.",

    water:
      "Water cannot be entered unless the player has at least 2 wood.",

    bridge:
      "When the player moves into a water tile with at least 2 wood, a bridge is built automatically on that single water tile. This costs 1 extra move, consumes 2 wood, and changes only that one water tile to B. Other connected water tiles remain water.",

    bridgeLimit:
      "Each bridge covers only one water tile. If there are multiple water tiles in a row, the player needs enough wood to build one bridge per water tile.",

    strategy:
      "Use the minimum number of actions needed to reach G. Do not collect wood unless it is required to build enough bridges. Avoid stepping on W unless that wood is necessary. Extra wood has no value at the end.",

    goal:
      "The player wins immediately when reaching G using no more than the maximum allowed moves.",

    lose:
      "The player loses if the move budget is exhausted before reaching G, or if no valid action can reach G.",
  },

  actions: [
    "MOVE_UP",
    "MOVE_DOWN",
    "MOVE_LEFT",
    "MOVE_RIGHT",
  ],

  remainingMoves: 30,
  wood: 0,

  visibleMap: [
    "P . W W W W ~ ~ G",
  ],
};
Enter fullscreen mode Exit fullscreen mode

The second tool, submitPlan, accepts the AI's proposed solution:

    inputSchema: {
      type: "object",
      properties: {
        actions: {
          type: "array",
          items: {
            type: "string",
            enum: [
              "MOVE_UP",
              "MOVE_DOWN",
              "MOVE_LEFT",
              "MOVE_RIGHT",
            ],
          },
        },
        summary: { type: "string" },
      },
      required: ["actions"],
      additionalProperties: false,
    },
Enter fullscreen mode Exit fullscreen mode

The AI returns an array of actions such as:

["MOVE_UP","MOVE_DOWN","MOVE_LEFT","MOVE_RIGHT"]
Enter fullscreen mode Exit fullscreen mode

Then submitPlan feeds those actions into the game loop, which replays them with a short delay so players can watch the AI attempt to solve the puzzle.

Pretty neat, right?

Well... It worked. The AI successfully called both tools and then it immediately exposed another problem: my level design was too difficult. Even Level 1 turned out to be surprisingly challenging for the models I tested.

For development and testing, I used the WebMCP Inspector with the Gemini models available through the free API tier:

  • Gemini 3 Flash Preview
  • Gemini 3.1 Flash-Lite
  • Gemini 3.5 Flash

All three models correctly called both tools, but none of them managed to generate a valid solution for just Level 1. At that moment, I realized that perhaps I had been a little too optimistic about my puzzle design, so I lowered the difficulty. Eventually, AI finally managed to reach the tower and complete the first level.

Victory ...Well... a small victory. I'm fairly sure stronger models would perform better on the harder levels, but I also didn't want to discover how much puzzle-solving curiosity could cost in API tokens.

If you'd like to try it yourself, this is the prompt I used:

You are playing Tower Before Dusk.

First call getGameState. Study the objective, legend, rules, remainingMoves, wood, and visibleMap.

Create one complete plan to reach G before sunset. Use only the listed actions. Account for move costs, automatic bridge building, wood collection, rocks, water, and remainingMoves.

Then call submitPlan exactly once with the full action list. Do not submit partial plans.
Enter fullscreen mode Exit fullscreen mode

Interesting Thoughts

Going into this project, I assumed the hardest part would be integrating WebMCP into the game, but it wasn't.

The real surprise was discovering that even simple puzzle levels weren't trivial for AI models. The tools worked almost immediately, but designing levels that felt straightforward to humans while making AI struggle turned out to be an interesting challenge.

It made me realize that puzzles we consider "easy" often rely on intuition and reasoning patterns that aren't as obvious to language models as I had expected.

The Sunset Arrives

And that's how Tower Before Dusk came to life. I set out to build a game for the June Solstice Game Jam and explore an experimental technology, discovering that simple-looking puzzle games aren't necessarily simple for AI and creating something that humans and language models can both struggle to beat.

Honestly, I think that's a pretty fitting result for a game about racing against the setting sun.

Top comments (36)

Collapse
 
hemapriya_kanagala profile image
Hemapriya Kanagala

Daniel, this is a really creative idea. Making a game that both humans and AI can play is not something you see every day.

I'll definitely give it a try when I get some time. Curious to see whether I can beat the AI on the harder levels ๐Ÿ˜„

Collapse
 
gramli profile image
Daniel Balcarek

Thanks, Hemapriya! โค๏ธ

The first three levels are intentionally easier because the lightweight models were already struggling with them. Levels 4 and 5 should feel more like normal puzzle difficulty.

And Iโ€™m hoping to add a few genuinely hard ones over the weekend too. ๐Ÿ˜…

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Wow, another addictive game! ๐Ÿ˜„ Saving this one for after work. BTW, Google should probably send us some stickers for all the free webMCP promotion we're doing ๐Ÿ˜‚

Collapse
 
gramli profile image
Daniel Balcarek

That would actually be amazing! ๐Ÿ˜„ But without your article, I probably would not even know WebMCP existed, so most of the credit goes to you! โค๏ธ

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Deal! ๐Ÿ˜„ In that case, I'll take two stickers! ๐Ÿ˜‚โค๏ธ

Thread Thread
 
gramli profile image
Daniel Balcarek

Absolutely! You definitely deserve both of them!๐Ÿ† ๐Ÿ˜‚

Collapse
 
hiper2d profile image
Aliaksei Zelianouski

The "AI struggled" result might be model tier plus format more than AI in general. A move-budgeted tile puzzle leans on exactly what trips LLMs up - spatial reasoning, counting, and one-shot planning with no feedback loop - and you tested the lightweight Flash models, which is where that breaks first. Worth a frontier run before lowering difficulty: I've seen people report Fable 5 is genuinely strong at spatial reasoning in games now, so the gap might be tier, not a ceiling.

Either way it comes back to balance, and that's why I build conversational games. Mixing human and AI players is just easier when the game advances through dialogue - the challenge becomes language, the one thing these models are genuinely good at, so they sit near human level instead of way below or way above. Assuming you can prompt them to stay on track, of course.

Collapse
 
gramli profile image
Daniel Balcarek

Yep, I agree and I actually described it in the article that this is a limitation of the models I used. Stronger models would probably perform much better.

And as you said, conversational games are a much more natural fit for these models, but then it would not be as much fun for me to challenge them with something outside their comfort zone. ๐Ÿ˜„

Collapse
 
utkarshbansal01 profile image
Utkarsh Bansal

Loved the game, it's really addictive. I have a few suggestions though.
Right now, the game feels a bit too much like calculating moves, similar to a puzzle like checkers. Instead of giving a strict move limit and placing the castle far away, you could move the castle closer and give players some extra moves.

Example: if reaching the castle takes 32 moves, give the player 40 moves.
The extra 8 moves could be used to collect extra resources that could be used in the future levels. This will add a layer of strategy on top of it instead of forcing a single optimal path.

With this you can also create a leader board or personal best score on the min moves taken to reach the castle.

Collapse
 
gramli profile image
Daniel Balcarek

Thanks, Utkarsh, I really appreciate it and Iโ€™m glad you like it!

Those are both great ideas. The game would definitely become much more interesting. My only concern is the AI side: the lightweight models already struggle with the simplest levels and adding optional objectives plus long-term resource decisions could make them lose track even more easily.

But for a human-focused mode, I think this would be a very fun direction. ๐Ÿ‘๏ธ

Collapse
 
neo_nietzsche profile image
Eryc Tri Juni S

the hard part wasn't WebMCP. it was the puzzle. ๐ŸŽฏ
one question though โ€” did the model actually count the moves, or just vibes-based sequence and pray?
because those are very different failure modes. ๐Ÿ‘€

Collapse
 
gramli profile image
Daniel Balcarek

I included remainingMoves in the game state, and Gemini 3.1 Flash-Lite especially often did not use the full budget. For example, it could have 28 moves available but submit only around 20 actions and stop before reaching the goal.

So it was not always a case of running out of moves, sometimes it simply produced an incomplete plan. The models I tested were lightweight, though, so stronger models would probably perform much better.

Collapse
 
neo_nietzsche profile image
Eryc Tri Juni S

so it had budget left and still stopped short โ€” that's not a counting problem, that's the model not knowing it failed until after it submitted.

it thought it was done. that's the scarier failure mode.

Collapse
 
harsh2644 profile image
Harsh

Cool๐Ÿ˜Ž Puzzle game for humans and AI such a unique angle Curious what's one puzzle type that AI solves faster than humans and one that humans consistently beat AI at? Would love to hear about the design process.

Thanks for sharing! ๐Ÿš€

Collapse
 
gramli profile image
Daniel Balcarek

Thanks, Harsh!

I developed and debugged it with Googleโ€™s WebMCP Model Context Tool Inspector. It currently offers only three model options: Gemini 3 Flash Preview, Gemini 3.1 Flash-Lite, and Gemini 3.5 Flash, so there was not much room for broader experimentation.

From what I tested, Flash-Lite often fails even on level 1. The other two can finish level 1, but they already struggle with level 2.

So, for now, humans win. ๐Ÿ˜„

I think stronger models would probably do better, but testing those would mean building my own agent outside the Inspector. That could be a fun next step. ๐Ÿค”

Collapse
 
webdeveloperhyper profile image
Web Developer Hyper

The idea of AI thinking for itself and playing the game is unique and fun. Good game! There might be many other great ways to use AI that we haven't discovered yet. ๐Ÿค”

Collapse
 
gramli profile image
Daniel Balcarek

Thanks! Glad you like it. โค๏ธ

Yeah, youโ€™re right. Discovering those new possibilities is one of the most fascinating and interesting things to do.

Collapse
 
webdeveloperhyper profile image
Web Developer Hyper

Looking forward to seeing what unique AI idea you come up with next! ๐Ÿ˜„

Thread Thread
 
gramli profile image
Daniel Balcarek

โค๏ธA lot of my ideas are inspired by articles from the DEV Community, so Iโ€™m always curious to see what interesting things people build and write about next! ๐Ÿ˜„๐Ÿ˜…

Thread Thread
 
webdeveloperhyper profile image
Web Developer Hyper

You are surely one of the most creative and highly skilled engineers in the DEV Community! ๐Ÿ‘

Thread Thread
 
gramli profile image
Daniel Balcarek

Oh, thank you! Thatโ€™s really encouraging and it warms my heart. ๐Ÿ˜Š

I wouldnโ€™t go that far, though, there are plenty of excellent engineers in the DEV Community, including you! ๐Ÿ™Œ

Collapse
 
itskondrat profile image
Mykola Kondratiuk

curious how the AI actually navigates the puzzle - does WebMCP give it a structured state dump, or does it read the DOM like a human would? the move order would look really different depending on that

Collapse
 
gramli profile image
Daniel Balcarek

WebMCP exposes a gameState tool, and that returns a structured state with the objective, rules, legend, remaining moves, current resources, and visibleMap:

visibleMap: [
  "P . W ~",
  ". R . G"
]
Enter fullscreen mode Exit fullscreen mode

So the AI sees the puzzle as structured data, not as DOM. I included the full state earlier in the article, probably the longest code example there. ๐Ÿ˜€

Collapse
 
itskondrat profile image
Mykola Kondratiuk

structured state makes the agent reasoning legible in a way raw DOM never could โ€” you can actually trace a bad move back to the input. does the visible map ever mislead it when fog is only partial?

Thread Thread
 
gramli profile image
Daniel Balcarek

Ah, sorry for the confusion, there is no fog. visibleMap exposes the full map, so the failures are more about planning/counting mistakes than partial visibility.

Yep, bad variable naming on my side. ๐Ÿ˜…

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

got it, that's actually cleaner to debug - if the map's fully visible and it still miscounts, the failure is clearly in the planning layer. easier to isolate

Collapse
 
marina_eremina profile image
Marina Eremina

Really cool game, I even reached level 5! The previous one was entertaining as well, great job! ๐ŸŽ‰

Collapse
 
gramli profile image
Daniel Balcarek

Thanks! That was fast, you must be a good player! ๐Ÿ˜„

I made the first three levels easier on purpose so the AI would have a chance, but levels 4 and 5 were meant to be more challenging. Maybe I should add a few tougher ones. ๐Ÿ˜…

Collapse
 
marina_eremina profile image
Marina Eremina

I just like this type of game. The only thing is they usually come packed with ads. What about yours? Should we expect ads to show up later? ๐Ÿ˜…

Thread Thread
 
gramli profile image
Daniel Balcarek

Never! ๐Ÿ˜„ Or at least until Cloudflareโ€™s free tier is no longer enough to host the game. ๐Ÿ˜…

Just joking, Iโ€™d rather find another solution before adding ads.

Collapse
 
technogamerz profile image
๐“ฃ๐“ฑ๐“ฎ๐“›๐“ช๐”ƒ๐”‚ ๐“ฐ๐“ฒ๐“ป๐“ต โ—•โ โ€ฟโ โ—•

Bro, you're making so many awesome games these days, I wouldn't be surprised if GTA 6 turns out to be your next project!๐Ÿ˜…

Collapse
 
gramli profile image
Daniel Balcarek

Thatโ€™s a great one! ๐Ÿ˜‚ Iโ€™d love to say โ€œChallenge accepted,โ€ but I think GTA 6 might be slightly out of scope for the next DEV challenge. ๐Ÿคฃ