25_santa.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. #!/usr/bin/env python3
  2. import re
  3. import sys
  4. from itertools import combinations
  5. from intcode import read_program, run
  6. def send_command(droid, command):
  7. inputs = map(ord, command + '\n') if command else []
  8. buf = [chr(outp) for outp in map(droid.send, inputs) if outp is not None]
  9. for outp in droid:
  10. if outp is None:
  11. break
  12. buf.append(chr(outp))
  13. return ''.join(buf)
  14. def parse_output(output):
  15. assert output.endswith('Command?\n')
  16. loc = re.search(r'== (.*) ==', output, re.M).group(1)
  17. directions = re.findall(r'- (north|south|east|west)', output)
  18. m = re.search(r'Items here:', output)
  19. items = re.findall(r'- (.*)', output[m.end(0):]) if m else []
  20. return loc, directions, items
  21. def play(droid):
  22. opposite_directions = {'north': 'south', 'south': 'north',
  23. 'west': 'east', 'east': 'west', '': ''}
  24. dangerous_items = {'escape pod', 'giant electromagnet', 'photons',
  25. 'molten lava', 'infinite loop'}
  26. def dfs(prev, direction, path):
  27. nonlocal destination_path
  28. output = send_command(droid, direction)
  29. loc, directions, items = parse_output(output)
  30. for item in items:
  31. if item not in dangerous_items:
  32. send_command(droid, 'take ' + item)
  33. inventory.append(item)
  34. if loc == 'Pressure-Sensitive Floor':
  35. destination_path = tuple(path)
  36. if loc not in visited:
  37. visited.add(loc)
  38. for d in directions:
  39. if d != opposite_directions[direction]:
  40. dfs(loc, d, path + [d])
  41. send_command(droid, opposite_directions[d])
  42. inventory = []
  43. destination_path = None
  44. visited = {'outside'}
  45. dfs('outside', '', [])
  46. for direction in destination_path[:-1]:
  47. send_command(droid, direction)
  48. for dropnum in range(len(inventory)):
  49. for dropitems in combinations(inventory, dropnum):
  50. for item in dropitems:
  51. send_command(droid, 'drop ' + item)
  52. output = send_command(droid, destination_path[-1])
  53. if 'Alert!' not in output:
  54. return int(re.search(r'\d+', output).group(0))
  55. for item in dropitems:
  56. send_command(droid, 'take ' + item)
  57. print(play(run(read_program(sys.stdin))))