This commit is contained in:
parent
96f139fe10
commit
ae5527b72d
@ -1,7 +1,121 @@
|
||||
from typing import Any, Iterator
|
||||
import heapq
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Iterator, Literal, Sequence, TypeAlias, cast
|
||||
|
||||
from ..base import BaseSolver
|
||||
|
||||
Action: TypeAlias = Literal[">", "<", "v", "^", "A"]
|
||||
|
||||
NUM_PAD = ((7, 8, 9), (4, 5, 6), (1, 2, 3), (None, 0, "A"))
|
||||
MOV_PAD: tuple[tuple[Action | None, ...], ...] = ((None, "^", "A"), ("<", "v", ">"))
|
||||
|
||||
|
||||
@dataclass(frozen=True, order=True)
|
||||
class Node:
|
||||
robot_1: tuple[int, int] = (0, 2)
|
||||
robot_2: tuple[int, int] = (0, 2)
|
||||
robot_3: tuple[int, int] = (3, 2)
|
||||
|
||||
code: str = ""
|
||||
|
||||
|
||||
def apply_action(
|
||||
robot: tuple[int, int],
|
||||
action: Action,
|
||||
pad: tuple[tuple[int | str | None, ...], ...],
|
||||
):
|
||||
d_row, d_col = {"^": (-1, 0), "v": (1, 0), ">": (0, 1), "<": (0, -1)}[action]
|
||||
row, col = robot[0] + d_row, robot[1] + d_col
|
||||
|
||||
if 0 <= row < len(pad) and 0 <= col < len(pad[row]) and pad[row][col] is not None:
|
||||
return (row, col)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def create_node(node: Node, action: Action) -> Node | None:
|
||||
# main pad moves -> move first robot
|
||||
if action != "A":
|
||||
robot = apply_action(node.robot_1, action, MOV_PAD)
|
||||
if robot is not None:
|
||||
return Node(
|
||||
robot_1=robot,
|
||||
robot_2=node.robot_2,
|
||||
robot_3=node.robot_3,
|
||||
code=node.code,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
# activate pad 1 -> action on robot 1
|
||||
robot_1_action = MOV_PAD[node.robot_1[0]][node.robot_1[1]]
|
||||
assert robot_1_action is not None
|
||||
|
||||
if robot_1_action != "A":
|
||||
robot2 = apply_action(node.robot_2, robot_1_action, MOV_PAD)
|
||||
if robot2 is not None:
|
||||
return Node(
|
||||
robot_1=node.robot_1,
|
||||
robot_2=robot2,
|
||||
robot_3=node.robot_3,
|
||||
code=node.code,
|
||||
)
|
||||
return None
|
||||
|
||||
# activate pad 2 -> action on robot 2
|
||||
robot_2_action = MOV_PAD[node.robot_2[0]][node.robot_2[1]]
|
||||
assert robot_2_action is not None
|
||||
|
||||
if robot_2_action != "A":
|
||||
robot3 = apply_action(node.robot_3, robot_2_action, NUM_PAD)
|
||||
if robot3 is not None:
|
||||
return Node(
|
||||
robot_1=node.robot_1,
|
||||
robot_2=node.robot_2,
|
||||
robot_3=robot3,
|
||||
code=node.code,
|
||||
)
|
||||
return None
|
||||
|
||||
value = NUM_PAD[node.robot_3[0]][node.robot_3[1]]
|
||||
assert value is not None
|
||||
return Node(
|
||||
robot_1=node.robot_1,
|
||||
robot_2=node.robot_2,
|
||||
robot_3=node.robot_3,
|
||||
code=node.code + str(value),
|
||||
)
|
||||
|
||||
|
||||
class Solver(BaseSolver):
|
||||
def solve(self, input: str) -> Iterator[Any]: ...
|
||||
def dijkstra_for_code(self, target: str):
|
||||
queue: list[tuple[float, Node, tuple[str, ...]]] = [(0, Node(), ())]
|
||||
preds: dict[Node, tuple[str, ...]] = {}
|
||||
|
||||
while queue:
|
||||
dis, node, path = heapq.heappop(queue)
|
||||
|
||||
if not target.startswith(node.code):
|
||||
continue
|
||||
|
||||
if node in preds:
|
||||
continue
|
||||
|
||||
preds[node] = path
|
||||
|
||||
if node.code == target:
|
||||
self.logger.info(f"found [{target}]: {''.join(path)} ({len(path)})")
|
||||
return path
|
||||
|
||||
for action in cast(Sequence[Action], "A^v<>"):
|
||||
node_2 = create_node(node, action)
|
||||
if node_2:
|
||||
heapq.heappush(queue, (dis + 1, node_2, path + (action,)))
|
||||
|
||||
return None
|
||||
|
||||
def solve(self, input: str) -> Iterator[Any]:
|
||||
yield sum(
|
||||
len(self.dijkstra_for_code(code) or ()) * int(code[:-1], 10)
|
||||
for code in input.splitlines()
|
||||
)
|
||||
|
@ -0,0 +1,5 @@
|
||||
129A
|
||||
540A
|
||||
789A
|
||||
596A
|
||||
582A
|
@ -0,0 +1,5 @@
|
||||
029A
|
||||
980A
|
||||
179A
|
||||
456A
|
||||
379A
|
Loading…
Reference in New Issue
Block a user