From 1225a007ade6ca3131ea8af76c84eeda4355ae5c Mon Sep 17 00:00:00 2001 From: Holly McFarland Date: Wed, 11 Dec 2024 16:57:34 -0500 Subject: [PATCH] day 6 --- day_06/part_a.py | 58 +++++++++++++++++++++++++++++++++++++ day_06/part_b.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 day_06/part_a.py create mode 100644 day_06/part_b.py diff --git a/day_06/part_a.py b/day_06/part_a.py new file mode 100644 index 0000000..ad2977f --- /dev/null +++ b/day_06/part_a.py @@ -0,0 +1,58 @@ +import aocd +from enum import Enum, nonmember + +class Direction(Enum): + NORTH = (0, -1) + WEST = (-1, 0) + SOUTH = (0, 1) + EAST = (1, 0) + + @nonmember + def clockwise_from(d): + return list(Direction)[list(Direction).index(d) - 1] + +def facing(coords, direction): + return tuple(map(sum, zip(coords, direction.value))) + +def parse(data): + """ + Given maze data, return + (Tuple of guard coords, set of obstacle coords, grid width, grid height) + """ + guard = None + obstacles = set() + + for y, row in enumerate(data.split('\n')): + for x, char in enumerate(row): + if char == '#': + obstacles.add((x, y)) + elif char == '^': + guard = (x, y) + + return (guard, obstacles, x, y) + +def solve(data): + guard, obstacles, width, height = parse(data) + guard_direction = Direction.NORTH + + visited = set() + + while (0 <= guard[0] <= width) and (0 <= guard[1] <= height): + visited.add(guard) + while (facing_tile := facing(guard, guard_direction)) in obstacles: + guard_direction = Direction.clockwise_from(guard_direction) + guard = facing_tile + + return sum(1 for _ in visited) + + +if __name__ == '__main__': + puz = aocd.models.Puzzle(year=2024, day=6) + + # data = puz.examples[0].input_data + data = puz.input_data + + solution = solve(data) + + print(solution) + aocd.submit(solution, year=2024, day=6, part='a') \ No newline at end of file diff --git a/day_06/part_b.py b/day_06/part_b.py new file mode 100644 index 0000000..b981604 --- /dev/null +++ b/day_06/part_b.py @@ -0,0 +1,75 @@ +import aocd +import collections +from enum import Enum, nonmember + +class Direction(Enum): + NORTH = (0, -1) + WEST = (-1, 0) + SOUTH = (0, 1) + EAST = (1, 0) + + @nonmember + def clockwise_from(d): + return list(Direction)[list(Direction).index(d) - 1] + +def facing(coords, direction): + return tuple(map(sum, zip(coords, direction.value))) + +def parse(data): + """ + Given maze data, return + (Tuple of guard coords, set of obstacle coords, grid width, grid height) + """ + guard = None + obstacles = set() + + for y, row in enumerate(data.split('\n')): + for x, char in enumerate(row): + if char == '#': + obstacles.add((x, y)) + elif char == '^': + guard = (x, y) + + return (guard, obstacles, x, y) + +def simulate(guard, obstacles, width, height, return_visited_tiles=False): + guard_direction = Direction.NORTH + visited = collections.defaultdict(set) + + while (0 <= guard[0] <= width) and (0 <= guard[1] <= height): + visited[guard].add(guard_direction) + while (facing_tile := facing(guard, guard_direction)) in obstacles: + guard_direction = Direction.clockwise_from(guard_direction) + + # Loop detection, can only be true in modified grids + if guard_direction in visited[facing_tile]: + return True + + guard = facing_tile + + # No loop found + + if return_visited_tiles: + return set(k for k,v in visited.items() if v) + + return False + + +def solve(data): + guard, obstacles, width, height = parse(data) + + # Only bother checking positions that the guard crosses + potential_positions = simulate(guard, obstacles, width, height, return_visited_tiles=True) + + return sum(simulate(guard, obstacles | {p}, width, height) for p in potential_positions) + +if __name__ == '__main__': + puz = aocd.models.Puzzle(year=2024, day=6) + + # data = puz.examples[0].input_data + data = puz.input_data + + solution = solve(data) + + print(solution) + aocd.submit(solution, year=2024, day=6, part='b') \ No newline at end of file