11_rtg.py 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. #!/usr/bin/env python3
  2. import re
  3. from collections import namedtuple
  4. from itertools import chain
  5. from queue import PriorityQueue
  6. State = namedtuple('State', 'floors elevator')
  7. def get_score(state):
  8. # - lower accumulated distance of all elements to floor 4 = better
  9. # - elevator position closer to lowest item = better
  10. accumulated_distance = 0
  11. elevator_to_bottom = None
  12. top_dist = len(state.floors) - 1
  13. for i, (rtgs, chips) in enumerate(state.floors):
  14. nitems = len(rtgs) + len(chips)
  15. accumulated_distance += top_dist * nitems
  16. top_dist -= 1
  17. if nitems > 0 and elevator_to_bottom is None:
  18. elevator_to_bottom = state.elevator - i
  19. return accumulated_distance, elevator_to_bottom
  20. def is_valid(state):
  21. def floor_valid(rtgs, chips):
  22. return len(rtgs) == 0 or all(ty in rtgs for ty in chips)
  23. return 0 <= state.elevator < len(state.floors) and \
  24. all(floor_valid(r, c) for r, c in state.floors)
  25. def add_to_floor(floor, new_rtgs, new_chips):
  26. old_rtgs, old_chips = floor
  27. return old_rtgs + new_rtgs, old_chips + new_chips
  28. def valid_steps(state):
  29. floors, el = state
  30. def select(s):
  31. if len(s) > 0:
  32. for i, c in enumerate(s):
  33. yield c, s[:i] + s[i + 1:]
  34. def move_up(rtg, rem_rtgs, chip, rem_chips):
  35. cur = rem_rtgs, rem_chips
  36. top = add_to_floor(floors[el + 1], rtg, chip)
  37. return State(floors[:el] + (cur, top) + floors[el + 2:], el + 1)
  38. def move_down(rtg, rem_rtgs, chip, rem_chips):
  39. cur = rem_rtgs, rem_chips
  40. bot = add_to_floor(floors[el - 1], rtg, chip)
  41. return State(floors[:el - 1] + (bot, cur) + floors[el + 1:], el - 1)
  42. def gen_steps():
  43. rtgs, chips = floors[el]
  44. for rtg1, rem_rtgs1 in select(rtgs):
  45. if el < len(floors) - 1:
  46. for rtg2, rem_rtgs2 in select(rem_rtgs1):
  47. yield move_up(rtg1 + rtg2, rem_rtgs2, '', chips)
  48. for chip, rem_chips in select(chips):
  49. yield move_up(rtg1, rem_rtgs1, chip, rem_chips)
  50. yield move_up(rtg1, rem_rtgs1, '', chips)
  51. if el > 0:
  52. yield move_down(rtg1, rem_rtgs1, '', chips)
  53. for chip1, rem_chips1 in select(chips):
  54. if el < len(floors) - 1:
  55. for chip2, rem_chips2 in select(rem_chips1):
  56. yield move_up('', rtgs, chip1 + chip2, rem_chips2)
  57. yield move_up('', rtgs, chip1, rem_chips1)
  58. if el > 0:
  59. yield move_down('', rtgs, chip1, rem_chips1)
  60. return filter(is_valid, gen_steps())
  61. def min_steps(initial):
  62. worklist = PriorityQueue()
  63. seen = {initial}
  64. worklist.put((get_score(initial), 0, initial))
  65. nprocessed = 0
  66. while not worklist.empty():
  67. score, steps, state = worklist.get()
  68. if score == (0, 0):
  69. return steps
  70. for next_state in valid_steps(state):
  71. if next_state not in seen:
  72. worklist.put((get_score(next_state), steps + 1, next_state))
  73. seen.add(next_state)
  74. assert min_steps(State((('', 'HL'), ('H', ''), ('L', ''), ('', '')), 0)) == 11
  75. part1 = min_steps(State((('SP', 'SP'), ('TRC', 'RC'), ('', 'T'), ('', '')), 0))
  76. print(part1)
  77. print(part1 + min_steps(State((('ED', 'ED'), ('', ''), ('', ''), ('S', 'S')), 3)))
  78. #print(min_steps(State((('SPED', 'SPED'), ('TRC', 'RC'), ('', 'T'), ('', '')), 0)))