def invalidate(self): """ A boulder has been moved so the traceback must be recalculated. """ # calling this function means something has caused the old traceback to become invalid self.traceback = None # see where the boulders are boulder_locations = [loc for loc, c in self.level.items() if c == '0'] # create solvers associated with boulder locations pq = [] distance_solver_location_triples = [] for boulder_location in boulder_locations: self.level[boulder_location] = '.' boulder_transition = BoulderTransition(self, self.level, self.floor_neighbors, self.location_to_back_front_pairs) boulder_heuristic = BoulderTransitionHeuristic(self, self.level, self.location_to_back_front_pairs) initial_state = (boulder_location, self.player_location) solver = AscDP.MeasureInformedTraceback([initial_state], boulder_transition, boulder_heuristic) solver.step() self.level[boulder_location] = '0' distance = solver.get_distance() if distance is not None: heappush(pq, (distance, solver, boulder_location)) # Keep going until a solver has finished or until they have all failed to find a solution. best_path = None while pq: distance, solver, boulder_location = heappop(pq) solution = solver.get_solution() if solution: best_path = AscDP.traceback_to_path(*solution) break self.level[boulder_location] = '.' solver.step() self.level[boulder_location] = '0' distance = solver.get_distance() if distance is not None: heappush(pq, (distance, solver, boulder_location)) # Set the path if one was found. if best_path: # Convert the path to boulder pushes. assert len(best_path) > 1 self.traceback = [] for (new_boulder, new_player), (old_boulder, old_player) in zip(best_path[0:-1], best_path[1:]): if new_boulder != old_boulder: self.traceback.append((new_player, old_player))