19_beacon.py 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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: rotx(*rotz(*rotz(x, y, z))),
  25. rotz, rotz, rotz,
  26. rotx,
  27. rotz, rotz, rotz,
  28. lambda x, y, z: rotx(*rotx(*rotx(x, y, z))),
  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 largest_intersection(scan1, scan2):
  37. return Counter((x2 - x1, y2 - y1, z2 - z1)
  38. for x1, y1, z1 in scan1
  39. for x2, y2, z2 in scan2).most_common(1)[0]
  40. def move(scan, dx, dy, dz):
  41. return [(x + dx, y + dy, z + dz) for x, y, z in scan]
  42. def find_scanner(base, rotated_scans):
  43. for scan in rotated_scans:
  44. diff, count = largest_intersection(scan, base)
  45. if count >= 12:
  46. return diff, move(scan, *diff)
  47. return None, None
  48. def join(scans):
  49. joined = set(scans[0])
  50. remaining = deque(list(rotations(scan)) for scan in scans[1:])
  51. scanners = [(0, 0, 0)]
  52. while remaining:
  53. rotated_scans = remaining.popleft()
  54. scanner, moved = find_scanner(joined, rotated_scans)
  55. if scanner:
  56. joined |= set(moved)
  57. scanners.append(scanner)
  58. else:
  59. remaining.append(rotated_scans)
  60. return joined, scanners
  61. def max_distance(scanners):
  62. return max(abs(x2 - x1) + abs(y2 - y1) + abs(z2 - z1)
  63. for (x1, y1, z1), (x2, y2, z2) in combinations(scanners, 2))
  64. scans = list(parse(sys.stdin))
  65. joined, scanners = join(scans)
  66. print(len(joined))
  67. print(max_distance(scanners))