From 0ce095e9e5bb679b628ee7e40b32ebf0fd61194f Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 4 Dec 2024 12:27:06 +0100 Subject: [PATCH] Implement 2024 day 4 --- 2024/src/aoc/days/day4.py | 69 +++++++++++++++++++++++++++++++++++++ 2024/tests/__init__.py | 0 2024/tests/samples/04.1.txt | 5 +++ 2024/tests/samples/04.2.txt | 10 ++++++ 2024/tests/test_day4.py | 26 ++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 2024/src/aoc/days/day4.py create mode 100644 2024/tests/__init__.py create mode 100644 2024/tests/samples/04.1.txt create mode 100644 2024/tests/samples/04.2.txt create mode 100644 2024/tests/test_day4.py diff --git a/2024/src/aoc/days/day4.py b/2024/src/aoc/days/day4.py new file mode 100644 index 0000000..87f8f00 --- /dev/null +++ b/2024/src/aoc/days/day4.py @@ -0,0 +1,69 @@ +import numpy + +from . import SeparateRunner + + +class DayRunner(SeparateRunner): + @classmethod + def part1(cls, input: str) -> int: + grid = numpy.array(list(map(list, input.strip().split("\n")))) + + found = 0 + + directions = [ + (-1, -1), + (-1, 0), + (-1, 1), + (0, -1), + (0, 1), + (1, -1), + (1, 0), + (1, 1), + ] + + word = "XMAS" + + for y in range(grid.shape[0]): + for x in range(grid.shape[1]): + if grid[y, x] != "X": + continue + + for dx, dy in directions: + end_x = x + 3 * dx + end_y = y + 3 * dy + + if ( + end_x < 0 + or end_x >= grid.shape[1] + or end_y < 0 + or end_y >= grid.shape[0] + ): + continue + + if all( + grid[y + i * dy, x + i * dx] == c for i, c in enumerate(word) + ): + found += 1 + + return found + + @classmethod + def part2(cls, input: str) -> int: + grid = numpy.array(list(map(list, input.strip().split("\n")))) + + found = 0 + + magic = ord("M") ^ ord("S") + + for y in range(1, grid.shape[0] - 1): + for x in range(1, grid.shape[1] - 1): + if grid[y, x] != "A": + continue + + first_diag = ord(grid[y - 1, x - 1]) ^ ord(grid[y + 1, x + 1]) + secnd_diag = ord(grid[y - 1, x + 1]) ^ ord(grid[y + 1, x - 1]) + + if first_diag == magic and secnd_diag == magic: + found += 1 + + return found diff --git a/2024/tests/__init__.py b/2024/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/2024/tests/samples/04.1.txt b/2024/tests/samples/04.1.txt new file mode 100644 index 0000000..06d2c4e --- /dev/null +++ b/2024/tests/samples/04.1.txt @@ -0,0 +1,5 @@ +..X... +.SAMX. +.A..A. +XMAS.S +.X.... diff --git a/2024/tests/samples/04.2.txt b/2024/tests/samples/04.2.txt new file mode 100644 index 0000000..1f4eda2 --- /dev/null +++ b/2024/tests/samples/04.2.txt @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX diff --git a/2024/tests/test_day4.py b/2024/tests/test_day4.py new file mode 100644 index 0000000..7ff7887 --- /dev/null +++ b/2024/tests/test_day4.py @@ -0,0 +1,26 @@ +import os + +import pytest + +from aoc.days.day4 import DayRunner + + +def get_data(which: int) -> str: + sample = os.path.dirname(__file__) + f"/samples/04.{which}.txt" + with open(sample, mode="rt", encoding="utf-8") as f: + return f.read() + + +@pytest.mark.parametrize( + "input,answer", + [ + (get_data(1), 4), + (get_data(2), 18), + ], +) +def test_sample_part1(input: str, answer: int) -> None: + assert DayRunner.part1(input) == answer + + +def test_sample_part2() -> None: + assert DayRunner.part2(get_data(2)) == 9