Making Miaudoku
My wife wanted to play a puzzle game without being bothered by ads. I looked at the game and gave her the kind of answer you get when you're married to a software developer: "I can build this in an hour with an LLM."
To be fair, I wasn't that far off. Two hours later, she could play the first version in her browser. About half of that time went into setting up the server, DNS, and the other things needed to put it online.
That was at the end of May. It is now October. I've generated about 70,000 puzzles, built a separate tool for animating cats, and still regularly find things I want to change. Apparently, I had a few ideas for improvements.
The game is called Miaudoku. You place cats on a grid with colored regions. Each row, column, and region needs exactly one cat, and cats cannot touch, not even diagonally. Those are basically all the rules. No ads.
By now, I estimate it gets around 1,100 daily visitors, and that number is still rising. Some people play it every day and have been giving me feedback for months. That motivates me greatly. I also barely wrote any of the code by hand, which makes the development experience interesting to talk about. Because I certainly spent a lot of time making this thing. I wrote about my earlier experience with this in I Let AI Build My Website.
Getting Something Playable
I chose Flutter because I wanted a shared codebase for a web app and potentially a proper phone app. I haven't published native apps yet. The game is a progressive web app (PWA), so you can install it through supported browsers, and on most phones it largely behaves like an app. Largely. There are still differences, and I anticipate a few problems with the current architecture if I go ahead with native versions. I might do that if there is enough interest and I get around to it.
For now, you can just open it in your browser. It is hosted alongside this blog, and the installed version can be played offline. Your progress is stored locally. That also means it doesn't automatically follow you to another device, though I added export and import for that.
The first version already had the basic game, hints, scores, and settings. Over the following weeks I added achievements, unlockable cats, sound effects, daily challenges, and a lot of smaller things.
Looking at the changelog now, there are quite a few entries for things that sound minor. Better swipe controls. Resuming the exact board you left. Explaining a score. Making a button behave sensibly when you have no lives left. These things take time too, and there are a lot of them.
About the Cats
I love cats. We have cats, and many of the cats in Miaudoku are based on real ones I have or know. So making them part of the game was quite personal. These are little animated versions of animals that exist outside my computer, which makes creating and watching them a lot more enjoyable for me.
Eddy, with his stubby tail, is the cat behind "Purrfect." Pooki loves butt scratches and appears in "Flawless." Claudio was my first cat: very big, strong, and confident. He is "Big Cat Energy," which fits him quite well. And "Gone Feral" is Gimli, small and fluffy, but sometimes with a mean streak.
The cats became unlockable rewards and little companions on the board. Getting them to look and move the way I wanted turned out to be a whole topic of its own. Generating a nice cat picture is one thing. Making a small, transparent animation that loops reasonably well and still looks good on different background colors is another.
Some of the earlier artwork uses sprite sheets: a grid of pictures, each representing a frame of the animation. The game repository has a tool that cuts those into frames, removes the background, adjusts their placement and timing, and produces an animation. Even that involves quite a few details. The cat shouldn't jump around because it sits in a slightly different position in each frame. Background removal shouldn't eat its whiskers. And a background that looks transparent in a generated image may just be a picture of a checkerboard.
Eventually, I built a separate tool called Miauanim just to create and manage these animations. So the small game had acquired another software project.
Miauanim is a local workbench with a browser interface. It can start from a prompt and an appearance reference, manage generated motion, and take it through background removal, framing, looping, and export. It keeps previous attempts so I can compare them, and lets me reprocess existing material without generating everything again just because I want different padding or timing.
There is quite a lot hiding in those steps. Removing a background can also remove parts of the cat or leave a faint outline. Centering the subject can help with unwanted movement, but you don't want to cancel the movement of a cat that is deliberately walking somewhere. The last frame needs to transition back to the first without an obvious jump. Reversing the animation can work for some motions and look silly for others. Blending the ends can leave you with a briefly ghostly cat.
Then the result has to be small enough to ship. The game includes the cat artwork in its offline download, so every animation adds to the amount someone has to fetch. The current game assets use transparent WebP, at small resolutions. They are little game pieces, after all. They don't need to be cinema-quality videos, but they should still look like the cats they're meant to represent.
I could write a separate article about Miauanim. It became a fairly complex project in its own right, all because I wanted these particular cats moving around in my game. I don't mind. This was one of my favorite parts to work on.
But another big part of the project is something you barely see while playing: making the puzzles themselves.
What Makes a Puzzle Difficult?
The rules are simple enough. Generating a board that follows them is only the beginning. It should have exactly one solution, be solvable through reasoning, and have a difficulty rating that means something to the person playing it.
A larger board isn't necessarily a harder puzzle. It may just require more steps of the same relatively simple reasoning. A smaller board can require a deduction that is much harder to spot. So size and logical difficulty need to be separate things.
I spent a lot of time iterating on generation, grading, and classification. Early on, I moved to pregenerated puzzles so the game wouldn't have to do all that work while someone was waiting to play. Then came more work on the reasoning used to solve them, on recognizing patterns, and on deciding which puzzles belonged in which difficulty category.
Symmetry is an interesting example. A board can look difficult to a solver that doesn't recognize the implications of its symmetry, while a person who spots the pattern may find a much simpler way through it. If the solver misses that, its difficulty rating is misleading. Improving the solver can therefore change the grade of puzzles you already generated.
The grading eventually became based on the hardest kind of reasoning required to solve the puzzle, rather than just how many deductions it takes. Direct consequences, combinations of constraints, and reasoning through hypothetical placements don't all ask the same thing of a player. Even then, the grade describes what the implemented solver needs. It doesn't prove that a human couldn't find a simpler argument.
That distinction matters. A computer finding an answer isn't enough to tell me whether the puzzle is good.
By now, I've generated about 70,000 puzzles and selected roughly 6,400 of them to ship with the game. The selection covers different sizes, difficulties, and traits, including symmetric layouts and geometric shapes. I don't need every generated puzzle in the download, especially when I want the whole game to work offline.
Finding the larger and harder puzzles can take many hours. I used multiple threads to speed up generation, and my CPU got hot very quickly. There is something slightly amusing about spending that much computing time finding interesting places to put cartoon cats. But that is what it takes with the current generator and the requirements I've given it.
The LLMs helped write and revise the machinery for all of this. I still had to decide what I wanted that machinery to produce, look at the results, and keep refining it.
Explaining the Answer Is Another Problem
The puzzle logic also feeds the hint system. The same reasoning engine used to analyze a puzzle can look at the player's current board and find a useful next deduction.
That sounds straightforward until you have to explain the deduction on a phone screen.
A hint should help you understand why a cell can or cannot contain a cat. Just revealing a correct placement doesn't do much to teach you. And an explanation that is technically correct can still be almost useless if you can't follow what it refers to on the board.
There are repeated entries in the changelog for clearer hints, shorter hints, better highlighting, and more detailed explanations. That is a fairly accurate account of how it went. More detail wasn't automatically better. Neither was making everything shorter. The text and the board visualization had to work together.
Players pointed out explanations they couldn't follow, and sometimes they were simply right. I had more work to do. Most recently, a beginner asked for more hints, and I made unlimited hints the default in relaxed play. If someone wants help learning, that mode should let them get it.
Making It Look Decent
The visual design was another source of frustration.
I didn't like the standard design ideas the models came up with, and getting them to produce something good-looking wasn't easy. I spent a lot of time directing individual design elements, asking for changes, comparing results, and trying to make the whole thing consistent.
There were multiple iterations on the main screen, the game screen, the results, and the controls. A change that looks fine in one place still needs to fit with the rest. The models helped me iterate, but they didn't reliably come up with something I liked on their own.
Maybe ChatGPT wasn't the right tool for that part. Maybe I should have used a specialized service or a different model. I don't know. The design might still be crap, but it is certainly better and more consistent than what the GPT models initially came up with.
And even when I liked something, that didn't necessarily mean the players did. One person said the feedback when tapping cells looked laggy or choppy and wanted the mark to appear immediately. I added an option for that. I'd already added settings for whether the cats should keep animating, animate once, or stay still.
People have different preferences, and playing the same game regularly makes you notice things that might not bother someone trying it for five minutes.
People Actually Play This
I posted Miaudoku on Reddit in June. Since then, people have kept coming back with feedback, requests, and sometimes just a thank-you for making a game without ads.
That feels good. Knowing that people enjoy it makes spending my personal time on it much easier to justify. I'm learning a lot too, so I don't mind the time that much.
The visitor numbers come from the web server logs. I look at which files are downloaded and filter apparent bot traffic by access patterns. There is a lot of malicious traffic asking for things that have nothing to do with the game. The roughly 1,100 daily visitors are an estimate from that, not an exact count of people actively solving puzzles. Offline play also isn't something the server can observe.
Still, there are clearly quite a few people playing, and that changes how it feels to update the game. I'm afraid of breaking something for them. It is a little stressful. So far, it has mostly gone well, but the possibility is always there.
The game keeps growing too. The latest additions let people save individual puzzles and get a personalized mix based on what they enjoy. Those are still experimental. There are plenty of things left to improve in what already exists as well.
What Did I Actually Do?
When someone on Reddit asked whether I used AI, I said yes, for almost everything. Miaudoku has also been a project for trying different models, tools, and workflows. By September, I estimated that I'd spent over 200 hours on it. I've spent more since.
Very little of that was typing code. I made architecture and technology decisions, worked out gameplay rules, evaluated designs, tested the game, operated the deployment, collected feedback, and wrote down what I wanted the agents to build or change. I also tried things that didn't end up in the game.
That work is familiar to me from software development. What changed was how much of my time I spent on implementation myself. I could move between design, puzzle logic, infrastructure, and player feedback while having an agent do much of the coding. But I still needed to understand enough to judge what came back and decide what should happen next.
I go into the practical side of that in All Your Coworkers Are Probabilistic Too: making the intent clear, giving the agents useful context, reviewing their work, and keeping the feedback loop short. A lot of it is familiar software engineering work, even when someone or something else writes the code.
I don't think the first two hours and the following months contradict each other. Getting a playable version was quick. Figuring out what I wanted to improve, and then actually getting it to a state I was happy with, took much longer. Sometimes I'm still not happy with it.
But my wife got a game without the ads she complained about. Other people enjoy it too, and I get to keep learning while making it better. That is a pretty good reason to keep going.
If you have a feature request, a bug report, or other feedback about the game, leave it on the Miaudoku Feedback page.
Comments