23.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. from more_itertools import flatten
  2. from queue import PriorityQueue
  3. #############
  4. #01.4.7.A.DE#
  5. ###2#5#8#B###
  6. #3#6#9#C#
  7. #########
  8. blank_map = """#############
  9. #...........#
  10. ###.#.#.#.###
  11. #.#.#.#.#
  12. #########"""
  13. final_map = """#############
  14. #...........#
  15. ###A#B#C#D###
  16. #A#B#C#D#
  17. #########"""
  18. from util import get_input
  19. input = get_input("23.input", lambda a: a)
  20. top_row = [(1, 1), (2, 1), (4, 1), (6, 1), (8, 1), (10, 1), (11, 1)]
  21. columns = {
  22. 'A': [(3, 2), (3, 3)],
  23. 'B': [(5, 2), (5, 3)],
  24. 'C': [(7, 2), (7, 3)],
  25. 'D': [(9, 2), (9, 3)],
  26. }
  27. state_map = list(flatten([col for col in columns.values()])) + top_row
  28. cost = {'A': 1, 'B': 10, 'C': 100, 'D': 1000}
  29. def map_to_state(m, state_map):
  30. state = list()
  31. for (x, y) in state_map:
  32. state.append(m[y][x])
  33. return tuple(state)
  34. def state_to_map(state, state_map, blank_map):
  35. m = [[c for c in line] for line in blank_map.splitlines()]
  36. state = list(state)
  37. for i, (x, y) in enumerate(state_map):
  38. m[y][x] = state[i]
  39. return m
  40. def neighbors(pos, m):
  41. (x, y) = pos
  42. return [(x + dx, y + dy) for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)] if m[y + dy][x + dx] == '.']
  43. def try_walk(pos, m, dests, visited, dist):
  44. res = list()
  45. if pos in dests and dist > 0:
  46. res.append((pos, dist))
  47. for n in neighbors(pos, m):
  48. if n in visited:
  49. continue
  50. res += try_walk(n, m, dests, visited + [n], dist + 1)
  51. return res
  52. def all_moves(pos, m, columns):
  53. (x, y) = pos
  54. a = m[y][x]
  55. if a == '.':
  56. return []
  57. col = columns[a]
  58. if pos in col and all([m[y][x] == a for (x, y) in col[col.index(pos):]]):
  59. return []
  60. dests = []
  61. for c in columns.values():
  62. if pos in c:
  63. dests += top_row
  64. break
  65. if all([m[y][x] in [a, '.'] for (x, y) in col]):
  66. dests.append([(x, y) for (x, y) in col if m[y][x] == '.'][-1])
  67. return try_walk(pos, m, dests, [pos], 0)
  68. def next_states(state, state_map, blank_map, columns):
  69. state = list(state)
  70. res = list()
  71. m = state_to_map(state, state_map, blank_map)
  72. for i, a in enumerate(state):
  73. for dest, dist in all_moves(state_map[i], m, columns):
  74. new_state = list(state)
  75. new_state[i] = '.'
  76. new_state[state_map.index(dest)] = a
  77. res.append((tuple(new_state), dist * cost[a]))
  78. return res
  79. def state_search(state, blank_map, final_map, state_map, columns):
  80. final_state = map_to_state(final_map.splitlines(), state_map)
  81. dist_map = {state: 0}
  82. queue = PriorityQueue()
  83. queue.put((0, state))
  84. while not queue.empty():
  85. (d, state) = queue.get()
  86. dist = dist_map[state]
  87. for (next_state, ndist) in next_states(state, state_map, blank_map, columns):
  88. if dist + ndist < dist_map.get(next_state, 99999999):
  89. dist_map[next_state] = dist + ndist
  90. queue.put((dist + ndist, next_state))
  91. return dist_map[final_state]
  92. state = map_to_state(input, state_map)
  93. print("Part 1:", state_search(state, blank_map, final_map, state_map, columns))
  94. columns = {
  95. 'A': [(3, 2), (3, 3),(3, 4), (3, 5)],
  96. 'B': [(5, 2), (5, 3),(5, 4), (5, 5)],
  97. 'C': [(7, 2), (7, 3),(7, 4), (7, 5)],
  98. 'D': [(9, 2), (9, 3),(9, 4), (9, 5)],
  99. }
  100. state_map = list(flatten([col for col in columns.values()])) + top_row
  101. blank_map = """#############
  102. #...........#
  103. ###.#.#.#.###
  104. #.#.#.#.#
  105. #.#.#.#.#
  106. #.#.#.#.#
  107. #########"""
  108. final_map = """#############
  109. #...........#
  110. ###A#B#C#D###
  111. #A#B#C#D#
  112. #A#B#C#D#
  113. #A#B#C#D#
  114. #########"""
  115. extra_in = """ #D#C#B#A#
  116. #D#B#A#C#
  117. """.splitlines()
  118. state = map_to_state(input[0:3] + extra_in + input[3:], state_map)
  119. print("Part 2:", state_search(state, blank_map, final_map, state_map, columns))