Advent of Code 2022 day 15
It’s looking messier and messier. The grid is huge, so can’t hold it all in memory without causing my computer to crash. Had to abandon to use something less memory heavy.
Part 2 required me to try even harder than part 1. I got partly stuck later on
in part 2 due to having renamed some variables from y
to j
. Never do that.
Tried to use numba
for the first time. There’s a lot I still need to work out
about the package.
from numba import njit from numba.typed import List with open("15.txt") as f: # with open("15_test.txt") as f: data = f.read() # # y = 10 y = 2000000 sensors = List([ ( int(line.split(":")[0].split("x=")[1].split(",")[0]), int(line.split(":")[0].split("y=")[1]) ) for line in data.splitlines() ]) beacons = List([ ( int(line.split(":")[1].split("x=")[1].split(",")[0]), int(line.split(":")[1].split("y=")[1]) ) for line in data.splitlines() ]) @njit def solve(sensors, beacons, y): uniq_beacons = set(beacons) cannot_contain = set() for sensor, beacon in zip(sensors, beacons): distance = abs(beacon[0] - sensor[0]) + abs(beacon[1] - sensor[1]) if sensor[1] - distance <= y and sensor[1] + distance >= y: for i in range( -abs(distance-abs(y-sensor[1])), abs(distance-abs(y-sensor[1])+1) ): if ( (sensor[0]+i, y) not in uniq_beacons and (sensor[0]+i, y) is not sensor ): cannot_contain.add(sensor[0]+i) # for i in range(-distance, distance): # for j in range(-abs(abs(i)-distance), abs(abs(i)-distance)+1): # if ( # y == sensor[1]+j and # (sensor[0]+i, sensor[1]+j) not in uniq_beacons # ): # cannot_contain.add(sensor[0]+i) return len(cannot_contain) print(solve(sensors=sensors, beacons=beacons, y=y)) # Part 2 # max_coord=20 max_coord=4000000 # @njit def occupied(covers): return set(x for a, b in covers for x in range(a, b)) # @njit def overlap(covers, max_coord): covers = sorted(covers, key=lambda x: x[0]) start = 0 for (a, b) in covers: if a > start: return False start = max(start, b) if b > max_coord: return False return True # @njit def solve_row(j, points, max_coord): covers = [] for x, y, distance in points: if y - distance <= j and y + distance >= j: covers.append([ max(x-abs(distance-abs(j-y)), 0), min(x+abs(distance-abs(j-y)+1), max_coord) ]) # occupied = occupied.union(set( # range( # max(x-abs(distance-abs(j-y)), 0), # min(x+abs(distance-abs(j-y)+1), max_coord) # ) # )) if not overlap(covers, max_coord): # if len(occupied:=set(x for a, b in covers for x in range(a, b))) < max_coord: # if len(occupied) == max_coord: # break # if len(occupied) < max_coord: # print(len(occupied)) answer = set(range(max_coord)).difference(occupied(covers)) return answer.pop()*4000000+j return 0 # @njit def solve2(points, max_coord=20): for j in range(max_coord): if ans := solve_row(j, points, max_coord): return ans # @njit def sensor_distances(sensors, beacons): for sensor, beacon in zip(sensors, beacons): distance = abs(beacon[0] - sensor[0]) + abs(beacon[1] - sensor[1]) yield (*sensor, distance) points = [x for x in sensor_distances(sensors, beacons)] print(solve2(points, max_coord=max_coord)) # def sign(x): # if x < 0: # return -1 # return 1 # class Point: # def __init__(self, x, y): # self.point = [x, y] # def __str__(self): # return f"{self.x}, {self.y}" # @property # def x(self): # return self.point[0] # @property # def y(self): # return self.point[1] # class Beacon(Point): # def __init__(self, data): # beacon_data = data.split(": ")[1] # super().__init__( # x=int(beacon_data.split("x=")[1].split(",")[0]), # y=int(beacon_data.split("y=")[1].split(",")[0]) # ) # def __str__(self): # return "B" # class Sensor(Point): # def __init__(self, data, beacons=None): # if beacons is None: # beacons = [] # self.beacon = None # sensor_data = data.split(": ")[0] # super().__init__( # x=int(sensor_data.split("x=")[1].split(",")[0]), # y=int(sensor_data.split("y=")[1].split(",")[0]) # ) # beacon_data = data.split(": ")[1] # beacon_x = int(beacon_data.split("x=")[1].split(",")[0]) # beacon_y = int(beacon_data.split("y=")[1].split(",")[0]) # for beacon in beacons: # if beacon.x == beacon_x and beacon.y == beacon_y: # self.beacon = beacon # break # def __str__(self): # return "S" # class Grid: # def __init__(self, data): # self.beacons = [Beacon(line) for line in data.splitlines()] # self.sensors = [ # Sensor(line, beacons=self.beacons) for line in data.splitlines() # ] # self.max_y = max( # max([beacon.y for beacon in self.beacons]), # max([sensor.y for sensor in self.sensors]) # ) # self.min_y = min( # min(beacon.y for beacon in self.beacons), # min(sensor.y for sensor in self.sensors) # ) # self.max_x = max( # max(beacon.x for beacon in self.beacons), # max(sensor.x for sensor in self.sensors) # ) # self.min_x = min( # min(beacon.x for beacon in self.beacons), # min(sensor.x for sensor in self.sensors) # ) # self.x = self.max_x - self.min_x + 1 # self.y = self.max_y - self.min_y + 1 # print(self.x) # print(self.y) # print(self.x*self.y) # self.grid = "."*self.x*self.y # for beacon in self.beacons: # self.set_value(x=beacon.x, y=beacon.y, value="B") # for sensor in self.sensors: # self.set_value(x=sensor.x, y=sensor.y, value="S") # def __str__(self): # return "\n".join([self.col(j) for j in range(self.y)]) # def at(self, x, y): # if ( # y-self.min_y >= 0 and # x-self.min_x >= 0 and # y <= self.max_y and # x <= self.max_x # ): # return self.grid[((y-self.min_y)*self.x)+(x-self.min_x)] # def col(self, y): # return self.grid[(y-self.min_y)*self.x:(y-self.min_y+1)*self.x] # def set_value(self, x, y, value): # self.grid = ( # self.grid[:((y-self.min_y)*self.x)+x-self.min_x] + # value + # self.grid[((y-self.min_y)*self.x)+x-self.min_x+1:] # ) # def fill(self): # for sensor in self.sensors: # beacon = sensor.beacon # distance = abs(beacon.x - sensor.x) + abs(beacon.y - sensor.y) # for i in range(-distance, distance): # for j in range(-abs(abs(i)-distance), abs(abs(i)-distance)+1): # if self.at(x=sensor.x+i, y=sensor.y+j) == ".": # self.set_value(x=sensor.x+i, y=sensor.y+j, value="#") # def line(self, y): # return len( # [x for x in self.col(y) if x == "#"] # ) # grid = Grid(data) # # print(grid) # # grid.fill() # # print(grid) # # print(grid.line(10)) # # print(grid.line(2000000))