138 lines
3 KiB
Elixir
138 lines
3 KiB
Elixir
defmodule Day12 do
|
|
defp read_input do
|
|
Path.expand('input', Path.dirname(__ENV__.file))
|
|
|> File.read!()
|
|
end
|
|
|
|
defp parse_instructions(text) do
|
|
String.split(text, "\n", trim: true)
|
|
|> Enum.map(fn line ->
|
|
{
|
|
String.first(line) |> String.to_atom(),
|
|
String.slice(line, 1..-1) |> String.to_integer()
|
|
}
|
|
end)
|
|
end
|
|
|
|
defp steer([], _heading, position) do
|
|
position
|
|
end
|
|
|
|
defp steer([instruction | rest], heading, position = {x, y}) do
|
|
{new_heading, new_position} =
|
|
case instruction do
|
|
{:N, dy} -> {heading, {x, y + dy}}
|
|
{:S, dy} -> {heading, {x, y - dy}}
|
|
{:E, dx} -> {heading, {x + dx, y}}
|
|
{:W, dx} -> {heading, {x - dx, y}}
|
|
{:L, deg} -> {turn(heading, deg), position}
|
|
{:R, deg} -> {turn(heading, -deg), position}
|
|
{:F, dist} -> {heading, move(heading, position, dist)}
|
|
end
|
|
|
|
steer(rest, new_heading, new_position)
|
|
end
|
|
|
|
defp turn(heading, degrees) do
|
|
hd = %{E: 0, N: 90, W: 180, S: 270}
|
|
dh = Enum.into(hd, %{}, fn {h, d} -> {d, h} end)
|
|
|
|
new_deg = Map.fetch!(hd, heading) + degrees
|
|
Map.fetch!(dh, rem(new_deg + 360, 360))
|
|
end
|
|
|
|
defp move(heading, {x, y}, distance) do
|
|
case heading do
|
|
:N -> {x, y + distance}
|
|
:S -> {x, y - distance}
|
|
:E -> {x + distance, y}
|
|
:W -> {x - distance, y}
|
|
end
|
|
end
|
|
|
|
defp manhattan({x1, y1}, {x2, y2}) do
|
|
abs(x1 - x2) + abs(y1 - y2)
|
|
end
|
|
|
|
defp test_input do
|
|
"""
|
|
F10
|
|
N3
|
|
F7
|
|
R90
|
|
F11
|
|
"""
|
|
end
|
|
|
|
def part1 do
|
|
read_input()
|
|
|> parse_instructions()
|
|
|> steer(:E, {0, 0})
|
|
|> manhattan({0, 0})
|
|
end
|
|
|
|
def part2 do
|
|
read_input()
|
|
|> parse_instructions()
|
|
|> point({0, 0}, {10, 1})
|
|
|> manhattan({0, 0})
|
|
end
|
|
|
|
defp point([], ferry, _waypoint) do
|
|
ferry
|
|
end
|
|
|
|
defp point([instruction | rest], ferry, waypoint) do
|
|
{new_ferry, new_waypoint} =
|
|
case instruction do
|
|
{:N, dy} -> {ferry, v_add(waypoint, {0, +dy})}
|
|
{:S, dy} -> {ferry, v_add(waypoint, {0, -dy})}
|
|
{:E, dx} -> {ferry, v_add(waypoint, {+dx, 0})}
|
|
{:W, dx} -> {ferry, v_add(waypoint, {-dx, 0})}
|
|
{:L, deg} -> {ferry, rotate(waypoint, deg)}
|
|
{:R, deg} -> {ferry, rotate(waypoint, -deg)}
|
|
{:F, dist} -> {move2(waypoint, ferry, dist), waypoint}
|
|
end
|
|
|
|
point(rest, new_ferry, new_waypoint)
|
|
end
|
|
|
|
defp move2({vx, vy}, {rx, ry}, d) do
|
|
{rx + d * vx, ry + d * vy}
|
|
end
|
|
|
|
defp v_add({tx, ty}, {rx, ry}) do
|
|
{tx + rx, ty + ry}
|
|
end
|
|
|
|
def c_mul({r1, i1}, {r2, i2}) do
|
|
r = r1 * r2 - i1 * i2
|
|
i = r1 * i2 + i1 * r2
|
|
{r, i}
|
|
end
|
|
|
|
def c_pow(_z, 0), do: {1, 0}
|
|
|
|
def c_pow(z, n) do
|
|
Enum.reduce(Enum.into(1..n, [], fn _ -> z end), &c_mul/2)
|
|
end
|
|
|
|
defp rotate(vector, deg) do
|
|
ticks = div(deg, 90)
|
|
|
|
if ticks < 0 do
|
|
c_mul(vector, c_pow({0, -1}, -ticks))
|
|
else
|
|
c_mul(vector, c_pow({0, 1}, ticks))
|
|
end
|
|
end
|
|
|
|
def debug do
|
|
IO.inspect({-56, 28})
|
|
rotate({56, -28}, 180)
|
|
rotate({56, -28}, -180)
|
|
end
|
|
end
|
|
|
|
Day12.part1() |> IO.inspect()
|
|
Day12.part2() |> IO.inspect()
|