A second snake, same engine

Duel mode lands in one day: a turn-based match against an AI opponent that shares the daily engine and a first, cautious idea of what a safe move looks like.

A second game mode arrived in one day: Duel, a turn-based match against an AI opponent sharing the same board and the same fruit as the player. Every commit in the range, fourteen of them across three pull requests, came from GitHub’s coding agent again, this time with no hand-branched feature work alongside it at all.

One engine, two snakes#

The interesting decision was not the new UI, it was what did not need to change: buildAnimationSteps and rebuildGrid each grew one optional argument for the opponent’s body positions, treated as static walls a word can crash into, while the daily challenge’s own code paths stayed untouched. The engine that never met the framework took a second snake as an argument, not a rewrite. Five new database tables carried the rest of the mode: a session record, a per-move replay log, a public leaderboard, and two more for daily-mode analytics the game had never collected until an AI opponent’s move needed the same logging path the player’s already had.

A first, cautious opponent#

Each opponent turn samples a handful of random dictionary words, scores each one by its word value plus whatever fruit and combos it would collect along the way, and sorts the results into safe and unsafe piles depending on whether the move ends in a collision. A safe word is preferred over an unsafe one no matter how the scores compare, with a minor tiebreaker layered on for otherwise-close safe choices; only once repeated sampling turns up nothing safe at all does the opponent settle for its best unsafe option.

It would rather not lose than try to win.

The same day surfaced a scoring bug in the win condition itself: the game had been awarding the win to whichever snake did not crash, regardless of either score, which the fix replaced with a direct comparison:

if (newPlayerScore > newOpponentScore) newWinner = 'player';
else if (newOpponentScore > newPlayerScore) newWinner = 'opponent';
else newWinner = 'draw';

Teaching two modes to look like one#

The rest of the day made Duel feel like a sibling of Daily Challenge rather than a separate app bolted alongside it: the same on-screen keyboard component now drives both modes instead of a second, slightly different one built for Duel; the duel leaderboard gained the daily leaderboard’s card layout, medal icons, and colour-coded results; and the how-to-play modal grew a Game Modes section explaining both at once, now re-shown every seven days instead of once ever, so a returning player who missed a new mode has a chance to find it.

Where this leaves things#

The opponent that shipped today plays it safe by construction, never trading a guaranteed non-collision for a bigger word, and whether that makes for a match worth playing more than once is an open question rather than a settled one. What is settled is the shape underneath it: the same pure engine, the same board, the same fruit, extended by an argument rather than replaced by a second implementation, exactly the payoff the framework-free first day was betting on.