22_bricks.py 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. #!/usr/bin/env python3
  2. import sys
  3. from functools import reduce
  4. def parse(line):
  5. left, right = line.rstrip().split('~')
  6. xs, ys, zs = map(int, left.split(','))
  7. xe, ye, ze = map(int, right.split(','))
  8. return [(x, y, z) for x in range(xs, xe + 1)
  9. for y in range(ys, ye + 1)
  10. for z in range(zs, ze + 1)]
  11. def drop(i, brick, stack):
  12. air = 0
  13. while not any(z - air == 1 or (x, y, z - air - 1) in stack
  14. for x, y, z in brick):
  15. air += 1
  16. for x, y, z in brick:
  17. stack[(x, y, z - air)] = i
  18. base = stack.get((x, y, z - air - 1), i)
  19. if base != i:
  20. yield base
  21. def disintegrate(bricks):
  22. bricks = sorted(bricks, key=lambda brick: min(z for x, y, z in brick))
  23. stack = {}
  24. supports = [set(drop(i, brick, stack)) for i, brick in enumerate(bricks)]
  25. dom = [set(range(len(bricks))) for _ in bricks]
  26. lens = [0] * len(dom)
  27. while any(len(d) != l for d, l in zip(dom, lens)):
  28. lens = list(map(len, dom))
  29. dom = [{i} | reduce(set.intersection, map(dom.__getitem__, supp))
  30. if supp else {i} for i, supp in enumerate(supports)]
  31. for i in range(len(dom)):
  32. yield sum(i in d for j, d in enumerate(dom) if j != i)
  33. would_fall = list(disintegrate(map(parse, sys.stdin)))
  34. print(would_fall.count(0))
  35. print(sum(would_fall))