Skip to content

Instantly share code, notes, and snippets.

@isaacabraham
Created May 17, 2016 20:21
Show Gist options
  • Save isaacabraham/984d1769d80812247c11a5389c07b47b to your computer and use it in GitHub Desktop.
Save isaacabraham/984d1769d80812247c11a5389c07b47b to your computer and use it in GitHub Desktop.
Sample solution to Thoughtworks Rover challenge
// A few patterns to make it a bit easier to reason about the raw text. A more typesafe implementation might use full discriminated unions.
let (|North|South|East|West|) = function | "N" -> North | "S" -> South | "E" -> East | "W" -> West | _ -> failwith "Unknown direction"
let (|Rotate|MoveForward|) = function | 'L' | 'R' -> Rotate | 'M' -> MoveForward | _ -> failwith "Unknown command"
let (|Left|Right|) = function | 'L' -> Left | 'R' -> Right | _ -> failwith "Unknown command"
/// A DTO to represent the Rover
type Rover = { X : int; Y : int; Direction : string }
/// Moves the rover forward one unit.
let driveForward rover =
match rover.Direction with
| North -> { rover with Y = rover.Y + 1 }
| South -> { rover with Y = rover.Y - 1 }
| East -> { rover with X = rover.X + 1 }
| West -> { rover with X = rover.X - 1 }
/// Rotates the rover left or right by 90 degrees.
let rotate rotation rover =
{ rover with
Direction =
let directions = [ "N"; "E"; "S"; "W" ]
let index =
let currentPositionIndex = directions |> List.findIndex ((=) rover.Direction)
match rotation with
| Left -> currentPositionIndex - 1
| Right -> currentPositionIndex + 1
match index with
| -1 -> "W"
| 4 -> "N"
| index -> directions.[index] }
/// Carries out a single command on a the rover
let act rover command =
match command with
| MoveForward -> rover |> driveForward
| Rotate -> rover |> rotate command
/// Parses a single raw input command into a Rover and associated set of commands.
let parse [ rover:string; commands ] =
let [| x; y; direction |] = rover.Split ' '
{ X = int x; Y = int y; Direction = direction },
commands |> Seq.toArray
/// Everything above is the behaviour; below we feed the data into it.
/// Parse our sample dataset
let instructions =
[ "5 5"
"1 2 N"
"LMLMLMLMM"
"3 3 E"
"MMRMMRMRRM" ]
|> List.skip 1
|> List.chunkBySize 2
|> List.map parse
/// Carry out the commands
instructions
|> List.map(fun (rover, commands) ->(rover, commands) ||> Array.fold act)
|> List.iter(fun rover -> printfn "%d %d %s" rover.X rover.Y rover.Direction)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment