The bot that played only sunflowers
When Same Seed was still in balance, I was nervous about one specific thing: a game that rewards variety should be beatable without any variety at all. If one flower could fill all nine plots and still take first place, then the variety ladder we designed was decoration. So I wrote a bot that plays badly, on purpose, in exactly one way. It plants only sunflowers. Lazy. Unfair. A good way to find out whether the thing we built was real.
The bot we built to lose
Same Seed is a twelve day garden. Nine plots, three of each soil, six flower species, three actions a day for thirty six total. Every flower prefers one soil, and on that soil it grows at 125 per mille instead of 70, so each species has exactly three good plots. The variety bonus pays as you harvest more distinct species. That is the design, on paper.
The first thing to doubt in a game like this is that mixing actually pays. Coins drown bonuses. The sunflower is our most expensive flower at 96 per harvest and the daisy is the cheapest at 20, so a rational player can always argue that three sunflowers beat one of each of six kinds on raw coin, and that my ladder is a suggestion. I did not want to find that out from a player.
So there is a monoculture bot, one per species. It is the laziest possible strategy. Plant the one thing, again and again, ignore the rest of the garden. In tools/balance-probe.js it runs alongside a greedy bot, a variety seeking bot, and a fully random one, over 400 different daily seeds, and the answer is a table. The bot is deliberately stupid. It never looks at the variety bonus, which is the whole rule we are trying to test, which is exactly why it is useful.
What the first numbers did to us
The first table made me sit down. Watering was 40% of all actions. A cozy garden game that spends its entire afternoon doing a chore. I will admit that number embarrassed me a little, because the whole premise of a calm game is that the player wants to be there, and 40% of the time we were asking them to pour water on dirt that was already wet, which is not calm, it is a chore, and I had built it that way without noticing.
The fix was small, which is the part I wish I could plan for in advance. Watering now pours six units per action, and the well watered bonus dropped from 115 per mille to 104, so topping up every plot stopped being the correct move every day and became a reaction to the forecast. The variety ladder starts paying at two species and climbs steeply from there: 10, 35, 75, 130, 200, 290. The sunflower also lost a day of growing, which lets a full bouquet fit inside the season. We bumped RULESET to 2, because old replays have to keep verifying under the rules they were played.
The table we wanted
Same 400 seeds, ruleset 2. Watering fell from 40% of all actions to 28% for the same bot. The monoculture medians were 230 for daisy, 310 for tulip, 376 for lily, and 298 for sunflower. The designed line, variety greedy, landed at a median of 818 with a best of 1002. A fully random player came in at a median of 31.
The row that made me grin was the sunflower one. Its median of 298 is exactly three sunflowers plus the one species bonus, 3 × 96 + 10. On the best seed in the sample it reaches 586, which is six sunflowers and the same bonus. The bot can explain its own score to the coin, and that is what a balance report should look like. Numbers that decompose.
And the degeneracy we were afraid of is not there. The best monoculture, lily at 376 median, sits below the mixed greedy bot's tenth percentile at 633. If that line had crossed, the game would have a wrong answer that plays like the right one, and we would have shipped it, and a player who found it would have spent the whole season doing something we did not intend, which is the exact failure mode the variety ladder exists to prevent, and it is worth saying out loud because that is the whole reason the bot exists in the first place.
A trap in our own test suite
One small detail, because it is exactly the kind of thing that bites. In tools/bots.js, calling the bot runner with no third argument hands you the greedy bot, not the variety seeking line we designed. The fingerprint section of tests/test.js pins nine fixed scores to ruleset 2, and it carries a comment that reads TRAP, warning that quoting the greedy score as the designed line understates it by 80 points. We wrote the warning into the code, because we are clearly not to be trusted.
The suite has 245 assertions and zero failures. Nine of them are those fingerprints, fixed logs that must produce fixed scores. The comment around them says the right thing: everything else in the file compares two values computed the same way, so a silent drift in the growth math could agree with itself and pass. The fingerprints cannot.
Why the bad bot stays
I keep coming back to one thing: the monoculture bot is our most honest reviewer. It has no taste, no affection for the theme, no hope. It plays the one lazy strategy 400 times, and the table tells us whether the design is doing the work we think it is doing. The 40% watering and the 3.27 species were not design notes. They were measurements, and they changed two constants.
To be fair to myself, I do not fully trust the table either. Four hundred seeds and a handful of bots is a sample, not a proof, and a real player can still find an eighth strategy none of them thought of, which is the honest way to say that this is a review, not a verdict, and I am not pretending otherwise. But I trust the direction, and that is a different claim and I want to be careful about the difference. Any time we retune a number, the probe is the first thing that runs, and the monoculture row is the first thing we look at, because it is the row that would tell us the design stopped doing its job.