advent-of-code/2024/04/main.py
2024-12-08 21:36:04 +01:00

131 lines
3.2 KiB
Python

from typing import List, Union, Tuple
Point = Tuple[int]
Path = Tuple[Point]
Paths = [Path]
ALL_DIRECTIONS = (
(1, 1),
(1, 0),
(1, -1),
(0, 1),
(0, -1),
(-1, 1),
(-1, 0),
(-1, -1),
)
LETTER_MAP = {"X": 0, "M": 1, "A": 2, "S": 3}
class Board:
rows: List[List[int]]
def __init__(self, rows: List[List[int]]):
self.rows = rows
self.max_y = len(self.rows)
self.max_x = len(self.rows[0])
def get(self, x: int, y: int) -> Union[None, int]:
if x < 0 or y < 0:
return None
if x >= self.max_x or y >= self.max_y:
return None
return self.rows[y][x]
# def get_neighbours(self, x: int, y: int, target: int) -> List[Point]:
# n = []
# for dx in range(x - 1, x + 2):
# for dy in range(y - 1, y + 2):
# if self.get(dx, dy) == target:
# n.append((dx, dy))
# return n
@staticmethod
def parse(raw: str):
rows = [[LETTER_MAP[letter] for letter in line] for line in raw.splitlines()]
return Board(rows)
# def find_maze(b: Board, path: Path, next_char: int) -> Paths:
# x, y = path[-1]
# print(f"Find {x} {y} n={next_char}")
# paths: Paths = []
# neighbours = b.get_neighbours(x, y, next_char)
# # Found last letter
# if next_char == 3:
# return [path + (n,) for n in neighbours]
# # Recurse
# for n in neighbours:
# p = path + (n,)
# sub_paths = find(b, p, next_char + 1)
# if len(sub_paths):
# paths = paths + sub_paths
# return paths
def find_line(b: Board, point: Point, direction: Point) -> Union[Path, None]:
x, y = point
dx, dy = direction
path: Path = point
for i in range(1, 4):
px = x + (dx * i)
py = y + (dy * i)
if b.get(px, py) != i:
return None
path = path + ((x, y),)
return path
def find_x(b: Board, point: Point) -> bool:
x, y = point
for dx, dy in ALL_DIRECTIONS:
# Check if is "M", then the opposite for "S" and the the perpendicular
if b.get(x + dx, y + dy) == 1:
if b.get(x - dx, y - dy) == 3:
# Found first "MAS", check perpendicular
p_dx = -dy
p_dy = dx
p_a = b.get(x + p_dx, y + p_dy)
p_b = b.get(x - p_dx, y - p_dy)
if (p_a == 1 and p_b == 3) or (p_a == 3 and p_b == 1):
return True
return False
def solve(raw: str) -> int:
# Part 1
part1 = []
part2 = 0
b = Board.parse(raw)
for x in range(b.max_x):
for y in range(b.max_y):
p = b.get(x, y)
if p == 0:
for direction in ALL_DIRECTIONS:
found = find_line(b, (x, y), direction)
if found:
part1.append(found)
if p == 2:
if find_x(b, (x, y)):
part2 += 1
return (len(part1), part2)
# Test
with open("./2024/04/test.txt", "r") as f:
result = solve(f.read().strip())
print(result)
# Input
with open("./2024/04/input.txt", "r") as f:
result = solve(f.read().strip())
print(result)