11_rtg.py 3.3 KB

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