Prompt
Implement the logic functions for a Stroop-effect color game.
Write two functions:
getRound(round)— returns { word, displayColor }word: the color NAME to display (changes each round)displayColor: a different color to render it in (tricky!)
handleChoice(chosen, state)— returns { score, round, gameOver }chosen: the button the user tappedstate: { score, round } — current state- Increment score if chosen matches the word, advance round
- End game after 20 rounds
Solution
The Stroop game's one rule — score when the choice matches the word, never the ink color — lives in a single comparison, and both functions deriving the round's word from the same expression (COLORS[round % COLORS.length]) is what keeps them consistent. getRound and handleChoice never exchange hidden state; the round index is the sole input, so the question shown and the answer scored can't drift apart.
Making rounds deterministic functions of the index (word cycles through COLORS, ink offset by one) is a deliberate testability choice: every round is reproducible, and the offset-by-one guarantees word ≠ displayColor by construction — the incongruence that makes Stroop hard. Production would randomize, and then you need the guard determinism gives you for free: re-roll while displayColor === word, or the round becomes a freebie. Say that trade-off out loud.
The boundary is the classic fencepost: rounds are 0-indexed, so index 19 is the 20th and final round — round >= 19 flags gameOver on that choice while still scoring it. Off-by-one here either gives players 21 rounds or eats their last answer.
Red flag: scoring chosen === displayColor. The game still "works" — buttons respond, score moves — but you've built the opposite test (naming the ink), and no type checker will catch a rules bug. Encode rules in one pure function and unit-test the rule itself.
Say it: "Both functions derive the round's word from the same index expression, so question and scoring can't desync — and round 19 is the twentieth round, scored then ended."