# Pacman Search Project - 实验报告

## 项目概述

本项目是UC Berkeley CS188人工智能课程的第一个项目，主要实现各种搜索算法来解决Pacman游戏中的路径规划问题。项目涵盖了从基础的深度优先搜索到复杂的启发式搜索算法，以及针对特定问题的搜索策略设计。

## 实验环境

- 操作系统：Linux 6.17.9-2-cachyos
- Python版本：Python 3.x
- 项目路径：/home/gh0s7/project/cs188/proj1

## 实验内容与实现思路

### Q1: 深度优先搜索 (Depth First Search, DFS)

#### 实现思路
深度优先搜索是一种图搜索算法，它沿着树的深度遍历树的节点，尽可能深地搜索树的分支。当节点v的所在边都已被探寻过，搜索将回溯到发现节点v的那条边的起始节点。

#### 核心算法
1. 使用栈(Stack)数据结构实现LIFO（后进先出）的搜索策略
2. 维护一个已访问状态集合，避免重复搜索（图搜索）
3. 将状态和到达该状态的路径作为元组存储在栈中
4. 每次从栈顶弹出元素，检查是否为目标状态
5. 如果不是目标状态，将其未访问的后继状态加入栈中

#### 代码实现要点
```python
# 初始化栈用于深度优先搜索，存储(状态, 路径)元组
fringe = util.Stack()
# 记录已访问的状态，避免重复搜索
visited = set()
```

#### 测试结果
- 所有测试用例通过
- mediumMaze中找到长度为130的路径
- 扩展节点数：146个

### Q2: 广度优先搜索 (Breadth First Search, BFS)

#### 实现思路
广度优先搜索是一种图搜索算法，它从根节点开始，沿着树的宽度遍历树的节点。如果所有节点在同一深度被访问，那么算法将完全按照层级顺序进行访问。

#### 核心算法
1. 使用队列(Queue)数据结构实现FIFO（先进先出）的搜索策略
2. 维护一个已访问状态集合，避免重复搜索
3. 将状态和到达该状态的路径作为元组存储在队列中
4. 每次从队列头部弹出元素，检查是否为目标状态
5. 如果不是目标状态，将其未访问的后继状态加入队列中

#### 代码实现要点
```python
# 初始化队列用于广度优先搜索，存储(状态, 路径)元组
fringe = util.Queue()
# 记录已访问的状态，避免重复搜索
visited = set()
```

#### 测试结果
- 所有测试用例通过
- mediumMaze中找到长度为68的最短路径
- 扩展节点数：269个

### Q3: 一致代价搜索 (Uniform Cost Search, UCS)

#### 实现思路
一致代价搜索是广度优先搜索的扩展，它考虑了路径的代价。算法总是扩展当前代价最小的节点，确保找到最优解。

#### 核心算法
1. 使用优先队列(PriorityQueue)数据结构，按照累积代价排序
2. 维护一个已访问状态及其最小代价的字典
3. 将状态、路径和累积代价作为元组存储在优先队列中
4. 每次弹出代价最小的元素，检查是否为目标状态
5. 如果不是目标状态，将其后继状态加入优先队列中

#### 代码实现要点
```python
# 初始化优先队列用于统一代价搜索，存储(状态, 路径, 累积代价)元组
fringe = util.PriorityQueue()
# 记录已访问的状态及其最小代价
visited = {}
```

#### 测试结果
- 所有测试用例通过
- 在不同代价函数的迷宫中都能找到最优路径
- testSearch中找到长度为7的最优路径

### Q4: A*搜索 (A* Search)

#### 实现思路
A*搜索是一种启发式搜索算法，它结合了实际代价和启发式估计代价。算法使用f(n) = g(n) + h(n)作为评估函数，其中g(n)是从起始状态到当前状态的实际代价，h(n)是从当前状态到目标状态的启发式估计代价。

#### 核心算法
1. 使用优先队列，按照f(n)值排序
2. 维护一个已访问状态及其最小g(n)代价的字典
3. 将状态、路径和g(n)代价作为元组存储在优先队列中
4. 每次弹出f(n)值最小的元素，检查是否为目标状态
5. 如果不是目标状态，将其后继状态加入优先队列中

#### 代码实现要点
```python
# 计算新的f(n)值 = g(n) + h(n)
fValue = newCost + newHeuristic
# 将后继状态加入优先队列，优先级为f(n)值
fringe.push((successor, newActions, newCost), fValue)
```

#### 测试结果
- 所有测试用例通过
- mediumMaze中扩展221个节点，比UCS的269个节点更少
- 证明了启发式的有效性

### Q5: 角落问题 (Corners Problem)

#### 实现思路
角落问题要求Pacman访问迷宫中的四个角落。这是一个更复杂的搜索问题，需要设计合适的状态表示和状态转移。

#### 状态表示
状态表示为元组：(当前位置, 已访问的角落元组)
- 当前位置：Pacman的坐标
- 已访问的角落：四个布尔值，分别对应四个角落是否已被访问

#### 核心算法
1. 设计合理的状态表示，包含位置信息和角落访问状态
2. 实现getStartState()方法，返回初始状态
3. 实现isGoalState()方法，检查是否所有角落都已访问
4. 实现getSuccessors()方法，生成所有合法的后继状态

#### 代码实现要点
```python
# 状态表示为：(当前位置, 已访问的角落元组)
cornersVisited = tuple([corner == self.startingPosition for corner in self.corners])
return (self.startingPosition, cornersVisited)
```

#### 测试结果
- 所有测试用例通过
- tinyCorner中找到长度为28的路径

### Q6: 角落启发式 (Corners Heuristic)

#### 实现思路
为角落问题设计一个一致且可接受的启发式函数，以加速A*搜索。

#### 启发式策略
1. 计算当前位置到最远未访问角落的曼哈顿距离
2. 计算未访问角落之间的最大距离
3. 返回两个距离中的较大值作为启发式估计

#### 代码实现要点
```python
# 找出所有未访问的角落
unvisitedCorners = []
for i, corner in enumerate(corners):
    if not cornersVisited[i]:
        unvisitedCorners.append(corner)

# 计算到最远未访问角落的曼哈顿距离
maxDistance = 0
for corner in unvisitedCorners:
    distance = util.manhattanDistance(currentPosition, corner)
    maxDistance = max(maxDistance, distance)
```

#### 测试结果
- 所有测试用例通过
- 启发式测试通过，证明了一致性
- 扩展节点数：961个，满足要求

### Q7: 食物启发式 (Food Heuristic)

#### 实现思路
为食物收集问题设计一个一致且可接受的启发式函数，以加速A*搜索。

#### 启发式策略
1. 计算到最远食物的曼哈顿距离
2. 计算食物之间的最大距离
3. 使用食物数量作为基础代价
4. 返回三个策略中的最大值，确保启发式是可接受的且更准确

#### 代码实现要点
```python
# 计算到最近食物的曼哈顿距离
minDistance = float('inf')
for food in foodList:
    distance = util.manhattanDistance(position, food)
    if distance < minDistance:
        minDistance = distance

# 加上剩余食物数量的估计
return minDistance + len(foodList) - 1
```

#### 测试结果
- 18个测试用例全部通过
- 高级测试用例扩展节点数：8763个（满足阈值要求）
- 得分：4/4（满分）

### Q8: 寻找最近点路径 (Find Path to Closest Dot)

#### 实现思路
实现一个贪婪算法，每次都走向最近的食物点。这是一个次优但快速的策略。

#### 核心算法
1. 实现AnyFoodSearchProblem的isGoalState()方法
2. 在findPathToClosestDot()中使用BFS找到最近的食物点
3. 重复执行直到所有食物都被收集

#### 代码实现要点
```python
# 目标状态是Pacman到达任何食物的位置
return self.food[x][y]

# 使用广度优先搜索找到最近的食物点
path = search.bfs(problem)
```

#### 测试结果
- 所有测试用例通过
- 13个测试用例全部通过

## 实验总结

### 成果
- 成功实现了8个搜索算法和启发式函数
- 总得分：25/25（100%满分）
- Q1-Q8全部通过，获得满分

### 遇到的挑战
1. Q7的食物启发式设计较为复杂，需要在可接受性和一致性之间找到平衡
2. 状态表示的设计对搜索效率有很大影响
3. 启发式函数的质量直接影响搜索性能
4. 通过多次迭代优化，成功将启发式函数改进为满分版本

### 学到的知识
1. 掌握了各种搜索算法的原理和实现
2. 理解了启发式搜索的设计原则
3. 学会了如何针对特定问题设计合适的状态表示
4. 深入理解了可接受性和一致性的概念

### 改进方向
1. 可以进一步优化Q7的启发式函数，减少扩展节点数
2. 可以尝试更复杂的启发式策略，如最小生成树
3. 可以研究更高效的搜索算法变体

## 代码结构

### 主要文件
- `search.py`：包含所有搜索算法的实现
- `searchAgents.py`：包含搜索代理和特定问题的实现
- `util.py`：提供数据结构和工具函数

### 关键函数
- `depthFirstSearch()`：深度优先搜索
- `breadthFirstSearch()`：广度优先搜索
- `uniformCostSearch()`：一致代价搜索
- `aStarSearch()`：A*搜索
- `CornersProblem`：角落问题类
- `cornersHeuristic()`：角落启发式函数
- `foodHeuristic()`：食物启发式函数
- `findPathToClosestDot()`：寻找最近点路径

## 运行方法

### 单个问题测试
```bash
python autograder.py -q q1  # 测试Q1
python autograder.py -q q2  # 测试Q2
...
python autograder.py -q q8  # 测试Q8
```

### 全部问题测试
```bash
python autograder.py  # 测试所有问题
```

### 运行Pacman游戏
```bash
python pacman.py -l tinyMaze -p SearchAgent  # 运行Pacman游戏
```

## 结论

通过完成这个项目，我深入理解了各种搜索算法的原理和实现，掌握了启发式搜索的设计方法，学会了如何针对特定问题设计合适的搜索策略。这些知识对于解决人工智能中的路径规划问题具有重要意义。