from queue import PriorityQueue import sys import copy class grid: def __init__(self): self.y = [] self.attempts = 0 self.solutions = 0 self.current_smallest_solution_level = None self.point_costs = {(0, 0): 0} self.next_point_queue = PriorityQueue() self.next_point_queue.put((0, (0, 0))) self.visited = {} def addline(self, line): self.y.append([int(i) for i in line]) self.maxy = len(self.y) - 1 self.maxx = len(self.y[0]) - 1 # print(self.maxx) # print(self.maxy) def largegrid(self): for f in self.y: temp = copy.copy(f) for i in range(1, 5): for t in range(len(temp)): temp[t] += 1 if temp[t] > 9: temp[t] -= 9 f.extend(temp) temp2 = copy.copy(self.y) for i in range(1, 5): temp3 = copy.copy(temp2) for f in temp3: self.y.append([(g+i-1) % 9 + 1 for g in f]) self.maxy = len(self.y) - 1 self.maxx = len(self.y[0]) - 1 # def initialpath(self): def findpath(self): while not self.next_point_queue.empty(): level, poi = self.next_point_queue.get() if self.visited.get(poi, False): continue self.visited[poi] = True self.attempts += 1 if self.attempts % 100000 == 0: print('Attempt %d, solutions: %d, cost: %d, visited: %d, queued: %d' % ( self.attempts, self.solutions, self.current_smallest_solution_level or -1, len(self.visited), self.next_point_queue.qsize())) neighbors = self.get_neighbors(poi, level) for i in neighbors: next_level = level + int(self.y[i[0]][i[1]]) if (self.current_smallest_solution_level is not None and next_level > self.current_smallest_solution_level): # We already found a solution that was overall less risky # than the current path, so who cares. continue if i == (self.maxy, self.maxx): self.solutions += 1 if (self.current_smallest_solution_level is None or next_level < self.current_smallest_solution_level): self.current_smallest_solution_level = next_level # print('Found solution: ', i, ', orig level ', level) continue elif self.visited.get(i, False) and (i in self.point_costs and self.point_costs[i] <= next_level): # We already visited the point on a cheaper path. continue else: # print('Path: %s, next: %s' % (repr(path), i)) self.point_costs[i] = next_level self.next_point_queue.put((next_level, i)) # Try to find a path from the current point to the end by going down and right # alternatingly, so we can restrict the search to paths less costly than the # current one. next_level = level while poi != (self.maxx, self.maxy): if poi[0] < self.maxx: poi = (poi[0] + 1, poi[1]) next_level += int(self.y[poi[0]][poi[1]]) if poi in self.point_costs and next_level > self.point_costs[poi]: break self.point_costs[poi] = next_level if poi[1] < self.maxy: poi = (poi[0], poi[1] + 1) next_level += int(self.y[poi[0]][poi[1]]) if poi in self.point_costs and next_level > self.point_costs[poi]: break self.point_costs[poi] = next_level if poi == (self.maxx, self.maxy): self.solutions += 1 if (self.current_smallest_solution_level is None or next_level < self.current_smallest_solution_level): self.current_smallest_solution_level = next_level def get_neighbors(self, poi, level): nex = {} rv = [] if poi[0] != self.maxy: nex.setdefault(self.y[poi[0] + 1][poi[1]], list()).append((poi[0] + 1, poi[1])) if poi[1] != self.maxx: nex.setdefault(self.y[poi[0]][poi[1] + 1], list()).append((poi[0], poi[1] + 1)) if poi[0] != 0: nex.setdefault(self.y[poi[0] - 1][poi[1]], list()).append((poi[0] - 1, poi[1])) if poi[1] != 0: nex.setdefault(self.y[poi[0]][poi[1] - 1], list()).append((poi[0], poi[1] - 1)) order = sorted(nex.keys()) for j in order: for p in nex[j]: if (p not in self.visited and (p not in self.point_costs or self.point_costs[p] >= level + j)): rv.append(p) return rv gri = grid() with open(sys.argv[1], 'r') as f: for i in f.readlines(): gri.addline(i.strip()) gri.largegrid() #for f in gri.y: # print("".join(map(str, f))) gri.findpath() print(gri.current_smallest_solution_level)