from tools.aoc import AOCDay from tools.coordinate import Coordinate from tools.grid import Grid, GridTransformation from typing import Any FLIPS = [ GridTransformation.ROTATE_RIGHT, GridTransformation.ROTATE_RIGHT, GridTransformation.ROTATE_RIGHT, GridTransformation.ROTATE_RIGHT, GridTransformation.FLIP_HORIZONTALLY, GridTransformation.ROTATE_RIGHT, GridTransformation.ROTATE_RIGHT, GridTransformation.ROTATE_RIGHT, ] def init_grid() -> Grid: grid = Grid() grid.set(Coordinate(1, 0)) grid.set(Coordinate(2, 1)) grid.set(Coordinate(0, 2)) grid.set(Coordinate(1, 2)) grid.set(Coordinate(2, 2)) return grid class Day(AOCDay): inputs = [ [ (12, "input21_test"), (176, "input21") ], [ (2368161, "input21") ] ] def load_replacement_grids(self) -> dict: ret = {} for line in self.getInput(): rep, grid_string = line.split(" => ") ret[rep] = Grid.from_str(grid_string, true_char='#') rep_grid_size = len(rep.split("/")[0]) + 1 ret[rep].minX, ret[rep].maxX, ret[rep].minY, ret[rep].maxY = 0, rep_grid_size, 0, rep_grid_size return ret def interact(self, interact_count: int) -> int: grid = Grid.from_str('.#./..#/###') grid_size = 3 replacements = self.load_replacement_grids() for i in range(2 if self._current_test_file.endswith('test') else interact_count): new_grid = Grid() block_size = 3 replace_size = 4 if grid_size % 2 == 0: block_size = 2 replace_size = 3 for x in range(0, grid_size, block_size): for y in range(0, grid_size, block_size): sub_grid = grid.sub_grid(x, y, x + block_size - 1, y + block_size - 1) sub_grid.minX, sub_grid.minY, sub_grid.maxX, sub_grid.maxY = 0, 0, block_size - 1, block_size - 1 for f in FLIPS: sub_grid.transform(f) sub_grid.maxX, sub_grid.maxY = block_size - 1 + sub_grid.minX, block_size - 1 + sub_grid.minY if str(sub_grid) in replacements: new_grid.update( x // block_size * replace_size, y // block_size * replace_size, replacements[str(sub_grid)] ) break else: raise ValueError("No sub_grid matches found.") grid = new_grid grid_size = grid_size // block_size * replace_size return grid.getOnCount() def part1(self) -> Any: return self.interact(5) def part2(self) -> Any: return self.interact(18) if __name__ == '__main__': day = Day(2017, 21) day.run(verbose=True)