mediumReact Native#137

Stroop Color Game (Juego de Colores)

Prompt

Implement the logic functions for a Stroop-effect color game.

Write two functions:

  1. getRound(round) — returns { word, displayColor }
    • word: the color NAME to display (changes each round)
    • displayColor: a different color to render it in (tricky!)
  2. handleChoice(chosen, state) — returns { score, round, gameOver }
    • chosen: the button the user tapped
    • state: { score, round } — current state
    • Increment score if chosen matches the word, advance round
    • End game after 20 rounds

Solution

const COLORS = ['RED', 'BLUE', 'GREEN', 'YELLOW']
const COLOR_MAP = { RED: '#ef4444', BLUE: '#3b82f6', GREEN: '#22c55e', YELLOW: '#eab308' }

function getRound(round) {
  return { word: COLORS[round % COLORS.length], displayColor: COLORS[(round + 1) % COLORS.length] }
}
function handleChoice(chosen, { score, round }) {
  const isCorrect = chosen === COLORS[round % COLORS.length]
  if (round >= 19) return { score: isCorrect ? score + 1 : score, round: round + 1, gameOver: true }
  return { score: isCorrect ? score + 1 : score, round: round + 1, gameOver: false }
}
Mentor's take

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."