advent-of-code/2019-elixir/lib/solutions/day_03.ex

160 lines
4.1 KiB
Elixir
Raw Normal View History

2021-11-01 16:40:46 +01:00
defmodule Aoc19.Solution.Day03 do
@name "Day 3: Crossed Wires"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
@doc """
Parse raw input into a list of paths.
## Examples
iex> Aoc19.Solution.Day03.parse!("U3,R7\\nD11,L13")
[["U3", "R7"], ["D11", "L13"]]
"""
def parse!(raw) do
raw
|> String.split()
|> Enum.map(fn s -> String.split(s, ",") end)
end
@impl Solution
@doc """
Solution for the first part of "Day 3: Crossed Wires".
## Examples
iex> Aoc19.Solution.Day03.solve_first_part("R8,U5,L5,D3\\nU7,R6,D4,L4" |> Aoc19.Solution.Day03.parse!)
6
iex> Aoc19.Solution.Day03.solve_first_part("R75,D30,R83,U83,L12,D49,R71,U7,L72\\nU62,R66,U55,R34,D71,R55,D58,R83" |> Aoc19.Solution.Day03.parse!)
159
iex> Aoc19.Solution.Day03.solve_first_part("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51\\nU98,R91,D20,R16,D67,R40,U7,R15,U6,R7" |> Aoc19.Solution.Day03.parse!)
135
"""
def solve_first_part(input) do
[a, b] =
input
|> Enum.map(&get_wire/1)
|> Enum.map(&MapSet.new/1)
MapSet.intersection(a, b)
|> Enum.map(&manhattan_distance/1)
|> Enum.min()
end
@impl Solution
@doc """
Solution for the second part of "Day 3: Crossed Wires".
## Examples
iex> Aoc19.Solution.Day03.solve_second_part("R8,U5,L5,D3\\nU7,R6,D4,L4" |> Aoc19.Solution.Day03.parse!)
30
iex> Aoc19.Solution.Day03.solve_second_part("R75,D30,R83,U83,L12,D49,R71,U7,L72\\nU62,R66,U55,R34,D71,R55,D58,R83" |> Aoc19.Solution.Day03.parse!)
610
iex> Aoc19.Solution.Day03.solve_second_part("R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51\\nU98,R91,D20,R16,D67,R40,U7,R15,U6,R7" |> Aoc19.Solution.Day03.parse!)
410
"""
def solve_second_part(input) do
wires =
input
|> Enum.map(&get_wire/1)
ab = wires |> Enum.map(&Enum.count/1) |> Enum.sum()
[a, b] = wires
MapSet.intersection(MapSet.new(a), MapSet.new(b))
|> Enum.map(fn pos ->
{Enum.find_index(a, fn x -> x == pos end), Enum.find_index(b, fn x -> x == pos end)}
end)
|> Enum.map(fn {x, y} ->
ab - x - y
end)
|> Enum.min()
end
@doc """
Return a changeset (a tuple of x and y additions) for a defined direction.
"""
def direction("U"), do: {1, 0}
def direction("R"), do: {0, 1}
def direction("D"), do: {-1, 0}
def direction("L"), do: {0, -1}
@doc """
Get a wire's complete spread (in steps) from a list of directions.
## Examples
iex> Aoc19.Solution.Day03.get_wire(["U7", "R6", "D4", "L4"])
[{3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {4, 6}, {5, 6}, {6, 6}, {7, 6}, {7, 5}, {7, 4}, {7, 3}, {7, 2}, {7, 1}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}]
iex> Aoc19.Solution.Day03.get_wire(["R8", "U5", "L5", "D3"])
[{2, 3}, {3, 3}, {4, 3}, {5, 3}, {5, 4}, {5, 5}, {5, 6}, {5, 7}, {5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}, {0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}, {0, 2}, {0, 1}]
"""
def get_wire(paths) do
paths
|> Enum.map(&steps/1)
|> List.flatten()
|> Enum.reduce(
{{0, 0}, []},
fn {x1, y1}, {{x2, y2}, seen} ->
{{x1 + x2, y1 + y2}, [{x1 + x2, y1 + y2} | seen]}
end
)
|> elem(1)
end
@doc """
Get the Manhattan distance from position {0, 0}.
## Examples
iex> Aoc19.Solution.Day03.manhattan_distance({-300, 100})
400
iex> Aoc19.Solution.Day03.manhattan_distance({-20, -20})
40
iex> Aoc19.Solution.Day03.manhattan_distance({111, 111})
222
"""
def manhattan_distance({x, y}), do: abs(x) + abs(y)
@doc """
Get step changesets for a path.
## Examples
iex> Aoc19.Solution.Day03.steps("U4")
[{1, 0}, {1, 0}, {1, 0}, {1, 0}]
iex> Aoc19.Solution.Day03.steps("R2")
[{0, 1}, {0, 1}]
iex> Aoc19.Solution.Day03.steps("D3")
[{-1, 0}, {-1, 0}, {-1, 0}]
iex> Aoc19.Solution.Day03.steps("L1")
[{0, -1}]
"""
def steps(path) do
{d, steps} = String.split_at(path, 1)
modifier = direction(d)
stop_at = String.to_integer(steps)
1..stop_at |> Enum.map(fn _ -> modifier end)
end
end