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: pass @property def _part2(self) -> None: pass @staticmethod def _to_grid(input: str) -> List[List[str]]: return [list(line) for line in input.splitlines() if line] def _find_neighbours( self, origin: Tuple[int, int], bounds: Tuple[Tuple[int, int], ...] = ((-1, 1), (-1, 1)), locations: Tuple[Tuple[int, ...]] = (), target: str = "XMAS", ) -> Tuple[int, ...]: if len(target) <= 1: return locations current = self.inputgrid[origin[0]][origin[1]] next = target[target.find(current) + 1] for row in range(origin[0] + bounds[0][0], origin[0] + bounds[0][1] + 1): for col in range(origin[1] - bounds[1][0], origin[1] + bounds[1][1] + 1): if any( ( row < 0, row >= len(self.inputgrid[0]), col < 0, col >= len(self.inputgrid), ) ): continue if self.inputgrid[row][col].upper() == next.upper(): return self._find_neighbours( origin=(row, col), bounds=( (-1 if row < origin[0] else 0, 1 if row > origin[0] else 0), (-1 if col < origin[1] else 0, 1 if col > origin[1] else 0), ), locations=( locations, (row, col), ), target=target[1:], ) def locate_xmas(self) -> List[Tuple[int, int, int, int]]: location: List[Tuple[int, ...]] = [] for i, line in enumerate(self.inputgrid): for j, char in enumerate(line): if char.upper() == "X": location.append(self._find_neighbours((i, j), target="XMAS")) return location if __name__ == "__main__": example = FindXMAS(EXAMPLE) print(example._find_neighbours((0, 4))) print(example.locate_xmas()) # print(example) # assert example._part1 == 18