Mars Rover
Level: Intermediate 30–60 minConcepts: Design
Solutions: C# | TypeScript | Python
Develop an API that moves a rover around a grid on Mars.
- You are given the initial starting point(x,y) of a rover and the direction (N,S,E,W) it is facing.
- The rover receives a string of commands. Implement commands that:
- Move the rover forward(f)
- Move the rover backward(b)
- Turn the rover left(l)
- Turn the rover right(r)
- Implement wrapping from one edge or the grid to another - Planets are spheres after all.
Hint
Your constructor should look like: MarsRover(location, direction, gridSize). E.g var rover = new MarsRover([0,0],‘e’,[50,50]);
Example
The rover is on a 100x100 grid at location (0, 0) and facing SOUTH. The rover is given the commands “fflff” and should end up at (2,2).
Bonus
Implement obstacle detection before each move to a new square. If a given sequence of commands encounters an obstacle, move the rover up to the last possible point and reports the obstacle. You will need to amend your code to take in an array of obstacles.
Reference Walkthrough
Reference implementations in C#, TypeScript, and Python live at tddbuddy-reference-katas/mars-rover. This is an F2 (light builder) kata: one primary entity (Rover), three value enums (Direction, Command, MovementStatus), one domain exception (UnknownCommandException), and two small test-folder builders — RoverBuilder (.at().facing().onGrid().withObstacleAt()) and CommandBuilder (.forward().left().build() → "FL"). Obstacle blocking is modelled as a result state on the rover (status, lastObstacle), not an exception — the kata brief asks the rover to report the obstacle, which it does as a first-class state rather than a thrown error.
Scope note — pure domain only. The reference covers position, heading, forward/backward movement, rotation, grid wrapping, and obstacle detection. A renderer (ASCII grid print), a CLI loop, alternative command formats (whitespace, lowercase, numeric repeats like 3F), and multi-rover worlds are all out of scope and listed as stretch goals in the repo README. Those responsibilities introduce collaborators (renderers, input parsers, shared world state) — 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. - The Bounded Context Is the Scope the Agent Actually Needs
The industry gave the coding agent the whole repo as its working scope and is discovering that agents do the most damage where they are most helpful: making things consistent across boundaries that were not supposed to be consistent. Bounded contexts are not a design pattern for humans anymore. They are the scoping primitive that keeps the vocabulary the team owns from being consolidated by the tool the team hired. - 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.