Spaces:
Running
Running
| from copy import deepcopy | |
| def load_data(file): | |
| with open(file) as f: | |
| raw_data = f.readlines() | |
| grid = [] | |
| for line in raw_data: | |
| line = line.strip("\n") | |
| grid.append(list(line)) | |
| return grid | |
| def get_end_pos(grid): | |
| M = len(grid) | |
| N = len(grid[0]) | |
| for i in range(M): | |
| for j in range(N): | |
| if grid[i][j] == "E": | |
| return (i,j) | |
| class UnvisitedSet: | |
| # There must definitely better data structures for this... | |
| def __init__(self, grid): | |
| self.grid = grid | |
| self.visited = deepcopy(grid) | |
| self.values = [] # Store here the (min_value, pos, direction) where pos = (i,j) is position in grid | |
| M = len(grid) | |
| N = len(grid[0]) | |
| for i in range(M): | |
| for j in range(N): | |
| self.visited[i][j] = False | |
| pos = (i, j) | |
| if grid[i][j] == "S": | |
| self.values.append([0, pos, ">"]) | |
| else: | |
| self.values.append([float("inf"), pos, ""]) | |
| def update_value(self, new_pos, new_val, new_dir): | |
| for idx, (val, pos, d) in enumerate(self.values): | |
| if new_pos == pos and new_val < val: | |
| self.values[idx] = [new_val, new_pos, new_dir] | |
| break | |
| self.sort_values() | |
| def get_value(self, pos): | |
| for (v, p, d) in self.values: | |
| if pos == p: | |
| return v | |
| def get_direction(self, pos): | |
| for (v, p, d) in self.values: | |
| if pos == p: | |
| return d | |
| def mark_visited(self, pos): | |
| i, j = pos | |
| self.visited[i][j] = True | |
| def sort_values(self): | |
| self.values.sort(key=lambda x: x[0]) | |
| def get_min_unvisited(self): | |
| self.sort_values() | |
| for val, pos, d in self.values: | |
| i,j = pos | |
| if not self.visited[i][j] and val < float("inf"): | |
| return val, pos, d | |
| return "empty", "empty", "empty" | |
| def values_to_grid(self): | |
| new_grid = deepcopy(self.grid) | |
| for value, pos, _ in self.values: | |
| i, j = pos | |
| if value < float("inf"): | |
| new_grid[i][j] = "O" | |
| return new_grid | |
| def pprint(grid): | |
| grid_str = "\n".join(["".join(str(l)) for l in grid]) | |
| print(grid_str) | |
| def pprint2(grid): | |
| grid_str = "\n".join(["".join(l) for l in 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 | |
| def get_cost(pos, next_pos, direction): | |
| # Only valid if we are moving at most 1 neighbor away | |
| i, j = pos | |
| ni, nj = next_pos | |
| if (ni - i) > 0: | |
| intended_direction = "^" | |
| elif (ni - i) < 0: | |
| intended_direction = "v" | |
| elif (nj - j) > 0: | |
| intended_direction = ">" | |
| elif (nj - j) < 0: | |
| intended_direction = "<" | |
| else: | |
| raise ValueError("Debug?") | |
| if direction == intended_direction: | |
| cost = 1 | |
| elif direction == "^" and intended_direction == "v": | |
| cost = 2001 | |
| elif direction == "v" and intended_direction == "^": | |
| cost = 2001 | |
| elif direction == ">" and intended_direction == "<": | |
| cost = 2001 | |
| elif direction == "<" and intended_direction == ">": | |
| cost = 2001 | |
| else: | |
| # Every other case involves a 90deg rotation | |
| cost = 1001 | |
| return cost, intended_direction | |
| file = "input.txt" | |
| # file = "test2.txt" | |
| grid = load_data(file) | |
| def djikstra(grid): | |
| end_pos = get_end_pos(grid) | |
| unvisited_set = UnvisitedSet(grid) | |
| ei, ej = end_pos | |
| while not unvisited_set.visited[ei][ej]: | |
| val, pos, d = unvisited_set.get_min_unvisited() | |
| ns = get_neighbours(pos, grid) | |
| for next_pos in ns: | |
| cost, next_dir = get_cost(pos, next_pos, d) | |
| unvisited_set.update_value(next_pos, val+cost, next_dir) | |
| unvisited_set.mark_visited(pos) | |
| return unvisited_set | |
| unvisited_set = djikstra(grid) | |
| end_pos = get_end_pos(grid) | |
| print(unvisited_set.get_value(end_pos)) | |
| # ## Part 2 | |
| # # def djikstra(grid): | |
| # # end_pos = get_end_pos(grid) | |
| # # unvisited_set = UnvisitedSet(grid) | |
| # # | |
| # # ei, ej = end_pos | |
| # # while not unvisited_set.visited[ei][ej]: | |
| # # | |
| # # val, pos, d = unvisited_set.get_min_unvisited() | |
| # # i, j = pos | |
| # # if unvisited_set.visited[i][j]: | |
| # # continue | |
| # # | |
| # # ns = get_neighbours(pos, grid) | |
| # # | |
| # # | |
| # # | |
| # # for next_pos in ns: | |
| # # cost, next_dir = get_cost(pos, next_pos, d) | |
| # # unvisited_set.update_value(next_pos, val+cost, next_dir) | |
| # # | |
| # # unvisited_set.mark_visited(pos) | |
| # # | |
| # # return unvisited_set | |
| # # file = "test.txt" | |
| # # file = "input.txt" | |
| # file = "test2.txt" | |
| # # file = "input.txt" | |
| # grid = load_data(file) | |
| # end_pos = get_end_pos(grid) | |
| # unvisited_set = djikstra(grid) | |
| # end_pos = get_end_pos(grid) | |
| # def draw_grid_from_paths(grid, min_paths): | |
| # grid_copy = deepcopy(grid) | |
| # for pos in min_paths: | |
| # i,j = pos | |
| # grid_copy[i][j] = "O" | |
| # # pprint2(grid_copy) | |
| # return grid_copy | |
| # # Here we will get all points in the minimum path backtracking back to the start point | |
| # min_paths = [end_pos] | |
| # # for direction in [">", "^"]: | |
| # # direction = ">" | |
| # queue = [(end_pos, "v"), (end_pos, "<")] | |
| # visited = set() | |
| # unvisited_set.update_value((1, 14), float("inf"), new_dir = "v") | |
| # while len(queue) > 0: | |
| # pos, direction = queue.pop(0) | |
| # if pos in visited: | |
| # continue | |
| # val = unvisited_set.get_value(pos) | |
| # end_val = unvisited_set.get_value(end_pos) | |
| # # direction = unvisited_set.get_direction(pos) | |
| # ns = get_neighbours(pos, grid) | |
| # n_vals_pos_dirs = [] | |
| # for n_pos in ns: | |
| # n_val = unvisited_set.get_value(n_pos) | |
| # n_dir = unvisited_set.get_direction(n_pos) | |
| # n_vals_pos_dirs.append((n_val, n_pos, n_dir)) | |
| # # print(pos) | |
| # # print(val) | |
| # # print(n_vals_pos_dirs) | |
| # # print() | |
| # # print() | |
| # # Work backwards and subtract cost | |
| # pprint2(draw_grid_from_paths(grid, min_paths)) | |
| # for n_val, n_pos, _ in n_vals_pos_dirs: | |
| # if n_pos in visited: | |
| # continue | |
| # # if pos == end_pos: | |
| # # cost = 1 | |
| # # else: | |
| # # cost, n_dir = get_cost(pos, n_pos, direction) | |
| # cost, n_dir = get_cost(n_pos, pos, direction) | |
| # print(pos, n_pos, cost, direction, n_dir) | |
| # import ipdb; ipdb.set_trace(); | |
| # if n_val > end_val: | |
| # continue | |
| # # if n_val - (val - cost) >= 0: | |
| # if n_val <= (val - cost): | |
| # min_paths.append(n_pos) | |
| # pprint2(draw_grid_from_paths(grid, min_paths)) | |
| # if n_pos not in visited: | |
| # queue.append((n_pos, n_dir)) | |
| # visited.add(pos) | |
| # # draw_grid_from_paths(grid, min_paths) | |
| # # print() | |
| # # | |
| # # | |
| # # while len(queue) > 0: | |
| # # pos = queue.pop(0) | |
| # # ns = get_neighbours(pos, grid) | |
| # # | |
| # # if pos in visited: | |
| # # continue | |
| # # | |
| # # n_vals_pos = [] | |
| # # for n_pos in ns: | |
| # # n_val = unvisited_set.get_value(n_pos) | |
| # # n_vals_pos.append((n_val, n_pos)) | |
| # # | |
| # # n_vals_not_inf = sum([val < float("inf") for val, pos in n_vals_pos]) | |
| # # if n_vals_not_inf > 0: | |
| # # for n in ns: | |
| # # queue.append(n) | |
| # # min_paths.append(pos) | |
| # # else: | |
| # # print("Here") | |
| # # | |
| # # visited.add(pos) | |
| # print(len(set(min_paths))) | |
| # # import numpy as np | |
| # # import matplotlib.pyplot as plt | |
| # # | |
| # # grid_copy = deepcopy(grid) | |
| # # M = len(grid_copy) | |
| # # N = len(grid_copy[0]) | |
| # # for value in unvisited_set.values: | |
| # # val, pos, dir = value | |
| # # i, j = pos | |
| # # grid_copy[i][j] = val if val != float("inf") else grid[i][j] | |
| # # for i in range(M): | |
| # # for j in range(N): | |
| # # if grid_copy[i][j] == "#": | |
| # # grid_copy[i][j] = -1 | |
| # # if grid_copy[i][j] == ".": | |
| # # grid_copy[i][j] = 0 | |
| # # arr = np.array(grid_copy) | |
| # # plt.figure() | |
| # # | |
| # # plt.imshow(arr) | |