22.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from util import get_input
  2. from more_itertools import flatten
  3. from itertools import product
  4. def parse(line):
  5. action, stuff = line.split()
  6. coords = [s[2:].split("..") for s in stuff.split(",")]
  7. coords = [(int(a), int(b) + 1) for [a, b] in coords]
  8. return (action == 'on', tuple(coords))
  9. input = get_input("22.input", fn=parse)
  10. def part1():
  11. area = set()
  12. for (on, coords) in input:
  13. for x in range(max(coords[0][0], -50), min(coords[0][1], 50)):
  14. for y in range(max(coords[1][0], -50), min(coords[1][1], 50)):
  15. for z in range(max(coords[2][0], -50), min(coords[2][1], 50)):
  16. if on:
  17. area.add((x, y, z))
  18. else:
  19. area.discard((x, y, z))
  20. print("Part 1:", len(area))
  21. def split_by_plane(blk, coord, val):
  22. res = list()
  23. nblk = list(blk)
  24. if val > blk[coord][0]:
  25. nblk[coord] = (blk[coord][0], min(val, blk[coord][1]))
  26. res.append(tuple(nblk))
  27. if val < blk[coord][1]:
  28. nblk[coord] = (max(val, blk[coord][0]), blk[coord][1])
  29. res.append(tuple(nblk))
  30. return res
  31. def split_by(a, b):
  32. res = {a}
  33. for coord in range(3):
  34. for idx in range(2):
  35. res = set(flatten([split_by_plane(block, coord, b[coord][idx]) for block in res]))
  36. return res
  37. def intersects_coord(a, b):
  38. r = range(b[0] + 1, b[1])
  39. return a[0] in r or a[1] in r or (a[0] == b[0] and a[1] == b[1])
  40. def intersects(a, b):
  41. for coord in range(3):
  42. if not (intersects_coord(a[coord], b[coord]) or intersects_coord(b[coord], a[coord])):
  43. return False
  44. return True
  45. def contains(b, a):
  46. for coord in range(3):
  47. if range(a[coord][0], a[coord][1]) not in range(b[coord][0], b[coord][1]):
  48. return False
  49. return True
  50. def check_intersection(aa, bb):
  51. for a in aa:
  52. for b in bb:
  53. if a != b:
  54. if intersects(a, b):
  55. raise ValueError("Intersection", a, b)
  56. def block_size(a):
  57. return (a[0][1] - a[0][0]) * (a[1][1] - a[1][0]) * (a[2][1] - a[2][0])
  58. def test_split():
  59. orig = ((-10, 10), (-10, 10), (-10, 10))
  60. rem = ((-5, 5), (-5, 5), (-5, 5))
  61. split = set(split_by(orig, rem))
  62. print(block_size(orig))
  63. print(block_size(rem))
  64. print(sum(block_size(a) for a in split))
  65. res = split.difference({rem})
  66. print(sum(block_size(a) for a in res))
  67. check_intersection(split, split)
  68. area = set()
  69. for i, (on, nblock) in enumerate(input):
  70. print(i, "/", len(input))
  71. if not on:
  72. for eblock in list(area):
  73. if not intersects(eblock, nblock):
  74. continue
  75. if eblock == nblock:
  76. continue
  77. if contains(nblock, eblock):
  78. area.remove(eblock)
  79. continue
  80. area.remove(eblock)
  81. newarea = set(split_by(eblock, nblock))
  82. area = area.union(newarea)
  83. area = {b for b in area if not intersects(b, nblock)}
  84. else:
  85. nblocks = {nblock}
  86. possible_issues = [a for a in area if intersects(a, nblock)]
  87. for eblock in possible_issues:
  88. for nblock in list(nblocks):
  89. if eblock == nblock:
  90. continue
  91. if not intersects(eblock, nblock):
  92. continue
  93. nblocks.remove(nblock)
  94. new = set(split_by(nblock, eblock))
  95. new = {b for b in new if not intersects(b, eblock)}
  96. nblocks = nblocks.union(new)
  97. area = area.union(nblocks)
  98. part1()
  99. print("Part 2:", sum(block_size(b) for b in area))