15_repair.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env python3
  2. import sys
  3. from collections import defaultdict, deque
  4. from operator import add, mul, lt, eq
  5. from time import sleep
  6. def run(p, get_input, memsize=0):
  7. def decode_param(offset):
  8. return p[pc + offset], modes // (10 ** (offset - 1)) % 10
  9. def pload(offset):
  10. param, mode = decode_param(offset)
  11. return param if mode == 1 else p[param + relbase * mode // 2]
  12. def pstore(offset, value):
  13. param, mode = decode_param(offset)
  14. p[param + relbase * mode // 2] = value
  15. opmap = {1: add, 2: mul, 7: lt, 8: eq}
  16. p = p + [0] * memsize
  17. pc = relbase = 0
  18. while p[pc] != 99:
  19. modes, opcode = divmod(p[pc], 100)
  20. if opcode in (1, 2, 7, 8):
  21. pstore(3, opmap[opcode](pload(1), pload(2)))
  22. pc += 4
  23. elif opcode == 3:
  24. pstore(1, get_input())
  25. pc += 2
  26. elif opcode == 4:
  27. yield pload(1)
  28. pc += 2
  29. elif opcode == 5:
  30. pc = pload(2) if pload(1) else pc + 3
  31. elif opcode == 6:
  32. pc = pload(2) if not pload(1) else pc + 3
  33. elif opcode == 9:
  34. relbase += pload(1)
  35. pc += 2
  36. NORTH, SOUTH, EAST, WEST = range(1, 5)
  37. WALL, SPACE, OXYGEN, UNKOWN = range(4)
  38. DX = [0, 0, -1, 1]
  39. DY = [-1, 1, 0, 0]
  40. def draw(mapped, robot):
  41. if '-v' not in sys.argv:
  42. return
  43. def draw_pos(pos):
  44. return 'D' if pos == robot else '#.O '[mapped.get(pos, UNKOWN)]
  45. minx = min(x for x, y in mapped)
  46. maxx = max(x for x, y in mapped)
  47. miny = min(y for y, y in mapped)
  48. maxy = max(y for y, y in mapped)
  49. print('\033c', end='')
  50. for y in range(miny, maxy + 1):
  51. print(''.join(draw_pos((x, y)) for x in range(minx, maxx + 1)))
  52. sleep(.001)
  53. def map_area(program):
  54. reverse_dir = [SOUTH, NORTH, WEST, EAST]
  55. pos = 0, 0
  56. direction = NORTH
  57. control = run(program, lambda: direction, 0)
  58. mapped = {pos: SPACE}
  59. path = [(NORTH, pos)]
  60. oxygen_distance = None
  61. def pick_direction():
  62. x, y = pos
  63. for d in (NORTH, SOUTH, EAST, WEST):
  64. newpos = x + DX[d - 1], y + DY[d - 1]
  65. if newpos not in mapped:
  66. return False, (d, newpos)
  67. return True, path.pop()
  68. while len(path):
  69. backtrack, (direction, newpos) = pick_direction()
  70. status = next(control)
  71. mapped[newpos] = status
  72. if status != WALL:
  73. if not backtrack:
  74. path.append((reverse_dir[direction - 1], pos))
  75. pos = newpos
  76. if status == OXYGEN:
  77. oxygen_distance = len(path) - 1
  78. draw(mapped, pos)
  79. return oxygen_distance, mapped
  80. def spread_oxygen(mapped):
  81. frontier = set(pos for pos, status in mapped.items() if status == OXYGEN)
  82. minutes = -1
  83. while frontier:
  84. newfrontier = set()
  85. for x, y in frontier:
  86. mapped[(x, y)] = OXYGEN
  87. for d in range(4):
  88. nb = x + DX[d], y + DY[d]
  89. if mapped[nb] == SPACE:
  90. newfrontier.add(nb)
  91. minutes += 1
  92. frontier = newfrontier
  93. draw(mapped, None)
  94. return minutes
  95. program = list(map(int, sys.stdin.readline().split(',')))
  96. oxygen_distance, mapped = map_area(program)
  97. print(oxygen_distance)
  98. print(spread_oxygen(mapped))