Tic Tac Toe
Level: Beginner 15–30 minConcepts: StateDesign
Solutions: C# | TypeScript | Python
Create a program to implement the classic game of Tic Tac Toe.
Requirements
- Implement game rules:
- 3x3 grid game board
- Two players (X and O)
- Players take turns placing their mark
- First player to get 3 in a row (horizontally, vertically, or diagonally) wins
- Game ends in a draw if all spaces are filled with no winner
- Handle game state:
- Track current player’s turn
- Validate moves (prevent playing in occupied spaces)
- Detect win conditions
- Detect draw conditions
- Return appropriate results:
- Current game state
- Winner (if any)
- Error messages for invalid moves
Test Cases
| Scenario | Moves | Expected Result | Notes |
|---|---|---|---|
| Empty Board | None | Game in progress | Initial state |
| Horizontal Win | X(0,0), O(1,0), X(0,1), O(1,1), X(0,2) | X wins | Top row win |
| Vertical Win | X(0,0), O(0,1), X(1,0), O(1,1), X(2,0) | X wins | Left column win |
| Diagonal Win | X(0,0), O(0,1), X(1,1), O(0,2), X(2,2) | X wins | Top-left to bottom-right |
| Draw | X(0,0), O(0,1), X(0,2), O(1,0), X(1,1), O(2,0), X(1,2), O(2,2), X(2,1) | Draw | No winner |
| Invalid Move | X(0,0), X(0,1) | Error | Same player twice |
Edge Cases to Consider
- Playing in an occupied space
- Playing out of turn
- Playing outside the board boundaries
- Continuing play after game end
- Multiple win conditions
Tips
- Start with an empty board representation
- Add move validation next
- Then implement win detection
- Finally add game state management
- Consider using enums for player marks and game states
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/tic-tac-toe. This is an F2 (light builder) kata: one primary entity (Board), a pair of value enums (Cell, Outcome), three domain exceptions for invalid moves, and one small test-folder BoardBuilder (10–30 lines) whose chained .withXAt(r,c) and .withOAt(r,c) calls read as a direct literal of the board state under test.
Scope note — pure domain only. The reference covers placing marks, turn tracking, win/draw detection, and invalid-move rejection. Rendering (ASCII board, HTML view), a CLI loop, a computer opponent, move history/undo, and n-in-a-row generalisation are all out of scope and listed as stretch goals in the repo README. Those responsibilities introduce collaborators (renderers, input parsers, strategy interfaces) — which is 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
- 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.