Files
cs188/proj1/search.py
2025-12-02 01:20:07 +08:00

278 lines
10 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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