The situation
The survey rover is parked at the lip of Kestrel Ravine, and the drive program came up the uplink with a hole in it. Somewhere in that string of commands is a token the transmitter mangled — maybe a letter that means nothing, maybe a distance that isn't a number, maybe a perfectly valid instruction that would drive an expensive robot off a cliff.
The rover does not get to find that out the hard way. Your job is the layer between the uplink and the motors: parse each command, refuse the broken ones by name, and drive the rest.
The world
An 8 × 8 grid. x runs right, y runs down (like screen coordinates, not like maths class). Legal cells are 0..=7 on both axes. The rover starts parked at 0,0 facing East.
x →
0 1 2 3 4 5 6 7
0 ▶ . . . . . . . ▶ = start, facing East
1 . . . . . . . .
2 . . . . . . . . North = y − 1
⋮ South = y + 1
7 . . . . . . . .
↓ yBecause y points down, a right turn from East faces South. Left turns go the other way. Four turns in the same direction bring you back where you started.
The given types
Facing, Pose, Step, RoverError and START are already in the editor. Don't change them — the hidden tests build and compare those exact shapes.
Your mission
1. parse_step(token: &str) -> Result<Step, RoverError>
| Token | Result |
|---|---|
"L" | Ok(Step::Left) |
"R" | Ok(Step::Right) |
"F3", "F12", "F0" | Ok(Step::Forward(n)) |
"Z", "left", "" | Err(UnknownCommand(token)) |
"F", "Fx", "F-2", "F1.5" | Err(BadDistance(token)) |
The rule for telling the two errors apart: if it starts with F it was meant to be a move, so a broken distance is a BadDistance. Anything that isn't L, R, or F… is an UnknownCommand. Both errors carry the offending token so the operator can see what came down the wire.
2. step_once(pose: Pose, step: Step) -> Result<Pose, RoverError>
Left/Rightturn in place — position unchanged.Forward(n)movesncells along the current facing.- If the move would end outside the grid, return
Err(OutOfBounds { x, y })wherex/yare where it would have landed — off-grid values and all.step_once(at 6,0 facing East, Forward(2))reportsOutOfBounds { x: 8, y: 0 }, not a clamped7.
3. run(program: &str) -> Result<Pose, RoverError>
Whitespace-separated tokens, executed from START, returning the final pose. The first error stops everything and comes straight back to the caller — this is what ? is for.
run("F3 R F2 L F1") -> Ok(Pose { x: 4, y: 2, facing: East })
run("") -> Ok(START)
run("F1 Z F1") -> Err(UnknownCommand("Z"))
run("F1 F9 L") -> Err(OutOfBounds { x: 10, y: 0 })How you're graded
parse_step_reads_turns_and_movesparse_step_rejects_unknown_tokensparse_step_rejects_bad_distancesturning_walks_the_compass← turns must not move the roverforward_moves_along_the_facingdriving_off_the_grid_is_an_error← including7,7being legal and8,0notrun_executes_a_whole_programrun_stops_at_the_first_error
Run drives the sample program and prints the trace, which the viewer replays on the grid. Change the program string in main and watch a different route.
Stretch goals
- Make
RoverErrorimplementDisplaysoHALTprints a sentence instead of aDebugdump. - Add
Step::Back(n)without duplicating the direction table. - Let the rover wrap around the grid instead of erroring — one line, if your direction handling is where it should be.
- Return every pose the rover visits (
Vec<Pose>) instead of just the last one.