import matrix import math import sys from pprint import pprint import shared from scanf import scanf from typing import Optional from dataclasses import dataclass LIMIT = 4000000 #def fuckoff_ill_do_my_own_cityblock(y1,x1, y2,x2): def cityblock(y1,x1, y2,x2): return abs(y2-y1) + abs(x2-x1) @dataclass class Sensor: sX: int sY: int bX: int bY: int _d: int = None @property def s(self): return (self.sY, self.sX) @property def b(self): return (self.bY, self.bX) @property def distance(self): if self._d is None: self._d = cityblock(self.sY,self.sX, self.bY, self.bX) return self._d def distance_to(self, bY, bX): return cityblock(self.sY,self.sX, bY, bX) def on_line(self, y): midpoint = (y,self.s[1]) d = self.distance_to(*midpoint) if d > self.distance: return [] need = self.distance - d start = (y, midpoint[1] - need) end = (y, midpoint[1] + need) return list(range(start[1],end[1]+1)) def in_diamond(self): sX,sY = self.sX, self.sY up_lim = sY - self.distance dn_lim = sY + self.distance le_lim = sX - self.distance ri_lim = sX + self.distance u = (up_lim, sX) d = (dn_lim, sX) l = (sY, le_lim) r = (sY, ri_lim) infliction = 1 height = -1 for idx, x in enumerate(range(l[1],r[1]+1)): height += infliction if (sY, x) == self.s: infliction = -1 for y in range(sY-height, sY+height+1): yield (y,x) def part1(rows, sample=False): sensors = [] sensor_points = [] beacon_points = [] ineligible_points = set() xSet = set() ySet = set() for row in rows: x,y,bx,by = scanf("Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row) xSet.add(x) xSet.add(bx) ySet.add(y) ySet.add(by) sensors.append(Sensor(sX=x,sY=y,bX=bx,bY=by)) minX, maxX = min(xSet),max(xSet) minY, maxY = min(ySet),max(ySet) for sensor in sensors: sensor_points.append(sensor.s) beacon_points.append(sensor.b) if sample: for yx in sensor.in_diamond(): ineligible_points.add(yx) CHECK_ROW = 2000000 if sample: CHECK_ROW = 10 ineligible = set() for s in sensors: coll = s.on_line(CHECK_ROW) ineligible.update(coll) count_ignoring_current_beacons = 0 for i in ineligible: if (CHECK_ROW, i) not in beacon_points: count_ignoring_current_beacons += 1 print(count_ignoring_current_beacons, "with removing beacons, final answer") if not sample: return mx = matrix.matrix_of_size(maxX+1, maxY+1) for yx in ineligible_points: y,x = yx if y >= 0 and x >= 0: if y <= maxY and x <= maxX: mx[y][x] = "#" for yx in beacon_points: y,x = yx if y >= 0 and x >= 0: if y <= maxY and x <= maxX: mx[y][x] = "B" for yx in sensor_points: y,x = yx if y >= 0 and x >= 0: if y <= maxY and x <= maxX: mx[y][x] = "S" print(matrix.ppmx(mx, pad=False,space=True)) tuning = lambda y,x: y + (LIMIT * x) def part2(rows): sensors = [] sensor_points = [] beacon_points = [] xSet = set() ySet = set() for row in rows: x,y,bx,by = scanf("Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", row) xSet.add(x) xSet.add(bx) ySet.add(y) ySet.add(by) sensors.append(Sensor(sX=x,sY=y,bX=bx,bY=by)) minX, maxX = min(xSet),max(xSet) minY, maxY = min(ySet),max(ySet) for sensor in sensors: beacon_points.append(sensor.b) #coord_range = (0, LIMIT) coord_range = (0, 20) ineligible = set() for s in sensors: coll = s.on_line(CHECK_ROW) ineligible.update(coll) count_ignoring_current_beacons = 0 for i in ineligible: if (CHECK_ROW, i) not in beacon_points: count_ignoring_current_beacons += 1 print(count_ignoring_current_beacons, "with removing beacons, final answer") def main(): sample = False if sys.argv[-1] == "--sample": sample = True rows = [row for row in shared.load_rows(15)] with shared.elapsed_timer() as elapsed: part1(rows, sample) print("🕒", elapsed()) #with shared.elapsed_timer() as elapsed: # part2(rows) # print("🕒", elapsed()) if __name__ == "__main__": main()