24_blizzards.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. #!/usr/bin/env python3
  2. import sys
  3. from heapq import heappop, heappush
  4. from math import lcm
  5. def parse(inp):
  6. blizzards = {}
  7. DELTA = (1, 0), (-1, 0), (0, 1), (0, -1)
  8. for y, line in enumerate(inp):
  9. for x, cell in enumerate(line.rstrip()):
  10. if cell not in '#.':
  11. d = DELTA['><v^'.index(cell)]
  12. blizzards.setdefault((x - 1, y - 1), []).append(d)
  13. return blizzards, x - 1, y - 1
  14. def move(blizzards, w, h):
  15. for _ in range(lcm(w, h)):
  16. yield set(blizzards)
  17. newbliz = {}
  18. for (x, y), deltas in blizzards.items():
  19. for dx, dy in deltas:
  20. moved = (x + dx + w) % w, (y + dy + h) % h
  21. newbliz.setdefault(moved, []).append((dx, dy))
  22. blizzards = newbliz
  23. def walk(src, dst, step, blizzard_movement, w, h):
  24. start = src, step
  25. dist = {start: 0}
  26. work = [(0, start)]
  27. endx, endy = dst
  28. while work:
  29. pos, step = current = heappop(work)[1]
  30. if pos == dst:
  31. return step
  32. x, y = pos
  33. bliz = blizzard_movement[(step + 1) % len(blizzard_movement)]
  34. for dx, dy in ((-1, 0), (1, 0), (0, 0), (0, -1), (0, 1)):
  35. nx = x + dx
  36. ny = y + dy
  37. if (0 <= nx < w and 0 <= ny < h and (nx, ny) not in bliz) or \
  38. (nx == w - 1 and ny == h) or (nx == 0 and ny == -1):
  39. nb = (nx, ny), step + 1
  40. nbdist = dist[current] + 1
  41. if nbdist < dist.get(nb, 1000000):
  42. dist[nb] = nbdist
  43. estimate = abs(endx - nx) + abs(endy - ny)
  44. heappush(work, (nbdist + estimate, nb))
  45. blizzards, w, h = parse(sys.stdin)
  46. blizzard_movement = list(move(blizzards, w, h))
  47. start = 0, -1
  48. end = w - 1, h
  49. to_end = walk(start, end, 0, blizzard_movement, w, h)
  50. print(to_end)
  51. to_start = walk(end, start, to_end, blizzard_movement, w, h)
  52. print(walk(start, end, to_start, blizzard_movement, w, h))