Advertisement
Guest User

Untitled

a guest
Dec 20th, 2021
738
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.45 KB | None | 0 0
  1. import math
  2. class Scanner:
  3.     def __init__(self, id_):
  4.         self.points = []
  5.         self.distances = []
  6.         self.id_ = id_
  7.         self.offset = [0, 0, 0]
  8.         self.axis_sign = [1, 1, 1]
  9.         self.axis_map = [0, 1, 2]
  10.  
  11.     def calculate_distances(self):
  12.         self.distances = []
  13.         # Create a matrix of distances
  14.         for i in range(len(self.points)):
  15.             p1 = self.points[i]
  16.             distances = []
  17.  
  18.             for j in range(len(self.points)):
  19.                 if i == j:
  20.                     continue
  21.                 p2 = self.points[j]
  22.                 dist = math.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2 + (p2[2] - p1[2])**2)
  23.                 distances.append(dist)
  24.  
  25.             distances.sort()
  26.             self.distances.append(distances)
  27.  
  28.     def find_overlapping(self, other: 'Scanner'):
  29.         common_points = {}
  30.         for i in range(len(self.distances)):
  31.             for j in range(len(other.distances)):
  32.                 _c = 0
  33.                 if i in common_points:
  34.                     break
  35.                 for k in range(12):
  36.                     if self.distances[i][k] == other.distances[j][k]:
  37.                         _c += 1
  38.                 if _c > 1:
  39.                     # print(_c)
  40.                     common_points[i] = j
  41.         if len(common_points) < 1:
  42.             # Apparently 12 points are always shared.
  43.             return False
  44.  
  45.         # Determine axis switch. By default we assume complete alignment
  46.         axis_map = [0, 1, 2]
  47.         axis_sign = [1, 1, 1]
  48.         offset = [None, None, None]
  49.  
  50.         # For all possibilities of rotated and flipped axes, try to determine
  51.         # the offset.
  52.         for i in range(3):
  53.             # i - axis rotation
  54.             for j in [1, -1]:
  55.  
  56.                 _offset_x = []
  57.                 _offset_y = []
  58.                 _offset_z = []
  59.  
  60.                 for key in common_points:
  61.                     p1 = self.points[key]
  62.                     p2 = other.points[common_points[key]]
  63.                     _offset_x.append(p1[0] - j * p2[i])
  64.                     _offset_y.append(p1[1] - j * p2[i])
  65.                     _offset_z.append(p1[2] - j * p2[i])
  66.  
  67.                 # When applying axis mapping, do not do both switcheroos!
  68.                 # Other final mapping will be wrong.
  69.                 if len(set(_offset_x)) == 1:
  70.                     axis_map[0] = i
  71.                     axis_sign[0] = j
  72.                     offset[0] = _offset_x[0]
  73.                 if len(set(_offset_y)) == 1:
  74.                     axis_map[1] = i
  75.                     axis_sign[1] = j
  76.                     offset[1] = _offset_y[0]
  77.                 if len(set(_offset_z)) == 1:
  78.                     axis_map[2] = i
  79.                     axis_sign[2] = j
  80.                     offset[2] = _offset_z[0]
  81.                 # print(_offset_x, _offset_y, _offset_z)
  82.  
  83.         # If one of the axis is undetermined stop.
  84.         if any([_ == None for _ in offset]):
  85.             return False
  86.  
  87.         other.offset = offset
  88.         other.axis_map = axis_map
  89.         other.axis_sign = axis_sign
  90.         other.align_points()
  91.         return True
  92.  
  93.     def align_points(self):
  94.         """Function that aligns the offset with the root scanner.
  95.        """
  96.         for i in range(len(self.points)):
  97.             x, y, z = self.axis_map
  98.             sx, sy, sz = self.axis_sign
  99.  
  100.             new_x = self.offset[0] + sx * self.points[i][x]
  101.             new_y = self.offset[1] + sy * self.points[i][y]
  102.             new_z = self.offset[2] + sz * self.points[i][z]
  103.  
  104.             self.points[i][0] = new_x
  105.             self.points[i][1] = new_y
  106.             self.points[i][2] = new_z
  107.  
  108.  
  109. scanners = []
  110. file = "day_19.dat"
  111. with open(file, 'r') as f:
  112.     current = None
  113.     _c = 0
  114.     for line in f:
  115.         if line.startswith('---'):
  116.             if current:
  117.                 current.points = points
  118.                 current.calculate_distances()
  119.             current = Scanner(_c)
  120.             _c += 1
  121.             scanners.append(current)
  122.             points = []
  123.             continue
  124.         if not line.strip():
  125.             continue
  126.  
  127.         points.append([int(_) for _ in line.split(',')])
  128.  
  129.     current.points = points
  130.     current.calculate_distances()
  131.  
  132. to_process = scanners[:]
  133. processed = [scanners[0]]
  134. to_process.pop(0)
  135.  
  136. while to_process:
  137.     # pop the first one
  138.     scanner = to_process.pop(0)
  139.  
  140.     for i, aligned in enumerate(processed):
  141.         ok = aligned.find_overlapping(scanner)
  142.         if ok:
  143.             # print(f'Aligned to {i}')
  144.             break
  145.     if ok:
  146.         processed.append(scanner)
  147.         # for aligned in processed:
  148.         #     print(aligned.points)
  149.     else:
  150.         to_process.append(scanner)
  151.  
  152. # Gather all the points and return the number of beacons
  153. points = []
  154. for scanner in processed:
  155.     for point in scanner.points:
  156.         if point in points:
  157.             continue
  158.         else:
  159.             points.append(point)
  160. print("Part 1. Number of beacons:", len(points))
  161.  
  162.  
  163. max_distance_between_2scanners = 0
  164. for i in range(len(processed)):
  165.     for j in range(i, len(processed)):
  166.         s1 = processed[i]
  167.         s2 = processed[j]
  168.  
  169.         dist = abs(s1.offset[0] - s2.offset[0]) + abs(s1.offset[1] - s2.offset[1]) + abs(s1.offset[2] - s2.offset[2])
  170.  
  171.         if dist > max_distance_between_2scanners:
  172.             max_distance_between_2scanners = dist
  173. print("Part 2. Max manhattan distance between two scanners: ", max_distance_between_2scanners)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement