Bingo

Level: Advanced 60–90 min

Concepts: Algorithms

Solutions: C# | TypeScript | Python


Bingo is a game of chance played using cards with numbers printed on them, which are marked off of the card as the caller reads out some manner of random numbers. Play usually ceases once a certain pattern is achieved on a card, where the winner will shout out “Bingo!” in order to let the caller and the room know that there may be a winner.

There are lots of different variations of Bingo, each with their own specific rules. Classic US Bingo is perhaps the most well known, where the game is played using a 5x5 grid of numbers between 1 and 75, with a FREE space in the middle. There is also a UK version of Bingo, which uses a 9x3 grid of spaces containing numbers between 1 and 90, of which five spaces on each row are filled in.

To get the best reach for our Bingo game, we are going to model it on the US version to begin with. To make this work, we are going to need to be able to call out numbers, generate Bingo cards for people to play with, and check their cards when someone calls Bingo. Once we have got these basics in place, we can then start to add new features or tweak the way it works.

Calling Bingo Numbers

  • When a number is called it is between 1 and 75 inclusive.
  • No number repeats in a game. That is if 75 numbers are called they all appear once.

Bingo Card Generation

  • When generating a bingo card it must contain 25 unique spaces. There shall be 5 columns (BINGO) and each column shall contain 5 unique numbers according to the following rules. The exception to this is the N column, it’s 3 space should be left blank as a free space.
Column Lower Bound Upper Bound
B 1 15
I 16 30
N 31 45
G 46 60
O 61 75

Example Card

Example Bingo Card

Checking Bingo Cards

  • A bingo may only be called when a player can from a vertical, horizontal or diagonal line from the called numbers.

Reference Walkthrough

Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/bingo. This is an F2 (light builder) kata: one primary entity (Card), a rich WinPattern return type that names which line completed (Row(i), Column(i), DiagonalMain, DiagonalAnti, or None), a single NumberOutOfRangeError for called numbers outside 1–75, and one small test-folder CardBuilder (15–30 lines) whose chained .withNumberAt(r, c, n) and .withMarkAt(r, c) calls read as a direct literal of the card state under test.

Scope note — pure domain only. The reference covers card state, the free space, marking called numbers, and detecting the first completed line. Random card generation (the “25 unique numbers within column ranges” rule from the spec), a caller/draw service that emits unique numbers 1–75 without repeats, a game loop, win-pattern variants (blackout, four-corners, postage-stamp), the UK 9x3 variant, and persistence/multiplayer are all out of scope and listed as stretch goals in the repo README. Those responsibilities introduce collaborators (IRandom, draw-state services, persistence adapters) — which is F3 territory, not F2.

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.


  • 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 Contract Test Is the Only Witness the Agent Cannot Author
    A test the agent wrote against code the same agent wrote shares the agent's blind spot: a misunderstanding in the implementation becomes a matching misunderstanding in the test, the bar goes green, and the bug ships certified. Consumer-driven contract tests are the only category where a second team publishes the assertion the first team has to satisfy. That separation of authorship is exactly what tamper-resistant test design demanded, applied at the integration seam.
  • Cohesion Is the Coverage of the Agent Era
    Agent-generated codebases pass every structural-modularity metric while fragmenting the domain concept the team was trying to encode. Coverage measured whether a line ran. Mutation score measured whether an assertion pinned behavior. Cohesion is the next axis, and it measures whether the codebase's concepts are traversable end-to-end.