class Grid: def __init__(self, serial): self.serial = serial self.cells = [] self.populate() def populate(self): for y in range(1, 301): self.cells.append([]) for x in range(1, 301): self.cells[y - 1].append([]) power = self.cell_power(x, y) self.set(x, y, power) def get(self, x, y): return self.cells[y - 1][x - 1] def set(self, x, y, value): self.cells[y - 1][x - 1] = value def cell_power(self, x, y): rack = x + 10 p = (rack * y + self.serial) * rack p = (p // 100) % 10 # keep only hundreds digit return p - 5 class Cursor: def __init__(self, parent_grid, cursor_size): self.grid = parent_grid self.size = cursor_size self.x = self.y = 1 self.total_power = self.calc_power() def step(self, direction): if direction in ('right', 'left'): axis = 'vertical' delta_x, delta_y = 1, 0 elif direction in ('up', 'down'): axis = 'horizontal' delta_x, delta_y = 0, 1 if direction in ('right', 'down'): offset_new, offset_old = self.size, 0 elif direction in ('left', 'up'): offset_new, offset_old = -1, self.size - 1 delta_x, delta_y = delta_x * -1, delta_y * -1 new_points = self.get_row(offset_new, axis) old_points = self.get_row(offset_old, axis) self.total_power += sum(new_points) self.total_power -= sum(old_points) self.x, self.y = self.x + delta_x, self.y + delta_y def get_row(self, delta, axis): points = [] if axis == 'vertical': for y in range(self.y, self.y + self.size): points.append(self.grid.get(self.x + delta, y)) elif axis == 'horizontal': for x in range(self.x, self.x + self.size): points.append(self.grid.get(x, self.y + delta)) return points def jump(self, x, y): self.x = x self.y = y self.total_power = self.calc_power() def calc_power(self): total = 0 for y in range(self.size): for x in range(self.size): total += self.grid.get(self.x + x, self.y + y) return total def find_max(self): coords, max_power = (0, 0), 0 prev_dir = 'down' while True: if self.total_power > max_power: coords, max_power = (self.x, self.y), self.total_power if self.x in (1, 301 - self.size) and prev_dir != 'down': # cursor is at edge of grid direction = 'down' elif self.y % 2 == 0: # go left on even lines direction = 'left' else: # go right on odd lines direction = 'right' prev_dir = direction try: self.step(direction) except IndexError: break return coords, max_power grid = Grid(7857) cursor = Cursor(grid, 3) coords, max_power = cursor.find_max() print(f'Part 1: {coords[0]},{coords[1]}, ({max_power})\n') coords = max_power = grid_size = 0 for s in range(1, 301): cursor = Cursor(grid, s) c, m = cursor.find_max() if m > max_power: coords, max_power, grid_size = c, m, s print(f'Part 2: {coords[0]},{coords[1]},{grid_size} ({max_power})\n')