Skip to content

Instantly share code, notes, and snippets.

@xdaDaveShaw
Created October 30, 2019 23:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xdaDaveShaw/faad35ccd89e72a221e2a1d428e6b321 to your computer and use it in GitHub Desktop.
Save xdaDaveShaw/faad35ccd89e72a221e2a1d428e6b321 to your computer and use it in GitHub Desktop.
Solution to the Robot Journey coding challenge
//A solution to Mike Hadlow's Journey coding challenge in F#
//Details here: https://github.com/mikehadlow/Journeys
//Inspiration from Mark Seemann's Haskell solution:
//https://blog.ploeh.dk/2019/10/28/a-basic-haskell-solution-to-the-robot-journeys-coding-exercise/
[<Literal>]
let InputText = """1 1 E
RFRFRFRF
1 1 E
3 2 N
FRRFLLFFRRFLL
3 3 N
0 3 W
LLFFFLFLFL
2 4 S"""
//Types, based on the approach by Ploeh - before I stopped reading, and started solving :)
type Direction =
| North
| East
| South
| West
type Robot = {
Position: int * int
Facing: Direction }
type Command =
| TurnLeft
| TurnRight
| MoveForward
type Journey = {
Start: Robot
Commands: Command list
End: Robot }
module Parser =
open System
//Functional String.Split Helper
let split (by: string) (str: string) =
str.Split(by.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
let parseDirection = function
| "N" -> North
| "E" -> East
| "S" -> South
| "W" -> West
| _ -> failwith "Unexpected direction"
let parseRobot (robotPos: string) =
let parts = robotPos |> split " "
{ Position = int parts.[0], int parts.[1]
Facing = parseDirection parts.[2] }
let parseCommand = function
| 'L' -> TurnLeft
| 'R' -> TurnRight
| 'F' -> MoveForward
| _ -> failwith "Unexpected command"
let parseCommands (commands: string) =
commands
|> Seq.map parseCommand
|> Seq.toList
let parseJourney (parts: string seq) =
let parts = Seq.toList parts //Convert to list to allow indexing
{ Start = parseRobot parts.[0]
Commands = parseCommands parts.[1]
End = parseRobot parts.[2] }
let parseFile file =
file
|> split Environment.NewLine
|> Seq.toList
|> List.chunkBySize 3
|> List.map parseJourney
#r "FParsec\\FParsecCS.dll"
#r "FParsec\\FParsec.dll"
module CParser =
open FParsec
let pDirection : Parser<Direction, unit> =
(pchar 'N' >>% North)
<|>(pchar 'E' >>% East)
<|>(pchar 'S' >>% South)
<|>(pchar 'W' >>% West)
let pRobot =
pint32 .>> spaces >>= fun x ->
pint32 .>> spaces >>= fun y ->
pDirection >>= fun direcetion ->
preturn { Position = x, y;
Facing = direcetion; }
let pCommand : Parser<Command, unit> =
(pchar 'F' >>% MoveForward)
<|>(pchar 'L' >>% TurnLeft)
<|>(pchar 'R' >>% TurnRight)
let pCommands =
many pCommand
let pJourney =
pRobot .>> newline >>= fun start ->
pCommands .>> newline >>= fun commands ->
pRobot >>= fun ``end`` ->
preturn { Start = start;
Commands = commands;
End = ``end``; }
let pJournies =
sepBy pJourney (newline >>. newline)
let parseFile txt =
match run pJournies txt with
| Success(res, _, _) -> res
| Failure(f, _, _) -> failwith f
module Execution =
let turnRight = function
| North -> East
| East -> South
| South -> West
| West -> North
let turnLeft = function
| North -> West
| East -> North
| South -> East
| West -> South
let moveForward (x, y) direction =
match direction with
| North -> x, y+1 //So Facing North increments "y" - ¯\_(ツ)_/¯
| East -> x+1, y
| South -> x, y-1
| West -> x-1, y
let executeCommand robot command =
match command with
| TurnRight -> { robot with Facing = turnRight robot.Facing }
| TurnLeft -> { robot with Facing = turnLeft robot.Facing }
| MoveForward -> { robot with Position = moveForward robot.Position robot.Facing }
let debugCommand robot command =
printfn "Robot %A" robot
printfn "Command %A" command
executeCommand robot command
let validateJourney journey =
let actual =
(journey.Start, journey.Commands)
//||> List.fold debugCommand //Swap with below to see the state *before* each execution
||> List.fold executeCommand
if (journey.End = actual) then
"Success"
else
sprintf "Expected %A\r\nActual %A" journey.End actual
//Swap between manual and parser combinator.
//let parseFile = Parser.parseFile
let parseFile = CParser.parseFile
let output =
InputText //File.ReadAllText
|> parseFile
|> Seq.map Execution.validateJourney
printfn "%A" output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment