Code Breaker
Level: Intermediate 30–60 minConcepts: AlgorithmsStringsValidationEdge Cases
Solutions: C# | TypeScript | Python
Build the scoring engine for a Mastermind-style code-breaking game. Given a secret code and a guess, return feedback indicating exact matches and partial matches.
Requirements
The secret code and guess are both 4-digit strings using digits 1–6.
Feedback Rules
- Exact match (
+) — correct digit in the correct position - Partial match (
-) — correct digit but in the wrong position - No match — digit not in the secret code
Priority
- Exact matches are evaluated first
- A digit consumed by an exact match cannot also count as a partial match
- Feedback string lists all
+symbols before-symbols
Output
Return a feedback string:
"++"means 2 exact matches"+-"means 1 exact match and 1 partial match""means no matches at all
Test Cases
| Secret | Guess | Feedback | Explanation |
|---|---|---|---|
"1234" | "5678" | "" | No matches |
"1234" | "1578" | "+" | 1 exact (position 1) |
"1234" | "1234" | "++++" | All exact |
"1234" | "4321" | "----" | All partial, none exact |
"1234" | "1243" | "++-" | Wait — see below |
"1234" | "1243" | "++--" | Positions 1,2 exact; 3,4 partial |
"1234" | "2135" | "+--" | Position 2 exact; 1,3 partial |
"1124" | "5167" | "+" | Position 2 exact; only one 1 available |
"1122" | "2211" | "----" | All partial, none in correct position |
"1111" | "1112" | "+++" | 3 exact; 2 is not a 1 |
"1111" | "2111" | "+++" | Positions 2,3,4 exact; 2 not in code |
The Tricky Case
Secret "1124", Guess "5167":
- Position 1: 5 vs 1 → no match
- Position 2: 1 vs 1 → exact match (
+) - Position 3: 6 vs 2 → no match
- Position 4: 7 vs 4 → no match
The 1 in position 1 of the guess might seem like a partial match, but the secret’s 1 at position 2 is already consumed by the exact match. Result: "+"
Bonus
- Support configurable code length (4, 5, or 6 digits)
- Support configurable digit range (1–6, 1–8, etc.)
- Build a full game loop: generate random secret, accept guesses, track attempt count
- Add a
isWon()check when feedback is all+
Hint
Process exact matches first in a separate pass. Mark matched positions as “consumed” in both the secret and guess. Then do a second pass for partial matches, only considering unconsumed positions. This two-pass approach handles all the tricky duplicate-digit cases cleanly.
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/code-breaker. This is an F2 (light builder) kata: two primary entities (Secret, Guess) each get a small test-folder builder so four-peg codes read as a single fluent line, a typed Peg enum closes the 1–6 domain at compile time, and Feedback carries both the structured counts (exactMatches, colorMatches) and the canonical "+"/"-" string.
Scope note — feedback engine only. The reference is scoped to scoring a guess against a secret. Random secret generation, the full game loop with attempt tracking, configurable code length, and a configurable peg range are all out of scope for this reference and listed as stretch goals in the repo README. Those responsibilities introduce a SecretGenerator collaborator and a Game aggregate — F3 territory, not F2.
- C# (.NET 8, xUnit, FluentAssertions 6.12.0) — walkthrough
- TypeScript (Node 20, Vitest 1.6, TS 5 strict) — walkthrough
- Python (3.11, pytest) — walkthrough
This kata ships in Agent Full-Bake mode at middle gear, the F2 (light builder) tier. See the repo’s Gears section for why middle gear is the deliberate choice.
Related reading
- Examples Pin Intent. Properties Pin the Invariants.
Example-based tests anchor scenarios in the team's vocabulary. Property-based tests anchor invariants across the input space. The agent needs both axes, and most suites only carry one. - Katas Are Rehearsal, Not Performance
You don't practice TDD on production code. You practice on katas and bring the muscle memory to production. The gap between knowing TDD and doing TDD is reps. - The Test Pyramid Was an Economic Argument
The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake.