Spaces:
Running
Running
def load_data(file): | |
with open(file) as f: | |
data = f.readlines() | |
coords = [line.strip("\n").split(",") for line in data] | |
# swap i, j because this is easier for my brain at this point | |
coords = [(int(j), int(i)) for i,j in coords] | |
return coords | |
def build_grid(M, N, coords): | |
grid = [] | |
for i in range(M): | |
row = [] | |
for j in range(N): | |
c = "." if (i,j) not in coords else "#" | |
row.append(c) | |
grid.append(row) | |
return grid | |
def pprint(grid): | |
grid_str = "\n".join(["".join(l) for l in grid]) | |
print(grid_str) | |
def pprint2(grid): | |
new_grid = copy.deepcopy(grid) | |
for i in range(M): | |
for j in range(N): | |
if isinstance(grid[i][j], tuple): | |
new_grid[i][j] = "O" | |
grid_str = "\n".join(["".join(l) for l in new_grid]) | |
print(grid_str) | |
def get_neighbours(pos, grid): | |
directions = [(0,1), (1,0), (-1,0), (0, -1)] | |
M = len(grid) | |
N = len(grid[0]) | |
ns = [] | |
i, j = pos | |
for dx, dy in directions: | |
ni, nj = (i+dx, j+dy) | |
if ni in range(M) and nj in range(N): | |
if grid[ni][nj] != "#": | |
ns.append((ni, nj)) | |
return ns | |
import copy | |
def bfs(grid): | |
parents = copy.deepcopy(grid) | |
start = (0, 0) | |
q = [] | |
q.append(start) | |
visited = set() | |
count = 0 | |
while len(q) > 0: | |
# # Visualize grid filling up | |
# # So much fun! | |
# if count % 5 == 0: | |
# print() | |
# pprint2(parents) | |
# print() | |
pos = q.pop(0) | |
if pos in visited: | |
continue | |
ns = get_neighbours(pos, grid) | |
for n in ns: | |
if n not in visited: | |
q.append(n) | |
ni, nj = n | |
parents[ni][nj] = (pos) | |
visited.add(pos) | |
# print(len(visited)) | |
count += 1 | |
return parents | |
# M, N = 7, 7 | |
# n_bytes = 12 | |
# file = "test.txt" | |
M, N = 71, 71 | |
n_bytes = 1024 | |
file = "input.txt" | |
coords = load_data(file) | |
grid = build_grid(M, N, coords[:n_bytes]) | |
# Run bfs, collect parents info | |
parents = bfs(grid) | |
shortest_grid = copy.deepcopy(grid) | |
shortest_path = [] | |
next_ = (M-1,N-1) | |
while next_ != (0, 0): | |
shortest_path.append(next_) | |
i, j = next_ | |
shortest_grid[i][j] = "O" | |
next_ = parents[i][j] | |
print(len(shortest_path)) | |
# Visualize shortest path | |
# pprint(shortest_grid) | |
## Part 2 | |
def is_dead_end(coords, n_bytes, M, N): | |
grid = build_grid(M, N, coords[:n_bytes]) | |
# Run bfs, collect parents info | |
parents = bfs(grid) | |
return parents[M-1][N-1] == "." | |
def euler(coords, n_bytes): | |
"""Returns coord of first cause of dead end, use mid-point to swap out""" | |
mid = len(n_bytes) // 2 | |
left = n_bytes[:mid] | |
right = n_bytes[mid:] | |
if len(n_bytes) == 1: | |
return n_bytes[0] - 1 # Off by one because the last one left is still a dead end | |
if is_dead_end(coords, left[-1], M, N): | |
return euler(coords, left) | |
else: | |
return euler(coords, right) | |
M, N = 71, 71 | |
n_bytes = 1024 | |
file = "input.txt" | |
coords = load_data(file) | |
grid = build_grid(M, N, coords[:n_bytes]) | |
n_bytes = list(range(len(coords))) | |
max_n_bytes = euler(coords, n_bytes) | |
i, j = coords[max_n_bytes] | |
print(f"{j},{i}") # Reverse it because we read coordinates in reverse at loading time | |