Created
January 7, 2019 22:27
-
-
Save marekciupak/806bb8c7bb0afd1005689f3e8584229b to your computer and use it in GitHub Desktop.
Advent of Code 2018 - Day 13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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