From 46a84ac4766beeb70d3003d10e3653f4145ef9f6 Mon Sep 17 00:00:00 2001 From: Holly McFarland Date: Wed, 4 Dec 2024 19:34:19 -0500 Subject: [PATCH] day 4 --- day_04/part_a.py | 79 ++++++++++++++++++++++++++++++++++++++++ day_04/part_b.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 day_04/part_a.py create mode 100644 day_04/part_b.py diff --git a/day_04/part_a.py b/day_04/part_a.py new file mode 100644 index 0000000..98c51c5 --- /dev/null +++ b/day_04/part_a.py @@ -0,0 +1,79 @@ +import aocd + +from enum import Enum +import collections + +class Direction(Enum): + NORTH = (0, -1) + NORTHEAST = (1, -1) + EAST = (1, 0) + SOUTHEAST = (1, 1) + SOUTH = (0, 1) + SOUTHWEST = (-1, 1) + WEST = (-1, 0) + NORTHWEST = (-1, -1) + +class Letter: + target = 'XMAS' + + def __init__(self, char, x, y, grid): + self.char = char + + self.x = x + self.y = y + self.grid = grid + + def neighbour_coords(self, direction): + return (self.x + direction.value[0], self.y + direction.value[1]) + + def neighbour(self, direction): + return self.grid[self.neighbour_coords(direction)] + + def num_starts(self): + """Return number of times this letter is the start of the target word""" + return sum(self._is_nth_position(0, d) for d in Direction) + + def _is_nth_position(self, n, direction): + """ + Return number of times this letter is the nth letter of the target word + in the given direction + """ + # Must be the correct letter + if self.char != self.target[n]: + return False + + # Return True if it's the last letter + if n == len(self.target) - 1: + return True + + # Return False if there are no more letter in this direction + if (next_letter := self.neighbour(direction)) is None: + return False + + # Ask the next letter + return next_letter._is_nth_position(n+1, direction) + +def parse(data): + """ + Parse string into grid, return all possible first letters of XMAS + """ + grid = collections.defaultdict(lambda: None) + x_list = [] + + for y, r in enumerate(data.split('\n')): + for x, c in enumerate(r): + grid[(x, y)] = Letter(c, x, y, grid) + if c == 'X': + x_list.append(grid[(x, y)]) + + return x_list + +def solve(data): + x_list = parse(data) + return sum(x.num_starts() for x in x_list) + +if __name__ == '__main__': + solution = solve(aocd.get_data(year=2024, day=4)) + + print(solution) + aocd.submit(solution, year=2024, day=4, part='a') \ No newline at end of file diff --git a/day_04/part_b.py b/day_04/part_b.py new file mode 100644 index 0000000..1201da2 --- /dev/null +++ b/day_04/part_b.py @@ -0,0 +1,93 @@ +import aocd + +from enum import Enum, nonmember +import collections + +class Direction(Enum): + NORTH = (0, -1) + NORTHEAST = (1, -1) + EAST = (1, 0) + SOUTHEAST = (1, 1) + SOUTH = (0, 1) + SOUTHWEST = (-1, 1) + WEST = (-1, 0) + NORTHWEST = (-1, -1) + + @nonmember + def opposite(d): + return Direction((d.value[0]*-1, d.value[1]*-1)) + +class Letter: + target = 'MAS' + + def __init__(self, char, x, y, grid): + self.char = char + + self.x = x + self.y = y + self.grid = grid + + def neighbour_coords(self, direction): + return (self.x + direction.value[0], self.y + direction.value[1]) + + def neighbour(self, direction): + return self.grid[self.neighbour_coords(direction)] + + def is_center(self): + """Return whether this letter is in the center of the target pattern""" + if self.char != self.target[1]: + return False + + corners = ( + Direction.NORTHWEST, + Direction.NORTHEAST, + Direction.SOUTHWEST, + Direction.SOUTHEAST, + ) + + return sum(self.neighbour(d)._is_nth_position(0, Direction.opposite(d)) for d in corners) == 2 + + def _is_nth_position(self, n, direction): + """ + Return number of times this letter is the nth letter of the target word + in the given direction + """ + # Must be the correct letter + if self.char != self.target[n]: + return False + + # Return True if it's the last letter + if n == len(self.target) - 1: + return True + + # Return False if there are no more letter in this direction + if (next_letter := self.neighbour(direction)) is None: + return False + + # Ask the next letter + return next_letter._is_nth_position(n+1, direction) + +def parse(data): + """ + Parse string into grid, return all possible first letters of XMAS + """ + grid = collections.defaultdict(lambda: Letter(None, None, None, None)) # Null letters + a_list = [] + + for y, r in enumerate(data.split('\n')): + for x, c in enumerate(r): + grid[(x, y)] = Letter(c, x, y, grid) + if c == 'A': + a_list.append(grid[(x, y)]) + + return a_list + +def solve(data): + a_list = parse(data) + return sum(a.is_center() for a in a_list) + +if __name__ == '__main__': + solution = solve(aocd.get_data(year=2024, day=4)) + + print(solution) + aocd.submit(solution, year=2024, day=4, part='b') \ No newline at end of file