2024-12-16 07:54:23 +00:00
|
|
|
from typing import List, Tuple
|
|
|
|
|
|
|
|
EXAMPLE = """
|
|
|
|
MMMSXXMASM
|
|
|
|
MSAMXMSMSA
|
|
|
|
AMXSXMAAMM
|
|
|
|
MSAMASMSMX
|
|
|
|
XMASAMXAMM
|
|
|
|
XXAMMXXAMA
|
|
|
|
SMSMSASXSS
|
|
|
|
SAXAMASAAA
|
|
|
|
MAMMMXMMMM
|
|
|
|
MXMXAXMASX
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
class FindXMAS:
|
|
|
|
def __init__(self, input: str) -> None:
|
|
|
|
self.input = input
|
|
|
|
self.inputgrid = self._to_grid(self.input)
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
return f"Part1: {self._part1}\nPart2: {self._part2}"
|
|
|
|
|
|
|
|
@property
|
|
|
|
def _part1(self) -> int:
|
2024-12-22 13:59:30 +00:00
|
|
|
return len(self.locate_xmas())
|
2024-12-16 07:54:23 +00:00
|
|
|
|
|
|
|
@property
|
2024-12-22 15:47:46 +00:00
|
|
|
def _part2(self) -> int:
|
2024-12-24 09:20:59 +00:00
|
|
|
return len(self.locate_x_mas()) // 2
|
2024-12-16 07:54:23 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def _to_grid(input: str) -> List[List[str]]:
|
|
|
|
return [list(line) for line in input.splitlines() if line]
|
|
|
|
|
2024-12-22 13:59:30 +00:00
|
|
|
def find_neighbours(
|
2024-12-17 07:02:32 +00:00
|
|
|
self,
|
|
|
|
origin: Tuple[int, int],
|
|
|
|
target: str = "XMAS",
|
2024-12-22 13:59:30 +00:00
|
|
|
) -> List[Tuple[Tuple[int, ...]]]:
|
|
|
|
locations: List[Tuple[Tuple[int, ...]]] = []
|
|
|
|
x, y = origin
|
|
|
|
if self.inputgrid[x][y].upper() != target[0].upper():
|
|
|
|
return []
|
|
|
|
for dx in range(-1, 2):
|
|
|
|
for dy in range(-1, 2):
|
|
|
|
if dx == 0 and dy == 0:
|
2024-12-17 07:02:32 +00:00
|
|
|
continue
|
2024-12-22 13:59:30 +00:00
|
|
|
current = [(x, y)]
|
|
|
|
for n in range(1, len(target)):
|
|
|
|
nx = x + (n * dx)
|
|
|
|
ny = y + (n * dy)
|
|
|
|
if nx < 0 or ny < 0:
|
|
|
|
continue
|
|
|
|
try:
|
|
|
|
if self.inputgrid[nx][ny].upper() == target[n].upper():
|
|
|
|
current.append((nx, ny))
|
|
|
|
except IndexError:
|
|
|
|
pass
|
2024-12-22 15:47:46 +00:00
|
|
|
if len(current) == len(target):
|
2024-12-22 13:59:30 +00:00
|
|
|
locations.append(tuple(current))
|
|
|
|
return locations
|
2024-12-17 07:02:32 +00:00
|
|
|
|
2024-12-22 13:59:30 +00:00
|
|
|
def locate_xmas(self) -> List[Tuple[Tuple[int, ...]]]:
|
|
|
|
locations: List[Tuple[Tuple[int, ...]]] = []
|
2024-12-17 07:02:32 +00:00
|
|
|
|
2024-12-16 07:54:23 +00:00
|
|
|
for i, line in enumerate(self.inputgrid):
|
|
|
|
for j, char in enumerate(line):
|
|
|
|
if char.upper() == "X":
|
2024-12-22 13:59:30 +00:00
|
|
|
neighbours = self.find_neighbours((i, j), target="XMAS")
|
|
|
|
if neighbours is None:
|
|
|
|
continue
|
|
|
|
locations = [*locations, *neighbours]
|
2024-12-16 07:54:23 +00:00
|
|
|
|
2024-12-22 13:59:30 +00:00
|
|
|
return locations
|
2024-12-16 07:54:23 +00:00
|
|
|
|
2024-12-22 15:47:46 +00:00
|
|
|
def locate_x_mas(self) -> List[Tuple[Tuple[int, ...]]]:
|
|
|
|
mas: List[Tuple[Tuple[int, ...]]] = []
|
|
|
|
|
|
|
|
for i, line in enumerate(self.inputgrid):
|
|
|
|
for j, char in enumerate(line):
|
|
|
|
if char.upper() == "M":
|
|
|
|
neighbours = [
|
|
|
|
val
|
|
|
|
for val in self.find_neighbours((i, j), target="MAS")
|
|
|
|
if all([len(set(part)) == len("MAS") for part in zip(*val)])
|
|
|
|
]
|
|
|
|
if neighbours is None:
|
|
|
|
continue
|
|
|
|
mas = [*mas, *neighbours]
|
|
|
|
|
|
|
|
locations: List[Tuple[Tuple[int, ...]]] = []
|
|
|
|
# breakpoint()
|
|
|
|
for loc in mas:
|
|
|
|
oth = [val[1] for val in mas if val != loc and val[::-1] != loc]
|
|
|
|
# breakpoint()
|
|
|
|
if loc[1] in oth:
|
|
|
|
locations.append(loc)
|
|
|
|
|
2024-12-24 09:20:59 +00:00
|
|
|
return locations
|
2024-12-22 15:47:46 +00:00
|
|
|
|
2024-12-16 07:54:23 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
example = FindXMAS(EXAMPLE)
|
2024-12-22 13:59:30 +00:00
|
|
|
print(example)
|
|
|
|
assert example._part1 == 18
|
2024-12-24 09:20:59 +00:00
|
|
|
assert example._part2 == 9
|
2024-12-22 13:59:30 +00:00
|
|
|
with open("input.txt", "r") as f:
|
|
|
|
puzzle = FindXMAS(f.read())
|
|
|
|
print(puzzle)
|