/ Python / 2021 / 23.py
23.py
 1  from lib import *
 2  
 3  input = read_input(2021, 23)
 4  
 5  
 6  def generate_moves(rooms, hallway, n):
 7      def check_hallway(start, end):
 8          return all(hallway[i] is None or i == start for i in range(min(start, end), max(start, end) + 1))
 9  
10      def push_room(idx, elem):
11          return rooms[:idx] + (rooms[idx] + (elem,),) + rooms[idx + 1 :]
12  
13      def pop_room(idx):
14          return rooms[:idx] + (rooms[idx][:-1],) + rooms[idx + 1 :]
15  
16      def set_hallway(idx, elem):
17          return hallway[:idx] + (elem,) + hallway[idx + 1 :]
18  
19      for i, c in enumerate(hallway):
20          if c is None:
21              continue
22  
23          dst = "ABCD".index(c)
24  
25          if any(x != c for x in rooms[dst]):
26              continue
27  
28          if not check_hallway(i, 2 + 2 * dst):
29              continue
30  
31          dist = abs(2 + 2 * dst - i) + (n - len(rooms[dst]))
32  
33          yield dist * 10**dst, push_room(dst, c), set_hallway(i, None)
34  
35          return
36  
37      for i in range(4):
38          if all(x == "ABCD"[i] for x in rooms[i]):
39              continue
40  
41          c = rooms[i][-1]
42  
43          src = 2 + 2 * i
44  
45          dst = "ABCD".index(c)
46  
47          for j in [0, 1, 3, 5, 7, 9, 10]:
48              if not check_hallway(src, j):
49                  continue
50  
51              dist = (1 + n - len(rooms[i])) + abs(src - j)
52  
53              yield dist * 10**dst, pop_room(i), set_hallway(j, c)
54  
55  
56  def solve(part2):
57      lines = input.splitlines()[3:1:-1]
58  
59      if part2:
60          lines.insert(1, "  #D#B#A#C#")
61  
62          lines.insert(2, "  #D#C#B#A#")
63  
64      n = len(lines)
65  
66      queue = [(0, cnt := 0, tuple([*zip(*lines)][3:-1:2]), (None,) * 11)]
67  
68      visited = set()
69  
70      while queue:
71          energy, _, rooms, hallway = heappop(queue)
72  
73          if (rooms, hallway) in visited:
74              continue
75  
76          visited.add((rooms, hallway))
77  
78          if rooms == tuple((c,) * n for c in "ABCD"):
79              return energy
80  
81          for c, r, h in generate_moves(rooms, hallway, n):
82              cnt += 1
83  
84              heappush(queue, (energy + c, cnt, r, h))
85  
86  
87  print(solve(False))
88  print(solve(True))