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
|
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):
|
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