Skip to content

Commit

Permalink
2024 day 4
Browse files Browse the repository at this point in the history
  • Loading branch information
Ted Cassirer committed Dec 4, 2024
1 parent 42e1dfa commit 8ecf902
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 10 deletions.
75 changes: 75 additions & 0 deletions aoc_cas/aoc2024/day4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from aoc_cas.util import solve_with_data

UP = (-1, 0)
DOWN = (1, 0)
LEFT = (0, -1)
RIGHT = (0, 1)
NE = (-1, 1)
NW = (-1, -1)
SE = (1, 1)
SW = (1, -1)


def _count_xmas(grid: list[list[str]], y: int, x: int) -> int:
M, N = len(grid), len(grid[0])
word_to_find = "XMAS"
word_count = 0
for dy, dx in [UP, DOWN, LEFT, RIGHT, NE, NW, SE, SW]:
for i, c in enumerate(word_to_find):
yy, xx = y + dy * i, x + dx * i
if not (0 <= yy < M and 0 <= xx < N) or grid[yy][xx] != c:
break
else:
word_count += 1
return word_count


def _parse_data(data: str) -> list[list[str]]:
return [list(line) for line in data.splitlines()]


def part_a(data: str) -> int:
grid = _parse_data(data)
M, N = len(grid), len(grid[0])
xmas_count = 0
for y in range(M):
for x in range(N):
xmas_count += _count_xmas(grid, y, x)
return xmas_count


def _is_mas_x(grid: list[list[str]], y: int, x: int) -> bool:
M, N = len(grid), len(grid[0])
if not (1 <= y < M - 1 and 1 <= x < N - 1):
# OOB
return False
# Build a string of the current cell and its diagonal neighbors
# 5.2
# .1.
# 4.3
coords = ((y + dy, x + dx) for dy, dx in [(0, 0), NE, SE, SW, NW])
x_string = "".join(grid[yy][xx] for yy, xx in coords)
# Check if the string is is a MAS X
return x_string in {"AMMSS", "ASSMM", "AMSSM", "ASMMS"}


def part_b(data: str) -> int:
grid = _parse_data(data)
M, N = len(grid), len(grid[0])
return sum(_is_mas_x(grid, y, x) for y in range(M) for x in range(N))


if __name__ == "__main__":
data = """
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
""".strip()
solve_with_data(2024, 4, data, answer_a=18, answer_b=9)
25 changes: 15 additions & 10 deletions aoc_cas/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ def load_module(year: int, day: int):


def solve_with_examples(year: int, day: int) -> None:
mod = load_module(year, day)
puzzle = Puzzle(year, day)
print(f"Testing example data")

puzzle.prose0_path.unlink(missing_ok=True)
for example in puzzle.examples:
print(f"\n{example.input_data}\n")
if example.answer_a is not None:
part_a_result = str(mod.part_a(example.input_data))
correct = part_a_result == example.answer_a
print(f"{'❌✅'[correct]} [Part A] Actual: {part_a_result} - Expected: {example.answer_a}")
if example.answer_b is not None:
part_b_result = str(mod.part_b(example.input_data))
correct = part_b_result == example.answer_b
print(f"{'❌✅'[correct]} [Part B] Actual: {part_b_result} - Expected: {example.answer_b}\n")
solve_with_data(year, day, example.input_data, answer_a=example.answer_a, answer_b=example.answer_b)


def solve_with_data(year: int, day: int, input_data: str, *, answer_a: object, answer_b: object = None) -> None:
mod = load_module(year, day)
print(f"\n{input_data}\n")
if answer_a is not None:
part_a_result = str(mod.part_a(input_data))
correct = part_a_result == str(answer_a)
print(f"{'❌✅'[correct]} [Part A] Actual: {part_a_result} - Expected: {answer_a}")
if answer_b is not None:
part_b_result = str(mod.part_b(input_data))
correct = part_b_result == str(answer_b)
print(f"{'❌✅'[correct]} [Part B] Actual: {part_b_result} - Expected: {answer_b}\n")
4 changes: 4 additions & 0 deletions precommit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
set -euo pipefail
echo "Black formatting"
poetry run black . --check
12 changes: 12 additions & 0 deletions tests/fixtures/2024/4.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
18
9

0 comments on commit 8ecf902

Please sign in to comment.