2024 Day 15 Part 2 😵

This commit is contained in:
Adam Millerchip 2024-12-16 02:55:05 +09:00
parent 4937115328
commit 0825d8db0e

View file

@ -2,10 +2,8 @@
defmodule Day15 do
def part1({warehouse, robot, moves}) do
attempt_moves(moves, robot, warehouse)
|> Enum.map(fn
{{x, y}, :box} -> x + y * 100
_ -> 0
end)
|> Enum.filter(&match?({_, :box}, &1))
|> Enum.map(fn {{x, y}, :box} -> x + y * 100 end)
|> Enum.sum()
end
@ -47,8 +45,140 @@ defmodule Day15 do
end
end
def part2(_input) do
:boring_skipped
def part2({warehouse, {x, y}, moves}) do
bigger_warehouse =
Enum.reduce(warehouse, %{}, fn {{x, y}, v}, bigger ->
{left, right} =
case v do
:box -> {{:box, :left}, {:box, :right}}
:wall -> {:wall, :wall}
end
bigger |> Map.put({x * 2, y}, left) |> Map.put({x * 2 + 1, y}, right)
end)
robot = {x * 2, y}
attempt_moves2(moves, robot, bigger_warehouse)
|> Enum.filter(&match?({_, {:box, :left}}, &1))
|> Enum.map(fn {{x, y}, {:box, :left}} -> x + y * 100 end)
|> Enum.sum()
end
def attempt_moves2([], _robot, warehouse), do: warehouse
def attempt_moves2([move | next_moves], robot, warehouse) do
next = move.(robot)
case Map.fetch(warehouse, next) do
:error ->
attempt_moves2(next_moves, next, warehouse)
{:ok, :wall} ->
attempt_moves2(next_moves, robot, warehouse)
{:ok, {:box, side}} ->
case get_pushable_boxes2(next, side, move, warehouse, [{next, side}]) do
:wall ->
attempt_moves2(next_moves, robot, warehouse)
boxes ->
new_boxes = Map.new(boxes, fn {box, side} -> {move.(box), {:box, side}} end)
warehouse =
warehouse
|> Map.drop(Enum.map(boxes, &elem(&1, 0)))
|> Map.merge(new_boxes)
attempt_moves2(next_moves, next, warehouse)
end
end
end
# need to account for 2-width boxes when pushing vertically
# @<--pushing down moves down all RHS of stack
# [][][][]
# [][][] @
# [][] []<--make sure RHS moves too
# []
# @<- pushing up pushes all
def get_pushable_boxes2(box, side, move, warehouse, pushable_boxes) do
next = move.(box)
case abs(elem(next, 1) - elem(box, 1)) do
# horizontal
0 ->
case Map.fetch(warehouse, next) do
:error ->
pushable_boxes
{:ok, :wall} ->
:wall
{:ok, {:box, next_side}} ->
get_pushable_boxes2(next, next_side, move, warehouse, [
{next, next_side} | pushable_boxes
])
end
# vertical
1 ->
{x, y} = box
{other_box, other_side} =
case side do
:left -> {{x + 1, y}, :right}
:right -> {{x - 1, y}, :left}
end
other_next = move.(other_box)
pushable_boxes = [{other_box, other_side} | pushable_boxes]
side
|> case do
:left ->
{{Map.fetch(warehouse, next), Map.fetch(warehouse, other_next)}, next, other_next}
:right ->
{{Map.fetch(warehouse, other_next), Map.fetch(warehouse, next)}, other_next, next}
end
|> case do
{{:error, :error}, _, _} ->
pushable_boxes
{{{:ok, :wall}, _}, _, _} ->
:wall
{{_, {:ok, :wall}}, _, _} ->
:wall
{{{:ok, {:box, side}}, :error}, left, _right} ->
get_pushable_boxes2(left, side, move, warehouse, [{left, side} | pushable_boxes])
{{:error, {:ok, {:box, side}}}, _left, right} ->
get_pushable_boxes2(right, side, move, warehouse, [{right, side} | pushable_boxes])
{{{:ok, {:box, :left}}, {:ok, {:box, :right}}}, left, _} ->
# if left-right, just one box, only need to check one side
# []
# []
get_pushable_boxes2(left, :left, move, warehouse, [{left, :left} | pushable_boxes])
{{{:ok, {:box, :right}}, {:ok, {:box, :left}}}, left, right} ->
# if right-left, two boxes, so need to check both...
# [][]
# []
left = get_pushable_boxes2(left, :right, move, warehouse, [{left, :right}])
right = get_pushable_boxes2(right, :left, move, warehouse, [{right, :left}])
if left == :wall or right == :wall do
:wall
else
pushable_boxes ++ left ++ right
end
end
end
end
def input do