# search.py # --------- # Licensing Information: You are free to use or extend these projects for # educational purposes provided that (1) you do not distribute or publish # solutions, (2) you retain this notice, and (3) you provide clear # attribution to UC Berkeley, including a link to http://ai.berkeley.edu. # # Attribution Information: The Pacman AI projects were developed at UC Berkeley. # The core projects and autograders were primarily created by John DeNero # (denero@cs.berkeley.edu) and Dan Klein (klein@cs.berkeley.edu). # Student side autograding was added by Brad Miller, Nick Hay, and # Pieter Abbeel (pabbeel@cs.berkeley.edu). """ In search.py, you will implement generic search algorithms which are called by Pacman agents (in searchAgents.py). """ import util class SearchProblem: """ This class outlines the structure of a search problem, but doesn't implement any of the methods (in object-oriented terminology: an abstract class). You do not need to change anything in this class, ever. """ def getStartState(self): """ Returns the start state for the search problem. """ util.raiseNotDefined() def isGoalState(self, state): """ state: Search state Returns True if and only if the state is a valid goal state. """ util.raiseNotDefined() def getSuccessors(self, state): """ state: Search state For a given state, this should return a list of triples, (successor, action, stepCost), where 'successor' is a successor to the current state, 'action' is the action required to get there, and 'stepCost' is the incremental cost of expanding to that successor. """ util.raiseNotDefined() def getCostOfActions(self, actions): """ actions: A list of actions to take This method returns the total cost of a particular sequence of actions. The sequence must be composed of legal moves. """ util.raiseNotDefined() def tinyMazeSearch(problem): """ Returns a sequence of moves that solves tinyMaze. For any other maze, the sequence of moves will be incorrect, so only use this for tinyMaze. """ from game import Directions s = Directions.SOUTH w = Directions.WEST return [s, s, w, s, w, w, s, w] def depthFirstSearch(problem: SearchProblem): """ Search the deepest nodes in the search tree first. Your search algorithm needs to return a list of actions that reaches the goal. Make sure to implement a graph search algorithm. To get started, you might want to try some of these simple commands to understand the search problem that is being passed in: print("Start:", problem.getStartState()) print("Is the start a goal?", problem.isGoalState(problem.getStartState())) print("Start's successors:", problem.getSuccessors(problem.getStartState())) """ # 初始化栈用于深度优先搜索,存储(状态, 路径)元组 # 使用栈实现LIFO(后进先出)的搜索策略 fringe = util.Stack() # 记录已访问的状态,避免重复搜索(图搜索) visited = set() # 获取起始状态并加入栈中,初始路径为空 startState = problem.getStartState() fringe.push((startState, [])) # 当栈不为空时继续搜索 while not fringe.isEmpty(): # 弹出栈顶元素(当前状态和到达该状态的路径) currentState, actions = fringe.pop() # 如果当前状态已经访问过,跳过 if currentState in visited: continue # 标记当前状态为已访问 visited.add(currentState) # 检查是否到达目标状态 if problem.isGoalState(currentState): return actions # 获取当前状态的所有后继状态 successors = problem.getSuccessors(currentState) # 将所有未访问的后继状态加入栈中 for successor, action, cost in successors: if successor not in visited: # 构建新的路径:当前路径 + 新动作 newActions = actions + [action] fringe.push((successor, newActions)) # 如果栈为空仍未找到目标,返回空列表 return [] def breadthFirstSearch(problem: SearchProblem): """Search the shallowest nodes in the search tree first.""" # 初始化队列用于广度优先搜索,存储(状态, 路径)元组 # 使用队列实现FIFO(先进先出)的搜索策略 fringe = util.Queue() # 记录已访问的状态,避免重复搜索(图搜索) visited = set() # 获取起始状态并加入队列中,初始路径为空 startState = problem.getStartState() fringe.push((startState, [])) # 当队列不为空时继续搜索 while not fringe.isEmpty(): # 弹出队列头部元素(当前状态和到达该状态的路径) currentState, actions = fringe.pop() # 如果当前状态已经访问过,跳过 if currentState in visited: continue # 标记当前状态为已访问 visited.add(currentState) # 检查是否到达目标状态 if problem.isGoalState(currentState): return actions # 获取当前状态的所有后继状态 successors = problem.getSuccessors(currentState) # 将所有未访问的后继状态加入队列中 for successor, action, cost in successors: if successor not in visited: # 构建新的路径:当前路径 + 新动作 newActions = actions + [action] fringe.push((successor, newActions)) # 如果队列为空仍未找到目标,返回空列表 return [] def uniformCostSearch(problem: SearchProblem): """Search the node of least total cost first.""" # 初始化优先队列用于统一代价搜索,存储(状态, 路径, 累积代价)元组 # 使用优先队列实现按代价优先搜索的策略 fringe = util.PriorityQueue() # 记录已访问的状态及其最小代价,避免重复搜索(图搜索) visited = {} # 获取起始状态并加入优先队列中,初始路径为空,初始代价为0 startState = problem.getStartState() fringe.push((startState, [], 0), 0) # (状态, 路径, 累积代价), 优先级=累积代价 # 当优先队列不为空时继续搜索 while not fringe.isEmpty(): # 弹出优先级最高的元素(累积代价最小的元素) currentState, actions, currentCost = fringe.pop() # 如果当前状态已经访问过,且当前代价大于等于已访问的代价,跳过 if currentState in visited and currentCost >= visited[currentState]: continue # 记录当前状态及其最小代价 visited[currentState] = currentCost # 检查是否到达目标状态 if problem.isGoalState(currentState): return actions # 获取当前状态的所有后继状态 successors = problem.getSuccessors(currentState) # 将所有后继状态加入优先队列中 for successor, action, stepCost in successors: # 计算新的累积代价 newCost = currentCost + stepCost # 构建新的路径:当前路径 + 新动作 newActions = actions + [action] # 将后继状态加入优先队列,优先级为新的累积代价 fringe.push((successor, newActions, newCost), newCost) # 如果优先队列为空仍未找到目标,返回空列表 return [] def nullHeuristic(state, problem=None): """ A heuristic function estimates the cost from the current state to the nearest goal in the provided SearchProblem. This heuristic is trivial. """ return 0 def aStarSearch(problem: SearchProblem, heuristic=nullHeuristic): """Search the node that has the lowest combined cost and heuristic first.""" # 初始化优先队列用于A*搜索,存储(状态, 路径, 累积代价)元组 # 使用优先队列实现按f(n)=g(n)+h(n)优先搜索的策略 # 其中g(n)是实际代价,h(n)是启发式估计代价 fringe = util.PriorityQueue() # 记录已访问的状态及其最小g(n)代价,避免重复搜索(图搜索) visited = {} # 获取起始状态并加入优先队列中,初始路径为空,初始g(n)代价为0 startState = problem.getStartState() startHeuristic = heuristic(startState, problem) fringe.push((startState, [], 0), startHeuristic) # (状态, 路径, g(n)), 优先级=f(n)=g(n)+h(n) # 当优先队列不为空时继续搜索 while not fringe.isEmpty(): # 弹出优先级最高的元素(f(n)值最小的元素) currentState, actions, currentCost = fringe.pop() # 如果当前状态已经访问过,且当前g(n)代价大于等于已访问的g(n)代价,跳过 if currentState in visited and currentCost >= visited[currentState]: continue # 记录当前状态及其最小g(n)代价 visited[currentState] = currentCost # 检查是否到达目标状态 if problem.isGoalState(currentState): return actions # 获取当前状态的所有后继状态 successors = problem.getSuccessors(currentState) # 将所有后继状态加入优先队列中 for successor, action, stepCost in successors: # 计算新的g(n)代价 newCost = currentCost + stepCost # 计算新的h(n)启发式估计代价 newHeuristic = heuristic(successor, problem) # 计算新的f(n)值 = g(n) + h(n) fValue = newCost + newHeuristic # 构建新的路径:当前路径 + 新动作 newActions = actions + [action] # 将后继状态加入优先队列,优先级为f(n)值 fringe.push((successor, newActions, newCost), fValue) # 如果优先队列为空仍未找到目标,返回空列表 return [] # Abbreviations bfs = breadthFirstSearch dfs = depthFirstSearch astar = aStarSearch ucs = uniformCostSearch