From 0825d8db0e059bb5062f8bb081579861ad57ab73 Mon Sep 17 00:00:00 2001 From: Adam Millerchip Date: Mon, 16 Dec 2024 02:55:05 +0900 Subject: [PATCH] =?UTF-8?q?2024=20Day=2015=20Part=202=20=F0=9F=98=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 2024/day15.exs | 142 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 6 deletions(-) diff --git a/2024/day15.exs b/2024/day15.exs index e639cb4..b5dad91 100755 --- a/2024/day15.exs +++ b/2024/day15.exs @@ -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