2024-12-06 - Guard Gallivant
(original .ipynb)
Day 6 puzzle input is a little 2D of empty space, obstacle and a guard (".", "#" and "^" respectively), mine is here). Part 1 involves following a set of rules governing the guard's movement and identifying the number of distinct places he visits before leaving the area. Part 2 involves finding the number of places you can add an obstacle to so that the guard ends up in a loop.
puzzle_input_str = open("./puzzle_input/day6.txt").read()
test_input_str = """....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#..."""
directions = (
(-1, 0),
(0, 1),
(1, 0),
(0, -1)
)
def parse_input(input_str):
grid = []
start_r, start_c = None, None
for r, row in enumerate(input_str.splitlines()):
maybe_start_c = row.find("^")
if maybe_start_c > 0:
start_r = r
start_c = maybe_start_c
grid.append([c for c in row])
return (start_r, start_c, grid)
def out_of_bounds(grid, r, c):
if r < 0 or c < 0:
return True
if r >= len(grid) or c >= len(grid[0]):
return True
return False
def is_guard(grid, r, c):
return grid[r][c] == "#"
def try_move(grid, r, c, direction):
dr, dc = directions[direction]
next_r, next_c = r+dr, c+dc
if out_of_bounds(grid, next_r, next_c):
return None
while is_guard(grid, next_r, next_c):
direction = (direction + 1) % len(directions)
dr, dc = directions[direction]
next_r, next_c = r+dr, c+dc
return (next_r, next_c, direction)
def part_one(input_str):
r, c, grid = parse_input(input_str)
direction = 0
positions = {(r,c)}
while True:
next_move = try_move(grid, r, c, direction)
if not next_move:
break
r, c, direction = next_move
positions.add(
(r,c)
)
return len(positions)
assert 41 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 5305
from copy import deepcopy
def add_obstruction(grid, obstruction):
new_grid = deepcopy(grid)
r, c = obstruction
new_grid[r][c] = "#"
return new_grid
def part_two(input_str):
start_r, start_c, grid = parse_input(input_str)
count_positions = 0
potential_obstructions = {(start_r, start_c)}
r, c, direction = start_r, start_c, 0
while True:
next_move = try_move(grid, r, c, direction)
if not next_move:
break
r, c, direction = next_move
potential_obstructions.add((r,c))
while potential_obstructions:
visited = {(start_r, start_c, 0)}
obstruction = potential_obstructions.pop()
new_grid = add_obstruction(grid, obstruction)
r, c, direction = start_r, start_c, 0
while True:
next_move = try_move(new_grid, r, c, direction)
if not next_move:
break
if next_move in visited:
count_positions += 1
break
r, c, direction = next_move
visited.add(next_move)
return count_positions
assert 6 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
part two: 2143