# paulfioravanti's solution

## to Robot Simulator in the Elm Track

Published at Jul 29 2019 · 0 comments
Instructions
Test suite
Solution

#### Note:

This exercise has changed since this solution was written.

Write a robot simulator.

A robot factory's test facility needs a program to verify robot movements.

The robots have three possible movements:

• turn right
• turn left

Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, e.g., {3,8}, with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

• The letter-string "RAALAL" means:
• Turn right
• Turn left
• Turn left yet again
• Say a robot starts at {7, 3} facing north. Then running this stream of instructions should leave it at {9, 4} facing west.

## Elm Installation

Refer to the Installing Elm page for information about installing elm.

## Writing the Code

The first time you start an exercise, you'll need to ensure you have the appropriate dependencies installed. Thankfully, Elm makes that easy for you and will install dependencies when you try to run tests or build the code.

Execute the tests with:

``````\$ elm-test
``````

Automatically run tests again when you save changes:

``````\$ elm-test --watch
``````

As you work your way through the test suite, be sure to remove the `skip <|` calls from each test until you get them all passing!

## Source

Inspired by an interview question at a famous company.

## Submitting Incomplete Solutions

It is possible to submit an incomplete solution so you can see how others have completed the exercise.

### Tests.elm

``````module Tests exposing (assertionList, tests)

import Expect
import RobotSimulator exposing (Bearing(..), Robot, advance, defaultRobot, simulate, turnLeft, turnRight)
import Test exposing (..)

tests : Test
tests =
describe "RobotSimulator"
[ describe "init"
(let
robot =
defaultRobot
in
[ test "coordinates" <|
\() -> Expect.equal { x = 0, y = 0 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal North robot.bearing
]
)
, describe "setup"
(let
robot =
Robot South { x = -1, y = 1 }
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = -1, y = 1 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal South robot.bearing
]
)
, skip <|
describe "turn right"
(List.range 1 3
|> scanl (\_ r -> turnRight r) defaultRobot
|> List.map .bearing
|> assertionList [ North, East, South, West ]
|> List.indexedMap (\i e -> test ("step " ++ String.fromInt i) (\() -> e))
)
, skip <|
describe
"turn left"
(List.range 1 3
|> scanl (\_ r -> turnLeft r) defaultRobot
|> List.map .bearing
|> assertionList [ North, West, South, East ]
|> List.indexedMap (\i e -> test ("step " ++ String.fromInt i) (\() -> e))
)
(let
robot =
Robot North { x = 0, y = 0 }
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = 0, y = 1 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal North robot.bearing
]
)
(let
robot =
Robot East { x = 0, y = 0 }
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = 1, y = 0 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal East robot.bearing
]
)
(let
robot =
Robot South { x = 0, y = 0 }
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = 0, y = -1 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal South robot.bearing
]
)
(let
robot =
Robot West { x = 0, y = 0 }
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = -1, y = 0 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal West robot.bearing
]
)
, describe "simulate prog 1"
(let
robot =
Robot North { x = 0, y = 0 }
|> simulate "LAAARALA"
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = -4, y = 1 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal West robot.bearing
]
)
, describe "simulate prog 2"
(let
robot =
Robot East { x = 2, y = -7 }
|> simulate "RRAAAAALA"
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = -3, y = -8 } robot.coordinates
, test "bearing" <|
\() -> Expect.equal South robot.bearing
]
)
, describe "simulate prog 3"
(let
robot =
Robot South { x = 8, y = 4 }
|> simulate "LAAARRRALLLL"
in
[ skip <|
test "coordinates" <|
\() -> Expect.equal { x = 11, y = 5 } robot.coordinates
, skip <|
test "bearing" <|
\() -> Expect.equal North robot.bearing
]
)
]

{-| Given a list of values and another list of expected values,
generate a list of Assert Equal assertions.
-}
assertionList : List a -> List a -> List Expect.Expectation
assertionList xs ys =
List.map2 Expect.equal xs ys

{-| Taken from 0.18's List.scanl for easier upgrade to 0.19
<https://github.com/elm-lang/core/blob/5.1.1/src/List.elm#L171>
-}
scanl : (a -> b -> b) -> b -> List a -> List b
scanl f b xs =
let
scan1 x accAcc =
case accAcc of
acc :: _ ->
f x acc :: accAcc

[] ->
[]

-- impossible
in
List.reverse (List.foldl scan1 [ b ] xs)``````
``````module RobotSimulator exposing
( Bearing(..)
, Robot
, defaultRobot
, simulate
, turnLeft
, turnRight
)

type Bearing
= North
| East
| South
| West

type alias Robot =
{ bearing : Bearing
, coordinates :
{ x : Int
, y : Int
}
}

defaultRobot : Robot
defaultRobot =
{ bearing = North
, coordinates = { x = 0, y = 0 }
}

turnRight : Robot -> Robot
turnRight robot =
bearingsList
|> rotate 1
|> turn robot

turnLeft : Robot -> Robot
turnLeft robot =
bearingsList
|> rotate -1
|> turn robot

let
{ x, y } =
robot.coordinates

newCoordinates =
case robot.bearing of
North ->
{ x = x, y = y + 1 }

East ->
{ x = x + 1, y = y }

South ->
{ x = x, y = y - 1 }

West ->
{ x = x - 1, y = y }
in
{ robot | coordinates = newCoordinates }

simulate : String -> Robot -> Robot
simulate directions robot =
directions
|> String.toList
|> List.foldl performDirection robot

-- PRIVATE

bearingsList : List Bearing
bearingsList =
[ North, East, South, West ]

rotate : Int -> List Bearing -> List Bearing
rotate count bearings =
case ( compare count 0, bearings ) of
( GT, [] ) ->
[]

( GT, head :: tail ) ->
rotate (count - 1) (tail ++ [ head ])

( LT, _ ) ->
bearings
|> List.reverse
|> rotate (abs count)
|> List.reverse

( EQ, _ ) ->
bearings

turn : Robot -> List Bearing -> Robot
turn robot newBearings =
let
index =
bearingsList
|> List.indexedMap Tuple.pair
|> List.filter (\( _, bearing ) -> bearing == robot.bearing)
|> Maybe.withDefault ( 0, robot.bearing )
|> Tuple.first

newBearing =
newBearings
|> List.drop index
|> Maybe.withDefault robot.bearing
in
{ robot | bearing = newBearing }

performDirection : Char -> Robot -> Robot
performDirection direction robot =
case direction of
'L' ->
turnLeft robot

'R' ->
turnRight robot

'A' ->

_ ->
robot``````