wanessalabs games production

Drop4

A 7x6 drop-disc game for two people on one device, or one against Evie. No timer, no failure state, no network calls.

ReactTypeScriptTailwindViteCloudflare Pages

I shipped a game I have never once seen move.

What It Is

Seven columns, six rows, forty-two cells. Drop a disc into any column that is not full and it falls to the bottom. Get four in a row in any direction and the game stops immediately. Fill the board with nobody getting four and it is a draw.

Two people play it on one phone held between them. One person plays it against Evie. A React module inside the Wanessa Labs Games Hub, with no accounts, no backend and no network calls.

The Repo Told Me No Before I Started

The famous version of this mechanic is somebody’s trademark, and the folder for this game already knew that. A README sitting there from months ago spelled out the rule: that name does not appear in the code, the interface, the metadata or the artwork, and the colours everyone associates with the box do not get reproduced either.

The instruction that kicked off this build used the trademarked name and asked for red and yellow discs. So the two things disagreed, and the older written rule won on the name. It was a real decision the project had already made, and the mechanic itself is generic anyway. Dropping discs into a grid is not what is protected. The name is.

The colour half of that rule I later relaxed on purpose, which is its own story further down.

The part worth keeping is not the trademark law. It is that a constraint already written down beats a fresh instruction that contradicts it, and the correct move is to say so out loud rather than quietly pick a side and hope nobody notices.

Why There Are Two Humans In This

The hub has twenty-six games and, until this one, exactly one that two people could play against each other on the same device.

Everything else is solitaire or you against bots. The hub’s own planning document names a second person who plays Dishwasher and Linked with me, and then almost nothing in the catalogue is built for her. That is the gap.

Evie exists because “two people in a room” and “nobody else is around” are different gaps, and a game you can only play half the time is half a game.

The Bug This Genre Always Has

The board fills up on the forty-second disc. What if that last disc is also the one that completes a line?

If you check “is the board full” before you check “did somebody just win”, you report a draw and you have stolen a win. It is an easy way to write it and it is wrong, and it only shows up in the rarest game there is.

So the win check runs first, always, and there is a comment on that line saying why, so nobody helpfully reorders it later.

Testing it needed a board that actually does that, and I was not going to invent one and squint at it. A small search program looked for a sequence of forty-two moves where nobody wins for the first forty-one and the final forced disc completes a line. It found one. That sequence is now a test.

Passing Tests Are Not Evidence

There is a lesson I collected the hard way on another project: a test that works out its expected answer by calling the very function it is testing is true by construction. It cannot fail. It will sit there green forever while the thing it is guarding quietly breaks.

So the win detector here is never compared against itself. The tests build a completely separate checker that walks all sixty-nine possible four-cell lines on the board and looks at every one of them, which is a different method from the shortcut the real code uses. When the slow honest one and the fast clever one agree across three hundred randomly played games, checked after every single move, that agreement is worth something.

Then I broke the code on purpose to see whether the tests would notice. Making a full board report a draw even when it was won: two failures. Making the win detector look in only one direction along each line instead of both: fourteen failures, including the three hundred game sweep.

Thirty-eight tests pass. The reason I trust them is not that they pass.

Changing My Mind About the Colours, In Public

I had specified coral and indigo discs, specifically to stay clear of the classic red-and-yellow look.

Then artwork arrived using a light blue grid with yellow and pink discs, which is close to the thing the rule was written to avoid. I flagged it rather than installing it quietly, and the call came back to ship it.

That is a fine call to make. It is somebody’s own rule about their own project, and the risk here is genuinely small. What was not optional was the consequence: if the card shows yellow and pink discs, the game has to show yellow and pink discs too, or every player gets a mismatch on their first tap.

So the discs changed to follow the art. The accessibility requirement did not move an inch, and it constrains the new palette harder than the old one did. Yellow and pink are both light colours, so the danger is not that they look similar in hue, it is that they sit at the same brightness. They are pulled far apart on brightness instead, and every disc also carries its own marking, so you can play the whole game without seeing colour at all.

One rule relaxed, one rule not. Written down in the project log either way.

A Small Thing About File Size

The artwork is a watercolour splash, and watercolour has soft transparent edges, which turn out to be where all the bytes live.

Compressing the picture harder barely helped: dropping quality from 70 to 50 saved four kilobytes out of eighty. Compressing the transparency instead took it from eighty-one kilobytes down to twenty-nine, with no visible edge halo when you put it on the card background where it actually lives.

Worth knowing if your images are heavier than they should be and turning the quality dial is doing nothing.

Evie

There is a bot, and she is named rather than being a difficulty setting, because “Evie” reads as somebody to play and “CPU (Medium)” reads as a settings row.

Three levels. The easy one is not random. It plays randomly except that it always takes a win it can see and always blocks a loss it can see, because an opponent that misses an obvious win is not an easy opponent, it is a broken one.

My first version of her thought for six hundred milliseconds a move. That is not thinking, that is the page freezing. The cause was that the search built a fresh copy of the board at every position it considered, which is the right way to hand a board to the rest of the program and the wrong way to explore ten thousand of them. She now scribbles on one scratch board and rubs it out again. Same strength, eighty-four milliseconds.

Then I picked how many moves ahead she looks by measuring rather than guessing. Seven moves ahead was ninety-three milliseconds on my desktop, which is closer to half a second on a cheap phone. Six moves is twenty. She looks six moves ahead.

What Is Not Done

I have never seen this game run in a browser.

The rules are proven, the bot is proven, a thousand tests pass, it builds clean, and it is now live. None of that is the same as watching a disc fall. Three different ways of driving a real browser failed from where I was working, so the checklist that would have covered a real win, a real draw, tapping a full column, playing it with only a keyboard, and the reduced-motion path is written and unrun.

It is live, which is not the same as verified, and those are two different claims.

Live at games.wanessalabs.com/games/drop4.

Development timeline

1 logged update on 28 Jul 2026.

  1. Drop four in a row against Evielaunch

    A seven by six board, played pass and play on one device or against a bot who thinks a few moves ahead.

Written from the release notes published with each update, not afterwards. See every project →