122 lines
2.6 KiB
Elixir
122 lines
2.6 KiB
Elixir
defmodule Aoc.Solution.Day05 do
|
|
import Aoc.Utils
|
|
|
|
@name "Day 5: Supply Stacks"
|
|
@behaviour Solution
|
|
|
|
@impl Solution
|
|
def get_name, do: @name
|
|
|
|
@impl Solution
|
|
def present(solution), do: "On 9000, top crate on each stack is #{solution}"
|
|
|
|
@impl Solution
|
|
def present_again(solution), do: "On 9001, top crate on each stack is #{solution}"
|
|
|
|
@impl Solution
|
|
def parse!(raw) do
|
|
[initial_state, instructions] = raw |> String.split("\n\n")
|
|
|
|
{
|
|
initial_state |> String.split("\n") |> Enum.drop(-1) |> parse_state(),
|
|
instructions |> String.trim() |> split_lines() |> parse_instructions()
|
|
}
|
|
end
|
|
|
|
@impl Solution
|
|
def solve({state, instructions}) do
|
|
state
|
|
|> move(instructions, 9000)
|
|
|> topmost()
|
|
end
|
|
|
|
@impl Solution
|
|
def solve_again({state, instructions}) do
|
|
state
|
|
|> move(instructions, 9001)
|
|
|> topmost()
|
|
end
|
|
|
|
def parse_state(state) do
|
|
blank = "[-]"
|
|
len = Enum.map(state, &String.length/1) |> Enum.max()
|
|
|
|
Enum.map(state, fn line ->
|
|
line
|
|
|> String.pad_trailing(len)
|
|
|> String.pad_leading(len + 1)
|
|
|> String.replace(" ", " #{blank}")
|
|
end)
|
|
|> Enum.map(fn l ->
|
|
l
|
|
|> String.trim()
|
|
|> String.split()
|
|
end)
|
|
|> Enum.zip()
|
|
|> Enum.map(fn t ->
|
|
t
|
|
|> Tuple.to_list()
|
|
|> Enum.reject(fn v -> v == blank end)
|
|
|> Enum.map(fn v ->
|
|
v
|
|
|> String.slice(1..-2)
|
|
end)
|
|
end)
|
|
end
|
|
|
|
def parse_instructions(lines) do
|
|
Enum.map(lines, fn "move " <> line ->
|
|
[steps, _f, from, _t, to] = String.split(line)
|
|
Enum.map([steps, from, to], &String.to_integer/1)
|
|
end)
|
|
end
|
|
|
|
def move(state, [], _), do: state
|
|
|
|
def move(state, [[n, from, to] | remaining], model) do
|
|
move(
|
|
case model do
|
|
9000 -> _move_9000(state, from - 1, to - 1, n)
|
|
9001 -> _move_9001(state, from - 1, to - 1, n)
|
|
end,
|
|
remaining,
|
|
model
|
|
)
|
|
end
|
|
|
|
def _move_9001(state, from, to, n) do
|
|
{pre, rem} = state |> Enum.at(from) |> Enum.split(n)
|
|
|
|
state
|
|
|> List.update_at(from, fn _ -> rem end)
|
|
|> List.update_at(to, fn l -> pre ++ l end)
|
|
end
|
|
|
|
def _move_9000(state, _from, _to, 0) do
|
|
state
|
|
end
|
|
|
|
def _move_9000(state, from, to, n) do
|
|
case Enum.at(state, from) do
|
|
[a | rem] ->
|
|
_move_9000(
|
|
state
|
|
|> List.update_at(from, fn _ -> rem end)
|
|
|> List.update_at(to, fn l -> [a | l] end),
|
|
from,
|
|
to,
|
|
n - 1
|
|
)
|
|
|
|
[] ->
|
|
state
|
|
|
|
nil ->
|
|
state
|
|
end
|
|
end
|
|
|
|
def topmost(state) do
|
|
state |> Enum.map(&List.first/1) |> Enum.join("")
|
|
end
|
|
end
|