Skip to content

Instantly share code, notes, and snippets.

@marekciupak
Created January 7, 2019 22:27
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 marekciupak/806bb8c7bb0afd1005689f3e8584229b to your computer and use it in GitHub Desktop.
Save marekciupak/806bb8c7bb0afd1005689f3e8584229b to your computer and use it in GitHub Desktop.
Advent of Code 2018 - Day 13
defmodule Day13 do
def parse(input) when is_binary(input) do
input
|> String.split("\n", trim: true)
|> parse
end
def parse(_rows, y \\ 0, acc \\ {%{}, []})
def parse([row | rest], y, {acc_map, acc_carts}) do
{row, carts} = parse_row(row, y)
acc_map = Map.put(acc_map, y, row)
acc_carts = carts ++ acc_carts
parse(rest, y + 1, {acc_map, acc_carts})
end
def parse([], _y, {acc_map, acc_carts}) do
{acc_map, Enum.reverse(acc_carts)}
end
def parse_row(row, y) when is_binary(row) do
row
|> String.codepoints()
|> parse_row(y)
end
def parse_row(_signs, _y, x \\ 0, acc \\ {%{}, []})
def parse_row([sign | rest], y, x, {acc_row, acc_carts}) do
case parse_sign(sign, y, x) do
{sign, cart_list} ->
acc_row = Map.put(acc_row, x, sign)
acc_carts = cart_list ++ acc_carts
parse_row(rest, y, x + 1, {acc_row, acc_carts})
nil ->
parse_row(rest, y, x + 1, {acc_row, acc_carts})
end
end
def parse_row([], _y, _x, {acc_row, acc_carts}) do
{acc_row, Enum.reverse(acc_carts)}
end
def parse_sign(sign, y, x) do
case sign do
"/" -> {:curve_top_left, []}
"-" -> {:path_horizontal, []}
"\\" -> {:curve_top_right, []}
"|" -> {:path_vertical, []}
" " -> nil
"+" -> {:intersection, []}
"^" -> {:path_vertical, [{y, x, :up, :go_left}]}
"v" -> {:path_vertical, [{y, x, :down, :go_left}]}
"<" -> {:path_horizontal, [{y, x, :left, :go_left}]}
">" -> {:path_horizontal, [{y, x, :right, :go_left}]}
end
end
def tick(carts, map) do
carts
|> Enum.sort_by(fn {y, x, _, _} -> {y, x} end)
|> tick(map, [])
end
def tick([cart | rest], map, acc) do
cart = move_cart(map, cart)
if(Enum.any?(rest ++ acc, fn c -> crashed?(c, cart) end)) do
rest = Enum.reject(rest, fn c -> crashed?(c, cart) end)
acc = Enum.reject(acc, fn c -> crashed?(c, cart) end)
tick(rest, map, acc)
else
tick(rest, map, [cart | acc])
end
end
def tick([], _map, acc) do
Enum.reverse(acc)
end
def crashed?({y1, x1, _, _}, {y2, x2, _, _}) do
y1 == y2 && x1 == x2
end
def move_cart(map, {y, x, dir, state}) do
{y, x} = new_position(y, x, dir)
{dir, state} = new_direction_and_state(map[y][x], dir, state)
{y, x, dir, state}
end
def new_position(y, x, dir) do
case dir do
:up -> {y - 1, x}
:down -> {y + 1, x}
:left -> {y, x - 1}
:right -> {y, x + 1}
end
end
def new_direction_and_state(sign, dir, state) do
case {sign, dir, state} do
{:curve_top_left, :left, state} -> {:down, state}
{:curve_top_left, :up, state} -> {:right, state}
{:curve_top_left, :down, state} -> {:left, state}
{:curve_top_left, :right, state} -> {:up, state}
{:curve_top_right, :left, state} -> {:up, state}
{:curve_top_right, :up, state} -> {:left, state}
{:curve_top_right, :down, state} -> {:right, state}
{:curve_top_right, :right, state} -> {:down, state}
{:path_horizontal, :left, state} -> {:left, state}
{:path_horizontal, :right, state} -> {:right, state}
{:path_vertical, :up, state} -> {:up, state}
{:path_vertical, :down, state} -> {:down, state}
{:intersection, :up, :go_left} -> {:left, :go_straight}
{:intersection, :left, :go_left} -> {:down, :go_straight}
{:intersection, :down, :go_left} -> {:right, :go_straight}
{:intersection, :right, :go_left} -> {:up, :go_straight}
{:intersection, dir, :go_straight} -> {dir, :go_right}
{:intersection, :up, :go_right} -> {:right, :go_left}
{:intersection, :right, :go_right} -> {:down, :go_left}
{:intersection, :down, :go_right} -> {:left, :go_left}
{:intersection, :left, :go_right} -> {:up, :go_left}
end
end
def crash_them_all(carts, map) when length(carts) > 1 do
carts
|> tick(map)
|> crash_them_all(map)
end
def crash_them_all([{y, x, _, _}], _map) do
{x, y}
end
def crash_them_all([], _map) do
nil
end
end
defmodule Day13Test do
use ExUnit.Case
test "parsing a single sign from the input" do
assert Day13.parse_sign("/", 0, 0) == {:curve_top_left, []}
assert Day13.parse_sign("-", 0, 0) == {:path_horizontal, []}
assert Day13.parse_sign("\\", 0, 0) == {:curve_top_right, []}
assert Day13.parse_sign("|", 0, 0) == {:path_vertical, []}
assert Day13.parse_sign(" ", 0, 0) == nil
assert Day13.parse_sign("+", 0, 0) == {:intersection, []}
assert Day13.parse_sign("^", 0, 0) == {:path_vertical, [{0, 0, :up, :go_left}]}
assert Day13.parse_sign("v", 0, 0) == {:path_vertical, [{0, 0, :down, :go_left}]}
assert Day13.parse_sign("<", 0, 0) == {:path_horizontal, [{0, 0, :left, :go_left}]}
assert Day13.parse_sign(">", 0, 0) == {:path_horizontal, [{0, 0, :right, :go_left}]}
end
test "parsing the row on the input" do
assert Day13.parse_row("/->>\\ ", 0) ==
{
%{
0 => :curve_top_left,
1 => :path_horizontal,
2 => :path_horizontal,
3 => :path_horizontal,
4 => :curve_top_right
},
[
{0, 2, :right, :go_left},
{0, 3, :right, :go_left}
]
}
end
test "parse the input" do
assert Day13.parse(~S"""
/->-\
| | /----\
| /-+--+-\ |
| | | | v |
\-+-/ \-+--/
\------/
""")
|> elem(0)
|> Map.get(0) == %{
0 => :curve_top_left,
1 => :path_horizontal,
2 => :path_horizontal,
3 => :path_horizontal,
4 => :curve_top_right
}
assert Day13.parse(~S"""
/->-\
| | /----\
| /-+--+-\ |
| | | | v |
\-+-/ \-+--/
\------/
""")
|> elem(1) == [
{0, 2, :right, :go_left},
{3, 9, :down, :go_left}
]
end
test "tick" do
{map, carts} =
Day13.parse(~S"""
/->-\
| | /----\
| /-+--+-\ |
| | | | v |
\-+-/ \-+--/
\------/
""")
assert Day13.tick(carts, map) == [
{0, 3, :right, :go_left},
{4, 9, :right, :go_straight}
]
{map, carts} =
Day13.parse(~S"""
/>-<\
| |
| /<+-\
| | | v
\>+</ |
| ^
\<->/
""")
assert Day13.tick(carts, map) == [
{2, 2, :down, :go_left},
{6, 2, :up, :go_left},
{6, 6, :up, :go_left}
]
end
test "moving carts" do
map = %{
0 => %{
2 => :path_horizontal,
3 => :path_horizontal,
4 => :curve_top_right
}
}
assert Day13.move_cart(map, {0, 2, :right, :go_left}) == {0, 3, :right, :go_left}
assert Day13.move_cart(map, {0, 3, :left, :go_left}) == {0, 2, :left, :go_left}
assert Day13.move_cart(map, {0, 3, :right, :go_left}) == {0, 4, :down, :go_left}
end
test "crashing all the cars" do
{map, carts} =
Day13.parse(~S"""
/>-<\
| |
| /<+-\
| | | v
\>+</ |
| ^
\<->/
""")
assert Day13.crash_them_all(carts, map) == {6, 4}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment