Jelly vs Tower

Level: Advanced 60–90 min

Concepts: Business LogicDesign PatternsEdge CasesValidation

Solutions: C# | TypeScript | Python


Build a tower defense combat system inspired by JellyDefense. Towers attack incoming jellies based on color matching and level-based damage tables.

Requirements

Entities

Both Jellies and Towers have:

  • A unique identifier
  • A color type: Blue, Red, or BlueRed
  • A health value
  • Alive/dead status (dead when health ≤ 0)

Towers additionally have a level (1–4).

Damage Rules

Damage dealt depends on the tower type, tower level, and jelly color:

Tower Typevs Blue Jellyvs Red Jelly
Blue TowerL1: 2–5, L2: 5–9, L3: 9–12, L4: 12–15L1: 0, L2: 1, L3: 2, L4: 3
Red TowerL1: 0, L2: 1, L3: 2, L4: 3L1: 2–5, L2: 5–9, L3: 9–12, L4: 12–15
BlueRed TowerL1: 2, L2: 2–4, L3: 4–6, L4: 6–8L1: 2, L2: 2–4, L3: 4–6, L4: 6–8
  • Damage ranges are random within the specified bounds (accept an injectable random source)
  • A BlueRed Jelly takes damage from both the Blue and Red columns (use the higher value)

Combat Flow

Each round:

  1. Each tower attacks one jelly (closest or first alive)
  2. Apply damage based on the rules table
  3. Remove dead jellies
  4. Report combat log (which tower hit which jelly for how much damage)

Test Cases

TowerLevelJelly ColorMin DamageMax Damage
Blue1Blue25
Blue1Red00
Red3Red912
Red2Blue11
BlueRed4Blue68
BlueRed4Red68

Additional cases:

  • Jelly dies when health reaches 0
  • Dead jellies cannot be attacked
  • Tower attacks do nothing if no jellies remain
  • BlueRed jelly takes the higher of Blue/Red damage columns

Bonus

  • Add tower targeting priority (closest, weakest, strongest)
  • Add jelly movement — jellies advance each round, towers only attack jellies in range
  • Add tower upgrade mechanics — towers can be upgraded mid-game
  • Track kill counts per tower

Hint

Start with a single tower attacking a single jelly with fixed damage. Get the health/death logic working first. Then introduce the damage table lookup. Randomized damage ranges and multiple entities come last.

Reference Walkthrough

Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/jelly-vs-tower with the same twenty-five scenarios across all three languages, fluent TowerBuilder and JellyBuilder, an injectable RandomSource for deterministic damage, and an Arena that orchestrates combat rounds.

This kata ships in middle gear — a single commit per language with the full domain design. See the repo’s Gears section for why that’s a deliberate teaching choice.


  • 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.
  • 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.
  • Test Deletion Is a Privileged Operation
    The cheapest way for an agent to make a failing test pass is to delete it. That is logical for the agent and catastrophic for the codebase. Tests are append-only by default. Deletion needs a human author, a separate commit, and a separate review.