19_beacon.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/env python3
  2. import sys
  3. from itertools import combinations, starmap
  4. from collections import deque, Counter
  5. def parse(f):
  6. for line in f:
  7. if line.startswith('---'):
  8. scan = []
  9. elif line == '\n':
  10. yield scan
  11. else:
  12. scan.append(tuple(map(int, line.split(','))))
  13. yield scan
  14. def rotx(x, y, z):
  15. return x, -z, y
  16. def rotz(x, y, z):
  17. return -y, x, z
  18. ALL_ROTATIONS = (
  19. rotz, rotz, rotz,
  20. rotx,
  21. rotz, rotz, rotz,
  22. rotx,
  23. rotz, rotz, rotz,
  24. lambda x, y, z: (-x, -z, -y),
  25. rotz, rotz, rotz,
  26. rotx,
  27. rotz, rotz, rotz,
  28. lambda x, y, z: (x, z, -y),
  29. rotz, rotz, rotz,
  30. )
  31. def rotations(scan):
  32. yield scan
  33. for rot in ALL_ROTATIONS:
  34. scan = list(starmap(rot, scan))
  35. yield scan
  36. def move(scan, dx, dy, dz):
  37. return [(x + dx, y + dy, z + dz) for x, y, z in scan]
  38. def find_scanner(base, rotated_scans):
  39. for scan in rotated_scans:
  40. [(diff, count)] = Counter((x2 - x1, y2 - y1, z2 - z1)
  41. for x1, y1, z1 in scan
  42. for x2, y2, z2 in base).most_common(1)
  43. if count >= 12:
  44. return diff, move(scan, *diff)
  45. return None, None
  46. def join(scans):
  47. joined = set(next(scans))
  48. remaining = deque(list(rotations(scan)) for scan in scans)
  49. scanners = [(0, 0, 0)]
  50. while remaining:
  51. rotated_scans = remaining.popleft()
  52. scanner, moved = find_scanner(joined, rotated_scans)
  53. if scanner:
  54. joined |= set(moved)
  55. scanners.append(scanner)
  56. else:
  57. remaining.append(rotated_scans)
  58. return joined, scanners
  59. def max_distance(scanners):
  60. return max(abs(x2 - x1) + abs(y2 - y1) + abs(z2 - z1)
  61. for (x1, y1, z1), (x2, y2, z2) in combinations(scanners, 2))
  62. joined, scanners = join(parse(sys.stdin))
  63. print(len(joined))
  64. print(max_distance(scanners))