def search(self, problem, frontier): """ Search for a solution saving new expanded nodes to a queue. :param problem: problem to solve :param frontier (Datastructures.Queue): that implements certain discipline :return: If solution was found return a list of actions to reach goal state from an initial state. If initial state is a goal state this method returns a list with a NoOpAction. If failed to find a solution it returns an empty list. """ self._frontier = frontier self.clear_instrumentation() root = Node(problem.get_initial_state()) # check root node before adding to a queue if self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, root): self._set_path_cost(root.get_path_cost()) return utils.actions_from_nodes(root.get_path_from_root()) self._add_to_frontier(root) self._set_new_queue_size() # while frontier isn't empty there are still states to check while not self._frontier.is_empty(): node_to_expand = self._pop_node_from_frontier() self._set_new_queue_size() # if state shouldn't be checked before adding it's time to check it now if not self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, node_to_expand): self._set_path_cost(node_to_expand.get_path_cost()) return utils.actions_from_nodes( node_to_expand.get_path_from_root()) # Get new nodes, check them and add to a frontier for fn in self.get_resulting_nodes_to_add_to_frontier( node_to_expand, problem): if self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, fn): self._set_path_cost(fn.get_path_cost()) return utils.actions_from_nodes( fn.get_path_from_root()) self._add_to_frontier(fn) self._set_new_queue_size() # if we are here, all frontier is empty and a goal state wasn't found. Search failed return self._failure()
def _recursive_dls(self, curNode, problem, limit): # if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) if utils.is_goal_state(problem, curNode): self.set_path_cost(curNode.get_path_cost()) return utils.actions_from_nodes(curNode.get_path_from_root()) elif limit == 0: # else if limit = 0 then return cutoff return self._cutoff() else: # else # cutoff_occurred? <- false childNodes = self.expand_node(curNode, problem) cutoff_occurred = False # for each action in problem.ACTIONS(node.STATE) do for node in childNodes: # child <- CHILD-NODE(problem, node, action) # result <- RECURSIVE-DLS(child, problem, limit - 1) result = self._recursive_dls(node, problem, limit - 1) # if result = cutoff then cutoff_occurred? <- true if self.is_cutoff(result): cutoff_occurred = True elif not self.is_failure(result): # else if result != failure then return result return result # if cutoff_occurred? then return cutoff else return failure if cutoff_occurred: return self._cutoff() else: return self._failure()
def search(self, problem, frontier): """ Search for a solution saving new expanded nodes to a queue. :param problem: problem to solve :param frontier (Datastructures.Queue): that implements certain discipline :return: If solution was found return a list of actions to reach goal state from an initial state. If initial state is a goal state this method returns a list with a NoOpAction. If failed to find a solution it returns an empty list. """ self._frontier = frontier self.clear_instrumentation() root = Node(problem.get_initial_state()) # check root node before adding to a queue if self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, root): self._set_path_cost(root.get_path_cost()) return utils.actions_from_nodes(root.get_path_from_root()) self._add_to_frontier(root) self._set_new_queue_size() # while frontier isn't empty there are still states to check while not self._frontier.is_empty(): node_to_expand = self._pop_node_from_frontier() self._set_new_queue_size() # if state shouldn't be checked before adding it's time to check it now if not self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, node_to_expand): self._set_path_cost(node_to_expand.get_path_cost()) return utils.actions_from_nodes(node_to_expand.get_path_from_root()) # Get new nodes, check them and add to a frontier for fn in self.get_resulting_nodes_to_add_to_frontier(node_to_expand, problem): if self._check_goal_before_adding_to_frontier: if utils.is_goal_state(problem, fn): self._set_path_cost(fn.get_path_cost()) return utils.actions_from_nodes(fn.get_path_from_root()) self._add_to_frontier(fn) self._set_new_queue_size() # if we are here, all frontier is empty and a goal state wasn't found. Search failed return self._failure()
def _rbfs(self, problem, node, node_f, f_limit, recursive_depth): self._set_max_recursive_depth(recursive_depth) # if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) if utils.is_goal_state(problem, node): return SearchResult(node, f_limit) # successors <- [] # for each action in problem.ACTION(node.STATE) do # add CHILD-NODE(problem, node, action) into successors successors = self.expand_node(node, problem) # if successors is empty then return failure, infinity if len(successors) == 0: return SearchResult(None, PlusInfinity()) # for each s in successors do # update f with value from previous search, if any f = [ max(self._evaluation_function.f(node), node_f) for node in successors ] # repeat while True: # best <- the lowest f-value node in successors best_index = self._get_best_f_value_index(f) # if best.f > f_limit then return failure, best.f if f[best_index] > f_limit: return SearchResult(None, f[best_index]) # if best.f > f_limit then return failure, best.f alt_index = self._get_next_best_f_value_index(f, best_index) # result, best.f <- RBFS(problem, best, min(f_limit, alternative)) sr = self._rbfs(problem, successors[best_index], f[best_index], min(f_limit, f[alt_index]), recursive_depth + 1) f[best_index] = sr.get_f_cost_limit() # if result != failure then return result if sr.found_solution(): return sr
def _rbfs(self, problem, node, node_f, f_limit, recursive_depth): self._set_max_recursive_depth(recursive_depth) # if problem.GOAL-TEST(node.STATE) then return SOLUTION(node) if utils.is_goal_state(problem, node): return SearchResult(node, f_limit) # successors <- [] # for each action in problem.ACTION(node.STATE) do # add CHILD-NODE(problem, node, action) into successors successors = self.expand_node(node ,problem) # if successors is empty then return failure, infinity if len(successors) == 0: return SearchResult(None, PlusInfinity()) # for each s in successors do # update f with value from previous search, if any f = [max(self._evaluation_function.f(node), node_f) for node in successors] # repeat while True: # best <- the lowest f-value node in successors best_index = self._get_best_f_value_index(f) # if best.f > f_limit then return failure, best.f if f[best_index] > f_limit: return SearchResult(None, f[best_index]) # if best.f > f_limit then return failure, best.f alt_index = self._get_next_best_f_value_index(f, best_index) # result, best.f <- RBFS(problem, best, min(f_limit, alternative)) sr = self._rbfs(problem, successors[best_index], f[best_index], min(f_limit, f[alt_index]), recursive_depth + 1) f[best_index] = sr.get_f_cost_limit() # if result != failure then return result if sr.found_solution(): return sr