Solve 2024:15 p2 "Warehouse Woes"

Glad this one is over. 150 lines of pure mayhem.
This commit is contained in:
Anders Englöf Ytterström 2024-12-18 00:39:21 +01:00
parent 7c2b4c835a
commit 7688ddb763

View file

@ -1,164 +1,145 @@
import re from output import D, matrix
from collections import Counter, defaultdict, deque
from heapq import heappop, heappush
from itertools import chain, combinations, compress, permutations
from output import ADJ, DD, D, ccw, cw, ints, matrix, mdbg, mhd, vdbg
def solve(data): def solve(data):
o = {k:v for k, v in zip("^>v<", D)} O = {k: v for k, v in zip("^>v<", D)}
grid, movements = data.split("\n\n") grid, movements = data.split("\n\n")
grid, H, W = matrix(grid) grid, H, W = matrix(grid)
movements = "".join(movements.split()) movements = "".join(movements.split())
grid1 = {(r, c):col for r, row in enumerate(grid) for c, col in enumerate(row)}
grid2 = {} grid_1 = {
for k, v in grid1.items(): (r, c): value for r, row in enumerate(grid) for c, value in enumerate(row)
r2, c2 = k }
match v:
grid_2 = {}
for pos, value in grid_1.items():
r2, c2 = pos
match value:
case "O": case "O":
grid2[(r2, c2 * 2)] = "[" grid_2[(r2, c2 * 2)] = "["
grid2[(r2, c2 * 2 + 1)] = "]" grid_2[(r2, c2 * 2 + 1)] = "]"
case "@": case "@":
grid2[(r2, c2 * 2)] = "@" grid_2[(r2, c2 * 2)] = "@"
grid2[(r2, c2 * 2 + 1)] = "." grid_2[(r2, c2 * 2 + 1)] = "."
case _: case _:
grid2[(r2, c2 * 2)] = v grid_2[(r2, c2 * 2)] = value
grid2[(r2, c2 * 2 + 1)] = v grid_2[(r2, c2 * 2 + 1)] = value
ans = []
for grid in [grid1, grid2]: answers = []
pos = [(r, c) for r in range(H) for c in range(W) if grid[(r,c)] == "@"][0]
for T, move in enumerate(movements): for grid, W in [(grid_1, W), (grid_2, W * 2)]:
pos = next((r, c) for r in range(H) for c in range(W) if grid[(r, c)] == "@")
for move in movements:
r, c = pos r, c = pos
targets = closest(grid, r, c, move, W, H) dr, dc = O[move]
moved = False
match move:
case "^":
targets = [((v, c), grid[(v, c)]) for v in range(r)][::-1]
case "<":
targets = [((r, v), grid[(r, v)]) for v in range(c)][::-1]
case "v":
targets = [((v, c), grid[(v, c)]) for v in range(r + 1, H)]
case ">":
targets = [((r, v), grid[(r, v)]) for v in range(c + 1, W)]
if "." not in map(lambda pv: pv[1], targets): if "." not in map(lambda pv: pv[1], targets):
continue continue
dr, dc = o[move]
moved = False
if grid[(r + dr, c + dc)] == ".": if grid[(r + dr, c + dc)] == ".":
moved = True moved = True
if grid[(r + dr, c + dc)] == "O" or (grid[(r + dr, c + dc)] in "[]" and dc != 0):
if grid[(r + dr, c + dc)] == "O" or (
grid[(r + dr, c + dc)] in "[]" and dc != 0
):
canmove = False canmove = False
flips = [] flips = []
for tpos, tvalue in targets: for tpos, tvalue in targets:
flips.append((tpos, tvalue)) flips.append((tpos, tvalue))
if tvalue == "#": if tvalue == "#":
break break
if tvalue == ".": if tvalue == ".":
canmove = True canmove = True
break break
if canmove: if canmove:
rotated = [rtv for rtp, rtv in flips] rotated = [value for _, value in flips]
rotated = rotated[-1:] + rotated[:-1] rotated = rotated[-1:] + rotated[:-1]
for fi, rtp in enumerate(flips):
rtp, *_ = rtp for offset, pos in enumerate(flips):
grid[rtp] = rotated[fi] grid[pos[0]] = rotated[offset]
moved = True moved = True
if grid[(r + dr, c + dc)] in "[]" and dc == 0 and dr != 0: if grid[(r + dr, c + dc)] in "[]" and dc == 0 and dr != 0:
# move cluster of boxes canmove = True
canmove = False bs = set()
flips = [] bmod = O[move][0]
for tpos, tvalue in targets:
flips.append((tpos, tvalue)) Q = [(r + bmod, c)]
if tvalue == "#":
break while Q:
if tvalue == ".": rr, cc = Q.pop(0)
canmove = True
break if (rr, cc) in bs:
if tvalue == "[": continue
# look on right side
# targets = closest(grid, r, c, move) bs.add((rr, cc))
pass
if tvalue == "]": if grid[(rr, cc)] == "#":
# look on right side canmove = False
# targets = closest(grid, r, c, move) continue
pass
if grid[(rr, cc)] in "@.":
continue
match grid[(rr, cc)]:
case "]":
Q.append((rr + bmod, cc))
Q.append((rr, cc - 1))
case "[":
Q.append((rr + bmod, cc))
Q.append((rr, cc + 1))
if canmove: if canmove:
rotated = [rtv for rtp, rtv in flips] patchset = {
rotated = rotated[-1:] + rotated[:-1] (rr, cc): (rr + bmod, cc)
for fi, rtp in enumerate(flips): for rr, cc in bs
rtp, *_ = rtp if grid[(rr, cc)] != "."
grid[rtp] = rotated[fi] }
dotset = set(bs) - set(patchset.values())
for psf, pst in sorted(patchset.items())[::-bmod]:
psnv = grid[psf]
grid[pst] = psnv
for rk in dotset:
grid[rk] = "."
moved = True moved = True
pass
if moved: if moved:
grid[(r, c)] = "." grid[(r, c)] = "."
grid[(r + dr, c + dc)] = "@" grid[(r + dr, c + dc)] = "@"
pos = r + dr, c + dc pos = r + dr, c + dc
if False and T > 67:
print(T, move) answers.append(sum(k[0] * 100 + k[1] for k, v in grid.items() if v in "O["))
print(targets)
show(grid, H, W) p1, p2 = answers
input("--- any key or gtfo ---")
ans.append(sum(k[0] * 100 + k[1] for k, v in grid.items() if v in "O["))
p1, p2 = ans
return p1, p2 return p1, p2
def show(grid, H, W):
for r in range(H):
for c in range(W):
print(grid[(r,c)], end="")
print("")
print("")
def closest(grid, r, c, move, W, H):
match move:
case "^":
targets = [((v,c), grid[(v,c)]) for v in range(r)][::-1]
case "<":
targets = [((r,v), grid[(r,v)]) for v in range(c)][::-1]
case "v":
targets = [((v,c), grid[(v,c)]) for v in range(r+1,H)]
case ">":
targets = [((r,v), grid[(r,v)]) for v in range(c+1,W)]
return targets
if __name__ == "__main__": if __name__ == "__main__":
import os
# use dummy data
inp = """
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#..O@..O.#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
""".strip()
# uncomment to instead use stdin
# import sys; inp = sys.stdin.read().strip()
# uncomment to use AoC provided puzzle input
with open("./input/15.txt", "r") as f: with open("./input/15.txt", "r") as f:
insp = f.read().strip() inp = f.read().strip()
# uncomment to do initial data processing shared by part 1-2
p1, p2 = solve(inp) p1, p2 = solve(inp)
print(p1) print(p1)
os.system(f"echo {p1} | wl-copy")
print(p2) print(p2)
os.system(f"echo {p2} | wl-copy")
# uncomment and replace 0 with actual output to refactor code
# and ensure nonbreaking changes
# assert p1 == 1426855
# assert p2 == 0