Spaces:
Running
Running
from dataclasses import dataclass | |
file = "input.txt" | |
with open(file) as f: | |
data = f.readlines() | |
grid = [] | |
for line in data: | |
line = line.strip("\n") | |
grid.append(list(line)) | |
M = len(grid) | |
N = len(grid[0]) | |
class Position: | |
i: int | |
j: int | |
direction: str | |
def rotate_90(self): | |
directions = ["^", ">", "v", "<"] | |
new_idx = (directions.index(self.direction) + 1) % len(directions) | |
self.direction = directions[new_idx] | |
def is_in_bounds(self, grid): | |
M = len(grid) | |
N = len(grid[0]) | |
return self.i in range(M) and self.j in range(N) | |
def get_next_pos(position: Position): | |
i, j, direction = position.i, position.j, position.direction | |
if direction == "^": | |
# Up | |
i -= 1 | |
elif direction == "v": | |
# Down | |
i += 1 | |
elif direction == "<": | |
# Left | |
j -= 1 | |
elif direction == ">": | |
# Right | |
j += 1 | |
return Position(i, j, direction) | |
def get_start_pos(grid): | |
M = len(grid) | |
N = len(grid[0]) | |
for i in range(M): | |
for j in range(N): | |
if grid[i][j] == "^": | |
return Position(i, j, "^") | |
def count_Xs(grid): | |
total = 0 | |
for i in range(M): | |
for j in range(N): | |
if grid[i][j] == "X": | |
total += 1 | |
return total | |
def pprint(grid, pos = None): | |
grid_copy = grid.copy() | |
if pos: | |
# Print the current position | |
grid_copy[pos.i][pos.j] = pos.direction | |
grid_str = "" | |
for line in grid_copy: | |
grid_str += "".join(line) | |
grid_str += "\n" | |
print("*"*20) | |
print() | |
print(grid_str) | |
print() | |
print("*"*20) | |
pos = get_start_pos(grid) | |
grid[pos.i][pos.j] = "X" # Mark starting point as visited | |
in_bounds = True | |
while in_bounds: | |
# pprint(grid, pos) | |
next_pos = get_next_pos(pos) | |
if next_pos.is_in_bounds(grid): | |
if grid[next_pos.i][next_pos.j] in [".", "X"]: | |
# Valid next mode, mark as visited and move | |
grid[pos.i][pos.j] = "X" | |
pos = next_pos | |
else: | |
# Otherwise, rotate | |
pos.rotate_90() | |
else: | |
# Out of bounds, game over | |
in_bounds = False | |
grid[pos.i][pos.j] = "X" | |
pos = None | |
# pprint(grid, pos) | |
print(count_Xs(grid)) | |
### Part 2 | |
prev_grid = grid.copy() | |
def load_grid(file): | |
with open(file) as f: | |
data = f.readlines() | |
grid = [] | |
for line in data: | |
line = line.strip("\n") | |
grid.append(list(line)) | |
return grid | |
def check_is_infinite(grid): | |
pos = get_start_pos(grid) | |
grid[pos.i][pos.j] = "X" # Mark starting point as visited | |
visited = set() # Keep track of positions and orientations | |
in_bounds = True | |
infinite_loop = False | |
while in_bounds and not infinite_loop: | |
# pprint(grid, pos) | |
next_pos = get_next_pos(pos) | |
if next_pos.is_in_bounds(grid): | |
if grid[next_pos.i][next_pos.j] in [".", "X"]: | |
# Valid next mode, mark as visited and move | |
grid[pos.i][pos.j] = "X" | |
pos = next_pos | |
else: | |
# Otherwise, rotate | |
pos.rotate_90() | |
set_pos = (pos.i, pos.j, pos.direction) | |
if set_pos in visited: | |
infinite_loop = True | |
else: | |
visited.add(set_pos) | |
else: | |
# Out of bounds, game over | |
in_bounds = False | |
grid[pos.i][pos.j] = "X" | |
pos = None | |
return infinite_loop | |
file = "input.txt" | |
# Load first to get stats | |
# grid = load_grid(file) | |
M = len(grid) | |
N = len(grid[0]) | |
# This is a very brute-force solution, takes long to run... | |
# For every point, place an obstacle, run the game, check if it gets stuck in infinite loop or not | |
# Surely, there must be a quicker way, but it works | |
total = 0 | |
for i in range(M): | |
for j in range(N): | |
# Reset grid | |
grid = load_grid(file) | |
start_pos = get_start_pos(grid) | |
if start_pos.i == i and start_pos.j == j: | |
# Can't set obstacle on starting point, ignore | |
continue | |
if prev_grid[i][j] != "X": | |
# It never passed by there, so we can ignore | |
continue | |
# print(i, j) | |
grid[i][j] = "O" # Set Obstacle | |
if check_is_infinite(grid): | |
total += 1 | |
print(total) | |