Too hard, too easy, finally fun. A game in a shopping list app
· · Not by Prompt Alone · episode 6 · Czytaj po polsku
How do you get users to come back to Zakupomat between one shopping trip and the next?
Anyone building an app people use "now and then" knows this question. You open a shopping list when something runs out, and in the shop. Then for a few days - nothing. And it's not about making the app louder. One of our users praised Zakupomat precisely for having no unnecessary frills 🙂
My idea was this - a small game. Something for a minute in the checkout queue, or for the passenger on the way to the shop (the passenger, not the driver 😉). Optional, tucked away under "More", with the family competing against each other. And from day one with one condition, which I told the agent straight out:
"It has to be a game people actually want to play, not some artificial filler in a shopping app."
I opened the issue on Friday night. On Saturday three versions in a row went to TestFlight, and the family played in between. By Sunday evening the game was in both stores. A year ago there was no way something like this could be built over a weekend - one person and two agents. Sounds like "one prompt"? Let me tell you what happened in between 😉
A basket and four shelves
I took the idea straight from communist-era Poland - from "Nu, pogodi!", the Soviet Elektronika IM 02 handheld. If you had one, you remember it - a wolf with a basket and eggs rolling down from four hen roosts 🙂 That game was much simpler. In the shopping version, products fall from four shelves: you catch the fresh ones and let the rotten ones drop to the floor. And hearts bring a life back. Three hearts, points, a streak bonus, a pace that keeps speeding up. Plus your best score, the family best, and a note that someone in the family beat their score since your last visit. And for beating your own record - confetti, the same one you get after finishing your shopping.
The first version had four buttons at the bottom to move the basket. I asked: why not just tap the shelf where the basket should go? The buttons were gone and you steer by touching the board. A small thing, but that's when it started being fun.
Version 1: too hard
The family got the game on their phones and off we went. I played, Marta played, the kids played. After a few rounds I wrote to the agent:
"Up to 700 points it's pretty easy, and getting past 1090 is almost impossible."
The code was correct. The tests passed. Mathematically, two products never landed in the basket at the same moment - the minimum gap was 460 ms. Except the final drop into the basket took 700 ms! So there were two or three products flying on the screen at once, and your brain saw several tasks at the same time. The test checked the logic. It didn't check the human.
And the second remark was even more interesting:
"My wife didn't get at all that she's not supposed to catch the rotten products."
To me it was obvious - after all, I came up with it 😂 For someone who simply opens the game, not at all. So we changed more than the instructions. The start screen now has three labelled pictures - "CATCH", "AVOID" and "+1 HEART" - and rotten products are olive, spotted, with a red ×. Not colour alone, because colour isn't enough for everyone.
Version 2: too easy
Fixes: a shorter final drop, at least 900 ms between products, a pace rising in six steps up to a limit, hearts to win a life back, sounds. Another build to TestFlight.
And here's the twist. On her very first try Marta got to 15,000 points... and stopped on her own. Because she was bored!
On top of that I had a list of "little things that annoy me": at least 3× too many hearts, every product falling at the same speed, the pace stopping at 6, leaving the game killing the round, so-so sounds. And in Codex, which I used for the first versions (GPT-6-Astra did an awesome job!), I had just run out of tokens. I carried on with Claudius - my pet name for Claude.
Version 3: two family scores as data
This is where Claude started with something other than tweaking parameters. It built a simple player model: the less time between products, the bigger the chance of a mistake. And it fitted the model to the only data we had - two family scores: about 1,000 in the first version and 15,000 in the second.
The model showed something we felt but couldn't put into words. In version 2 the pace stopped growing, and a heart came along on average every 28 products, healing almost every mistake. Statistically, rounds could drag on for 20-30 minutes. It was a game you couldn't lose. You could only give up on it.
What changed:
- The pace rises for the whole round, but more and more slowly - there's no limit, there's a wall that grows slowly.
- Every product falls at a different speed, so you can't play "to the beat". A fast product never overtakes a slower one, because the order is set by the moment of the catch, not by when it leaves the shelf.
- Hearts are 3× rarer and always fall fast - a reward for paying attention, not a life jacket.
- A bonus of up to ×5 for a long streak, so every mistake really hurts.
- Leaving the game pauses it, and from the pause you can start over.
The model now gives a typical round of 4-8 minutes. To be honest - that's a hypothesis, not a measurement. The model doesn't know whether the game is fun. Only the people at the checkout know that 😉
The rattle of the counter
One of the things I'm happiest about is... a sound. I wrote that the score count-up at the end of a round should look like the one on Zakupomat's public stats page, and the sound should be "that kind of fast rattling - I hope you know what I mean".
It did! There isn't a single audio file in the game. All the sounds - the "plop" into the basket that rises with the bonus, the splat of a dropped fruit, the "yuck" of a caught rotten one - are generated by code. And the rattle of the counter is computed from the same curve as the digit animation - it races at first, then slows down and goes quiet exactly when the last digit stops. Like a wheel of fortune 🙂
Competition without family member profiles
The hardest decision wasn't about the game at all, but about privacy. Zakupomat recognises a family by one shared key and deliberately doesn't know which family member bought what. So how do you show "my best" and "family best"?
The agent suggested nicknames. I said no. With usually two players in a family, if the record isn't mine, I know whose it is anyway 😉 You don't need to build a profile system to give a family the fun of competing.
We ended up with a random identifier of the app installation - not the person, not the phone's hardware ID, not an advertising ID. It isn't visible in the app. The server keeps only the best scores, not a history of every round. The family best is just a number, with no author. Deleting the family deletes its scores. And to be honest - it isn't full anonymity towards the server, because that identifier is exactly how it knows which record is whose. But it's the minimum the feature needs, written down in a project decision together with what we don't do with that identifier.
What it cost
I built the game with agents from Anthropic and OpenAI. I ran out of token limits more than once, and I didn't feel like waiting five hours or a week for a new window - so I paid extra. Building it was huge fun, but "free and effortless" it was not.
It also cost my time, and in a place where I usually save it. This was one of the first times I reviewed the agents' code so thoroughly - mostly Codex's in the early stages. And not just whether the game works and respects privacy, but the code itself: how it's organised, what's unnecessary, what will bite back at the next change. There's a lot of code. I believe it's better precisely because of those reviews, and that it doesn't leave technical debt that someone (that is, me 😉) would be paying off for months.
The agents' part - code, tests, reviews, fixes and the whole release process for two stores. Mine - the idea, the boundaries (optional, no profiles, no filler), pushing back on the controls, code review, collecting feedback from the family and deciding when it's good enough. An agent will write the next version in an hour. But whether it's fun to play, only someone playing it can tell.
Does it work?
I don't know. And that's the most honest answer. I don't have data yet on whether the game makes families come back to the app more often. I have one family that plays like crazy, and a record of 50,970 points.
That's why I added measurement straight away - in the admin panel I can see how many families have played and how many play each week. I'll come back with numbers once there's something to count.
What this means for your team
- "It works and has tests" is not the same as "people want to play it". Test human reaction time and what people actually see on the screen, not just the logic.
- Two data points from real people are worth more than a hundred simulations. And from two points you can build a model that tells you where to look.
- What's obvious to the maker isn't obvious to the user. Give the first version to someone who doesn't know how it was supposed to work.
- Privacy is design too. Often you need less data than the first idea suggests.
At ANSLAN we help teams work with AI agents so that speed goes hand in hand with decisions made by people who know the users. An agent will deliver a version over a weekend. Whether it's a good version, someone at the checkout will tell you.
And the game is waiting in Zakupomat, under "More": zakupomat.app. Free forever, no ads, no selling data and no subscription. The best score of all families is on zakupomat.app/stats (the page refreshes once a day). For now it's mine - 50,970. Think you can beat it? 😉
See you next week 🙂