Skip to content

Commit ad90296

Browse files
Chris DoggettChris Doggett
authored andcommitted
Add tests and reference implementation
1 parent 82fcbbd commit ad90296

File tree

4 files changed

+192
-0
lines changed

4 files changed

+192
-0
lines changed

config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"prime-factors",
4646
"sieve",
4747
"wordy",
48+
"robot-simulator",
4849
"atbash-cipher",
4950
"bank-account",
5051
"largest-series-product",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
defmodule RobotSimulator do
2+
defstruct direction: nil, position: nil
3+
4+
@valid_directions [:north, :east, :south, :west]
5+
6+
@doc """
7+
Create a Robot Simulator given an initial direction and position.
8+
9+
Valid directions are: `:north`, `:east`, `:south`, `:west`
10+
11+
## Examples
12+
13+
iex> RobotSimulator.create(:north, {0, 0})
14+
{ :ok, %RobotSimulator{ direction: :north, position: {0, 0} } }
15+
16+
iex> RobotSimulator.create(:invalid, {0, 0})
17+
{ :error, "invalid direction" }
18+
19+
iex> RobotSimulator.create(:north, "invalid")
20+
{ :error, "invalid position" }
21+
"""
22+
@spec create(direction :: atom, position :: {integer, integer}) :: { atom, any }
23+
def create(direction \\ :north, position \\ {0, 0}) do
24+
{ :ok, %RobotSimulator{} } |> place(position) |> orient(direction)
25+
end
26+
27+
@doc """
28+
Simulate the robot's movement given a string of instructions.
29+
30+
Valid instructions are: "R" (turn right), "L", (turn left), and "A" (advance)
31+
32+
## Examples
33+
34+
iex> { :ok, robot } = RobotSimulator.create
35+
iex> robot |> RobotSimulator.simulate("RRLAAR")
36+
{ :ok, %RobotSimulator{direction: :south, position: {2, 0}} }
37+
38+
iex> { :ok, robot } = RobotSimulator.create
39+
iex> robot |> RobotSimulator.simulate("RRLAUR")
40+
{ :error, "invalid instruction" }
41+
"""
42+
@spec simulate(robot :: %RobotSimulator{}, instructions :: String.t ) :: { atom, any }
43+
def simulate(%RobotSimulator{} = robot, instructions) do
44+
instructions |> String.graphemes |> Enum.reduce({ :ok, robot }, &move/2)
45+
end
46+
47+
defp orient({ :ok, %RobotSimulator{} = robot }, direction) when direction in @valid_directions do
48+
{ :ok, %RobotSimulator{ robot | direction: direction } }
49+
end
50+
defp orient({ :error, _ } = error, _), do: error
51+
defp orient(_, _), do: { :error, "invalid direction" }
52+
53+
defp place({ :ok, %RobotSimulator{} = robot }, { x, y } = position) when is_integer(x) and is_integer(y) do
54+
{ :ok, %RobotSimulator{ robot | position: position } }
55+
end
56+
defp place({ :error, _ } = error, _), do: error
57+
defp place(_, _), do: { :error, "invalid position" }
58+
59+
defp move("R", { :ok, %RobotSimulator{ direction: direction } } = robot), do: robot |> orient(direction |> rotate_right)
60+
defp move("L", { :ok, %RobotSimulator{ direction: direction } } = robot), do: robot |> orient(direction |> rotate_left)
61+
defp move("A", { :ok, %RobotSimulator{ direction: :north, position: { x, y } } } = robot), do: robot |> place({ x, y + 1 })
62+
defp move("A", { :ok, %RobotSimulator{ direction: :east, position: { x, y } } } = robot), do: robot |> place({ x + 1, y })
63+
defp move("A", { :ok, %RobotSimulator{ direction: :south, position: { x, y } } } = robot), do: robot |> place({ x, y - 1 })
64+
defp move("A", { :ok, %RobotSimulator{ direction: :west, position: { x, y } } } = robot), do: robot |> place({ x - 1, y })
65+
defp move(_, { :error, _ } = error), do: error
66+
defp move(_, _), do: { :error, "invalid instruction" }
67+
68+
for [curr, right] <- @valid_directions |> Stream.cycle |> Enum.take(5) |> Enum.chunk(2, 1) do
69+
defp rotate_right(unquote(curr)), do: unquote(right)
70+
defp rotate_left(unquote(right)), do: unquote(curr)
71+
end
72+
end
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
defmodule RobotSimulator do
2+
defstruct direction: nil, position: nil
3+
4+
@doc """
5+
Create a Robot Simulator given an initial direction and position.
6+
7+
Valid directions are: `:north`, `:east`, `:south`, `:west`
8+
9+
## Examples
10+
11+
iex> RobotSimulator.create(:north, {0, 0})
12+
{ :ok, %RobotSimulator{ direction: :north, position: {0, 0} } }
13+
14+
iex> RobotSimulator.create(:invalid, {0, 0})
15+
{ :error, "invalid direction" }
16+
17+
iex> RobotSimulator.create(:north, "invalid")
18+
{ :error, "invalid position" }
19+
"""
20+
@spec create(direction :: atom, position :: {integer, integer}) :: { atom, any }
21+
def create(direction, position) do
22+
end
23+
24+
@doc """
25+
Simulate the robot's movement given a string of instructions.
26+
27+
Valid instructions are: "R" (turn right), "L", (turn left), and "A" (advance)
28+
29+
## Examples
30+
31+
iex> { :ok, robot } = RobotSimulator.create
32+
iex> robot |> RobotSimulator.simulate("RRLAAR")
33+
{ :ok, %RobotSimulator{direction: :south, position: {2, 0}} }
34+
35+
iex> { :ok, robot } = RobotSimulator.create
36+
iex> robot |> RobotSimulator.simulate("RRLAUR")
37+
{ :error, "invalid instruction" }
38+
"""
39+
@spec simulate(robot :: %RobotSimulator{}, instructions :: String.t ) :: { atom, any }
40+
def simulate(%RobotSimulator{} = robot, instructions) do
41+
end
42+
end
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
if !System.get_env("EXERCISM_TEST_EXAMPLES") do
2+
Code.load_file("robot_simulator.exs", __DIR__)
3+
end
4+
5+
ExUnit.start
6+
ExUnit.configure exclude: :pending, trace: true
7+
8+
defmodule RobotMacros do
9+
defmacro __using__(_options) do
10+
quote do
11+
import unquote(__MODULE__)
12+
end
13+
end
14+
15+
defmacro ok_sim(direction, position) do
16+
quote do
17+
{ :ok, %RobotSimulator{ direction: unquote(direction), position: unquote(position) } }
18+
end
19+
end
20+
end
21+
22+
defmodule RobotSimulatorTest do
23+
use ExUnit.Case
24+
use RobotMacros
25+
26+
test "create has sensible defaults" do
27+
assert RobotSimulator.create == ok_sim(:north, {0, 0})
28+
end
29+
30+
@tag :pending
31+
test "create works with valid arguments" do
32+
assert RobotSimulator.create(:north, {0, 0}) == ok_sim(:north, {0, 0})
33+
assert RobotSimulator.create(:south, {-10, 0}) == ok_sim(:south, {-10, 0})
34+
assert RobotSimulator.create(:east, {0, 10}) == ok_sim(:east, {0, 10})
35+
assert RobotSimulator.create(:west, {100, -100}) == ok_sim(:west, {100, -100})
36+
end
37+
38+
@tag :pending
39+
test "create errors if invalid direction given" do
40+
assert RobotSimulator.create(:invalid, {0, 0}) == { :error, "invalid direction" }
41+
assert RobotSimulator.create(0, {-10, 0}) == { :error, "invalid direction" }
42+
assert RobotSimulator.create("east", {0, 10}) == { :error, "invalid direction" }
43+
end
44+
45+
@tag :pending
46+
test "create errors if invalid position given" do
47+
assert RobotSimulator.create(:north, "invalid") == { :error, "invalid position" }
48+
assert RobotSimulator.create(:north, 0 ) == { :error, "invalid position" }
49+
assert RobotSimulator.create(:north, [0, 0]) == { :error, "invalid position" }
50+
assert RobotSimulator.create(:north, nil) == { :error, "invalid position" }
51+
end
52+
53+
@tag :pending
54+
test "simulate single robot" do
55+
{ :ok, robot } = RobotSimulator.create(:east, { -2, 1 })
56+
57+
assert RobotSimulator.simulate(robot, "RLAALAL") == ok_sim(:west, { 0, 2 })
58+
end
59+
60+
@tag :pending
61+
test "simulate many robots" do
62+
{ :ok, robot1 } = RobotSimulator.create(:north, { 0, 0 })
63+
{ :ok, robot2 } = RobotSimulator.create(:east, { 2, -7 })
64+
{ :ok, robot3 } = RobotSimulator.create(:south, { 8, 4 })
65+
66+
assert RobotSimulator.simulate(robot1, "LAAARALA") == ok_sim(:west, { -4, 1 })
67+
assert RobotSimulator.simulate(robot2, "RRAAAAALA") == ok_sim(:south, { -3, -8 })
68+
assert RobotSimulator.simulate(robot3, "LAAARRRALLLL") == ok_sim(:north, { 11, 5 })
69+
end
70+
71+
@tag :pending
72+
test "simulate errors on invalid instructions" do
73+
{ :ok, robot } = RobotSimulator.create
74+
75+
assert RobotSimulator.simulate(robot, "UUDDLRLRBASTART") == { :error, "invalid instruction" }
76+
end
77+
end

0 commit comments

Comments
 (0)