Tennis Score
Level: Intermediate 30–60 minConcepts: State
Solutions: C# | TypeScript | Python
Create a program to score a tennis match.
Requirements
- Implement tennis scoring rules:
- 0 points = “Love”
- 1 point = “15”
- 2 points = “30”
- 3 points = “40”
- 4+ points = “Game” (if leading by 2)
- Handle deuce scenarios:
- Both players at 40 = “Deuce”
- Player ahead by 1 = “Advantage”
- Player ahead by 2 = “Game”
- Support match scoring:
- Games needed to win a set (6, leading by 2)
- Sets needed to win the match (2)
Tiebreak rules at 6-6 are a bonus and are not part of the reference implementations linked below.
Test Cases
| Scenario | Score | Expected Output |
|---|---|---|
| Start of Game | 0-0 | ”Love-Love” |
| First Point | 1-0 | ”15-Love” |
| Two Points Each | 2-2 | ”30-30” |
| Deuce | 3-3 | ”Deuce” |
| Advantage | 4-3 | ”Advantage Player 1” |
| Game Win | 4-2 | ”Game Player 1” |
| Set Win | 6-4 | ”Set Player 1” |
| Match Win | 6-4, 6-3 | ”Match Player 1” |
Stretch Goals (Not in the Reference)
The reference implementations cover the eight scenarios above and stop. If you want to extend further:
- Tiebreak scenarios (at 6-6 in games)
- Match point detection
- Validation against impossible game states
- Negative-score / invalid-input rejection
Tips
- Start with basic point scoring
- Add deuce handling next
- Then implement game scoring
- Finally add set and match scoring
- Consider using enums for score states
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/tennis-score — the same eight scenarios across all three languages, walked commit-by-commit through the TDD cycle.
- C# (.NET 8, xUnit, FluentAssertions) — walkthrough
- TypeScript (Node 20, Vitest, strict types) — walkthrough
- Python (3.11, pytest) — walkthrough
This is a Pedagogy mode kata — the walkthroughs step through the TDD cycle commit-by-commit. Watch the gear shift from low (fake-it, per-player if ladders) to middle once the Advantage scenario forces the refactor. The teaching point is the state machine that extracts itself: through Love, 15, 30, 40, Deuce the if/else chain looks fine, but Advantage starts comparing counts against each other instead of against fixed values. That’s the cliff. Lift the count into a seven-state enum (Love | Fifteen | Thirty | Forty | Deuce | Advantage | Game) and the formatter becomes a dispatch — the Game scenario that follows passes on arrival. Sets and match are deliberately not a second state machine; they’re integer tallies with win conditions. See the repo’s Gears section for why middle gear is where the state machine lands.
Related reading
- 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. - Agents Amplify Whatever Vocabulary They Find
Agents do not bring vocabulary to your codebase. They mirror what's already there. Strong vocabulary gets agent contributions that read like the team. Weak vocabulary gets faster mediocrity. The work you did for craft reasons just became leverage. - The Hidden Output of TDD Was Never Code
The visible artifact of TDD is a passing test suite. The actual artifact, the one that compounded value over the life of the codebase, was the vocabulary the team built while writing it. The dictionary, not the regression net.