24_bugs.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. #!/usr/bin/env python3
  2. import sys
  3. from collections import defaultdict
  4. def read_grid(f):
  5. lines = [line.replace('\n', '') for line in f]
  6. return tuple(x == '#' for line in lines for x in line), len(lines[0])
  7. def evolve_simple(grid, w):
  8. h = len(grid) // w
  9. def adjacent(i):
  10. y, x = divmod(i, w)
  11. nb = 0
  12. if x > 0:
  13. nb += grid[i - 1]
  14. if x < w - 1:
  15. nb += grid[i + 1]
  16. if y > 0:
  17. nb += grid[i - w]
  18. if y < h - 1:
  19. nb += grid[i + w]
  20. return nb
  21. def die_or_infest(i, bug):
  22. return adjacent(i) == 1 if bug else 1 <= adjacent(i) <= 2
  23. return tuple(die_or_infest(i, bug) for i, bug in enumerate(grid))
  24. def find_recurring(grid, w):
  25. seen = set()
  26. while grid not in seen:
  27. seen.add(grid)
  28. grid = evolve_simple(grid, w)
  29. return grid
  30. def biodiversity(grid):
  31. return sum(bug << i for i, bug in enumerate(grid))
  32. def evolve_infinite(grid, w, minutes):
  33. l = len(grid)
  34. assert l == w * w
  35. mid = w // 2 * w + w // 2
  36. top_edge = tuple(range(w))
  37. bottom_edge = tuple(range(l - w, l))
  38. left_edge = tuple(range(0, l, w))
  39. right_edge = tuple(range(w - 1, l, w))
  40. def spread(x, y, nx, ny, level):
  41. ni = ny * w + nx
  42. if ni == mid:
  43. if x > nx:
  44. return right_edge, level + 1
  45. elif x < nx:
  46. return left_edge, level + 1
  47. elif y > ny:
  48. return bottom_edge, level + 1
  49. elif y < ny:
  50. return top_edge, level + 1
  51. elif nx == -1:
  52. return (mid - 1,), level - 1
  53. elif nx == w:
  54. return (mid + 1,), level - 1
  55. elif ny == -1:
  56. return (mid - w,), level - 1
  57. elif ny == w:
  58. return (mid + w,), level - 1
  59. else:
  60. return (ni,), level
  61. def neighbors(i, level):
  62. y, x = divmod(i, w)
  63. yield spread(x, y, x - 1, y, level)
  64. yield spread(x, y, x + 1, y, level)
  65. yield spread(x, y, x, y - 1, level)
  66. yield spread(x, y, x, y + 1, level)
  67. def evolve(bugs):
  68. adjacent = defaultdict(int)
  69. for i, level in bugs:
  70. y, x = divmod(i, w)
  71. for nbs, nblev in neighbors(i, level):
  72. for nb in nbs:
  73. adjacent[(nb, nblev)] += 1
  74. return {x for x, a in adjacent.items()
  75. if a == 1 or (a == 2 and x not in bugs)}
  76. bugs = {(i, 0) for i, bug in enumerate(grid) if bug}
  77. while minutes > 0:
  78. bugs = evolve(bugs)
  79. minutes -= 1
  80. return len(bugs)
  81. grid, w = read_grid(sys.stdin)
  82. print(biodiversity(find_recurring(grid, w)))
  83. print(evolve_infinite(grid, w, 200))