From f4ef968162639bbd11b878860ad18437de89213b Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Sun, 8 Dec 2024 17:10:03 +0000 Subject: [PATCH] AoC 2024 day 8 --- day8/__init__.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ day8/__main__.py | 25 ++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 day8/__init__.py create mode 100644 day8/__main__.py diff --git a/day8/__init__.py b/day8/__init__.py new file mode 100644 index 0000000..89a67b0 --- /dev/null +++ b/day8/__init__.py @@ -0,0 +1,84 @@ +from dataclasses import dataclass +from typing import List, Tuple +from collections import defaultdict +from copy import copy, deepcopy + + +map_width = 0 +map_height = 0 + + +class Position: + row: int + col: int + + def __init__(self, row: int, col: int): + self.row = row + self.col = col + + def __repr__(self) -> str: + return f"({self.row},{self.col})" + + def __hash__(self): + return hash((self.row, self.col)) + + def is_valid(self) -> bool: + global map_height, map_width + return self.row >= 0 and self.row < map_height \ + and self.col >= 0 and self.col < map_width + + def antinodes_for(self, other: 'Position', part2: bool = False): + dr = self.row - other.row + dc = self.col - other.col + n = 1 + while True: + valid = 0 + a = Position(self.row + (n * dr), self.col + (n * dc)) + if a.is_valid(): + valid += 1 + yield a + b = Position(other.row - (n * dr), other.col - (n * dc)) + if b.is_valid(): + valid += 1 + yield b + if not part2: + break + if valid == 0: + break + n += 1 + if part2: + yield self + yield other + +@dataclass +class Problem: + data: List[str] + towers: defaultdict + + def run(self, part2: bool = False, debug: bool = False) -> int: + newdata = deepcopy(self.data) + for tower in self.towers: + for ndx, node in enumerate(self.towers[tower]): + others = copy(self.towers[tower]) + del others[ndx] + for other in others: + for anti in node.antinodes_for(other, part2): + newdata[anti.row][anti.col] = '#' + if debug: + for line in newdata: + print("".join(line)) + res = "".join(["".join(x) for x in newdata]) + return sum([1 for x in res if x == '#']) + + @staticmethod + def from_stream(stream) -> 'Problem': + towers = defaultdict(list) + data = [list(line.strip()) for line in stream] + global map_width, map_height + map_height = len(data) + map_width = len(data[0]) + for row, cells in enumerate(data): + for col, cell in enumerate(cells): + if cell != '.': + towers[cell].append(Position(row, col)) + return Problem(data, towers) diff --git a/day8/__main__.py b/day8/__main__.py new file mode 100644 index 0000000..41da195 --- /dev/null +++ b/day8/__main__.py @@ -0,0 +1,25 @@ +import sys +import argparse +from . import Problem + + +def main(args=None): + parser = argparse.ArgumentParser(description="AOC 2024 day 8") + parser.add_argument('filename', type=str) + parser.add_argument('-d', '--debug', action='store_true') + parser.add_argument('-2', '--part2', action='store_true') + options = parser.parse_args(args) + + with open(options.filename) as f: + problem = Problem.from_stream(f) + if options.debug: + for row in problem.data: + print("".join(row)) + print() + + print(f"result {problem.run(part2=options.part2, debug=options.debug)}") + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) -- 2.23.0