import InfoBox from '../components/InfoBox.astro';

Two days went into naming things that used to just live inside a bigger file: an animation pipeline, an API client, a service, a scoring formula, a grid query, a counter. None of it changed what a player experiences. All of it changed how much of the codebase has to be understood, or trusted, before touching any one part of it.

## The one store that missed the first fix

[The animation queue stopped mutating game state per frame](/posts/post-launch-week#three-refactors-each-blocked-on-the-last) for the daily and duel stores a week ago, but the demo overlay's own copy of the same step-and-complete logic was never touched, and it still updated game state on every animation frame instead of once the queue finished. A single `AnimationMachine` module now owns that pipeline for all three stores, daily, demo, and duel alike, so the fix that already existed for two of them finally reaches the third instead of living as three separate copies of the same idea.

## Naming the seams

A raw `fetch()` scattered across the daily store's calls to its own analytics endpoints became a small typed client the store calls without knowing a URL exists. A 362-line route handling the duel opponent's move shrank to about a third that size once its word-candidate fetching, batching, and caching moved into their own service, tested without a live database. And the rule that a collision voids a word's own score while [keeping whatever fruit it already ate](/posts/tuning-the-daily#crashing-does-not-erase-what-you-already-ate) stopped living as two separate copies in the daily and duel submission paths, consolidated into one scoring function both call.

## A shared counter, and the bug it had been hiding

Every new apple and combo fruit got its id from one number, incremented globally, shared across every game running in the process, including tests. Moving that counter onto the game state itself, so each game keeps its own, immediately exposed what the shared version had been hiding: two independent games created back to back produced an apple with the identical id, since the second game's own counter had never actually reset. The duel mode's own version of the same problem needed one more pass, the same day: the server had been guessing the counter's value from whatever edibles happened to still be on the board, a heuristic true only because a board always has at least one apple on it, and now the client sends the exact number it already holds, with the guess kept only as a fallback for anything that predates the change.

<InfoBox title="Everything else these two days touched">
  Several separate low-level grid functions collapsed into a single query object answering direct
  questions, can a word land here, pick an empty cell, what does the board look like, and a
  duplicated empty-cell-picking algorithm that had existed in two places came down to one. Preview
  deployments also stopped starting from an empty dictionary: a small curated word list, covering
  every direction letter and a handful of antonym pairs and palindromes, now seeds itself into a
  freshly created preview database on first startup, skipped entirely once real words are already
  there.
</InfoBox>

## Where this leaves things

Six named, independently tested modules now stand where six pieces of shared logic used to sit inside stores and routes, and the one bug the naming itself turned up, a counter that had never really been isolated, was found and closed within the same two days rather than waiting for a player to hit it first.