22_reactor.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #!/usr/bin/env python3
  2. import sys
  3. import re
  4. from itertools import starmap
  5. def clamp(n, nmin, nmax):
  6. return max(min(n, nmax), nmin)
  7. def split(start, end, point):
  8. sx, sy, sz = start
  9. ex, ey, ez = end
  10. px, py, pz = point
  11. yield (sx, sy, sz), (px, py, pz)
  12. yield (px, sy, sz), (ex, py, pz)
  13. yield (sx, py, sz), (px, ey, pz)
  14. yield (px, py, sz), (ex, ey, pz)
  15. yield (sx, sy, pz), (px, py, ez)
  16. yield (px, sy, pz), (ex, py, ez)
  17. yield (sx, py, pz), (px, ey, ez)
  18. yield (px, py, pz), (ex, ey, ez)
  19. class Cuboid:
  20. def __init__(self, on, start, end):
  21. self.on = on
  22. self.start = start
  23. self.end = end
  24. self.children = []
  25. def split(self, point):
  26. for start, end in split(self.start, self.end, point):
  27. child = Cuboid(self.on, start, end)
  28. if child.size():
  29. self.children.append(child)
  30. def toggle(self, subset):
  31. if subset.size():
  32. if self.children:
  33. for child in self.children:
  34. child.toggle(subset.clamp(child))
  35. elif subset.on != self.on:
  36. if subset.start == self.start:
  37. if subset.end == self.end:
  38. self.on = subset.on
  39. else:
  40. self.split(subset.end)
  41. self.children[0].on = subset.on
  42. else:
  43. self.split(subset.start)
  44. self.children[-1].toggle(subset)
  45. def size(self):
  46. dx, dy, dz = (r - l for l, r in zip(self.start, self.end))
  47. return dx * dy * dz
  48. def count_on(self):
  49. if self.children:
  50. return sum(child.count_on() for child in self.children)
  51. return self.on * self.size()
  52. def clamp(self, to):
  53. start = tuple(starmap(clamp, zip(self.start, to.start, to.end)))
  54. end = tuple(starmap(clamp, zip(self.end, to.start, to.end)))
  55. return Cuboid(self.on, start, end)
  56. def parse(line):
  57. xmin, xmax, ymin, ymax, zmin, zmax = map(int, re.findall(r'-?\d+', line))
  58. start = xmin, ymin, zmin
  59. end = xmax + 1, ymax + 1, zmax + 1
  60. return Cuboid(line.startswith('on'), start, end)
  61. def reboot(instructions, reactor):
  62. for cuboid in instructions:
  63. reactor.toggle(cuboid.clamp(reactor))
  64. return reactor.count_on()
  65. def stretch(dist):
  66. return Cuboid(False, (-dist, -dist, -dist), (dist + 2, dist + 2, dist + 2))
  67. instructions = list(map(parse, sys.stdin))
  68. print(reboot(instructions, stretch(50)))
  69. print(reboot(instructions, stretch(100000)))