From 0c7c54b5c964f78234b828b861f32beff057833f Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Fri, 6 Dec 2024 09:20:08 +0100 Subject: [PATCH] Implement 2024 day 6 --- 2024/src/aoc/days/day6.py | 105 ++++++++++++++++++++++++++++++++++++++ 2024/tests/samples/06.txt | 10 ++++ 2024/tests/test_day6.py | 17 ++++++ 3 files changed, 132 insertions(+) create mode 100644 2024/src/aoc/days/day6.py create mode 100644 2024/tests/samples/06.txt create mode 100644 2024/tests/test_day6.py diff --git a/2024/src/aoc/days/day6.py b/2024/src/aoc/days/day6.py new file mode 100644 index 0000000..b327383 --- /dev/null +++ b/2024/src/aoc/days/day6.py @@ -0,0 +1,105 @@ +import numpy + +from . import SeparateRunner + + +def does_loop( + grid: numpy.array, + x: int, + y: int, + dx: int, + dy: int, + visited: set[tuple[int, int, int, int]], +) -> bool: + try: + while True: + while y + dy >= 0 and x + dx >= 0 and grid[y + dy, x + dx] == "#": + dx, dy = -dy, dx + + x += dx + y += dy + + if x < 0 or y < 0: + return False + + pos = (x, y, dx, dy) + + if pos in visited: + return True + else: + visited.add(pos) + except IndexError: + return False + + +class DayRunner(SeparateRunner): + @classmethod + def part1(cls, input: str) -> int: + grid = input.strip().split("\n") + + for y, line in enumerate(grid): + if (x := line.find("^")) != -1: + break + + dx = 0 + dy = -1 + + visited = {(x, y)} + + try: + while True: + nx = x + dx + ny = y + dy + + if grid[ny][nx] == "#": + dx, dy = -dy, dx + else: + x, y = nx, ny + visited.add((x, y)) + except IndexError: + pass + + return len(visited) + + @classmethod + def part2(cls, input: str) -> int: + grid = numpy.array(list(map(list, input.strip().split("\n")))) + y, x = numpy.where(grid == "^") + + y = y[0] + x = x[0] + + dx = 0 + dy = -1 + loops = 0 + + visited = {(x, y, dx, dy)} + tiles_visited = {(x, y)} + + try: + while True: + while y + dy >= 0 and x + dx >= 0 and grid[y + dy, x + dx] == "#": + dx, dy = -dy, dx + + nx = x + dx + ny = y + dy + + if nx < 0 or ny < 0: + break + + if (nx, ny) not in tiles_visited: + # check for a loop + grid[ny, nx] = "#" + if does_loop(grid, x, y, dx, dy, visited.copy()): + loops += 1 + grid[ny, nx] = "L" + else: + grid[ny, nx] = "." + + x, y = nx, ny + tiles_visited.add((x, y)) + visited.add((x, y, dx, dy)) + except IndexError: + pass + + return loops diff --git a/2024/tests/samples/06.txt b/2024/tests/samples/06.txt new file mode 100644 index 0000000..a4eb402 --- /dev/null +++ b/2024/tests/samples/06.txt @@ -0,0 +1,10 @@ +....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#... diff --git a/2024/tests/test_day6.py b/2024/tests/test_day6.py new file mode 100644 index 0000000..3245015 --- /dev/null +++ b/2024/tests/test_day6.py @@ -0,0 +1,17 @@ +import os + +from aoc.days.day6 import DayRunner + + +def get_data() -> str: + sample = os.path.dirname(__file__) + "/samples/06.txt" + with open(sample, mode="rt", encoding="utf-8") as f: + return f.read() + + +def test_sample_part1() -> None: + assert DayRunner.part1(get_data()) == 41 + + +def test_sample_part2() -> None: + assert DayRunner.part2(get_data()) == 6