贪心搜索

This commit is contained in:
Gary Gan 2025-07-01 10:12:59 +08:00
parent ff217ece77
commit 712160462e
8 changed files with 2044 additions and 6 deletions

267
README_3x3_GREEDY.md Normal file
View File

@ -0,0 +1,267 @@
# 3x3视野贪心资源收集算法使用指南
## 概述
本项目实现了一个基于3x3视野的贪心算法用于在迷宫环境中收集资源。该算法的核心特点是
- **视野限制**每次移动时只考虑当前位置周围3x3范围内的单元格
- **贪心策略**:在可见范围内选择价值最高的资源
- **移动限制**:只能进行上下左右四个方向的移动
- **智能探索**:当视野内没有资源时,会进行探索性移动寻找新资源
## 文件说明
### 主要文件
1. **`greedy_3x3_algorithm.py`** - 独立的3x3视野贪心算法模块
2. **`tanxin.py`** - 原有的贪心算法实现已集成新的3x3算法
3. **`greedy_resource_collector.py`** - 完整版本的贪心收集器(支持全局搜索)
4. **`strict_3x3_greedy.py`** - 严格的3x3视野算法演示
### 演示文件
- **`greedy_resource_collector.py`** - 包含演示的完整贪心算法
- **`strict_3x3_greedy.py`** - 专门的3x3算法演示
## 使用方法
### 1. 基本使用
```python
from greedy_3x3_algorithm import Greedy3x3Algorithm
# 创建迷宫(二维数组,[y][x]格式)
maze = [
['s', '0', 'g5', '1', 't3'], # s=起点, g5=价值5的金币, 1=墙壁, t3=损失3的陷阱
['0', '1', '0', '0', 'g2'], # 0=可通行路径
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e'] # e=终点
]
# 创建算法实例
algorithm = Greedy3x3Algorithm(maze, debug=True)
# 运行算法
result = algorithm.run()
# 查看结果
print(f"总价值: {result['total_value']}")
print(f"收集资源数量: {result['resources_count']}")
print(f"移动步数: {result['total_moves']}")
# 打印详细结果
algorithm.print_result()
```
### 2. 在现有项目中集成
```python
from tanxin import Greedy3x3ResourceCollector
# 使用现有的迷宫数据
map_data = your_maze_data # 二维数组格式
# 创建收集器
collector = Greedy3x3ResourceCollector(map_data)
# 运行收集算法
result = collector.run_3x3_greedy_collection()
# 获取兼容格式的路径 (y, x格式)
path_yx = collector.get_path()
total_reward = collector.get_total_reward()
marked_map = collector.add_path_to_map()
```
### 3. 运行演示
```bash
# 运行独立算法演示
python greedy_3x3_algorithm.py
# 运行集成演示
echo "2" | python tanxin.py
# 运行比较演示
python strict_3x3_greedy.py
```
## 算法详解
### 地图元素说明
- **`s`** - 起点 (Start)
- **`e`** - 终点 (End)
- **`g数字`** - 金币资源,数字表示价值(如 `g5` = 价值5的金币
- **`t数字`** - 陷阱资源,数字表示损失(如 `t3` = 损失3的陷阱
- **`0`** - 可通行的路径
- **`1`** - 墙壁,无法通过
- **`l`** - 锁/机关
- **`b`** - Boss
### 算法流程
1. **初始化**:从起点开始,初始化路径和收集状态
2. **视野扫描**获取当前位置3x3范围内的所有单元格
3. **资源评估**
- 优先选择正价值资源(金币)
- 如果没有正价值资源,选择损失最小的负价值资源(陷阱)
4. **移动决策**
- 如果找到资源,移动到该位置并收集
- 如果没有资源,进行探索性移动到未访问的位置
5. **循环执行**重复步骤2-4直到满足停止条件
### 停止条件
- 达到最大移动步数默认1000步
- 连续多步默认20步无法找到新资源
- 无法进行任何移动
## 算法参数
### Greedy3x3Algorithm 参数
```python
algorithm = Greedy3x3Algorithm(
maze, # 必需:迷宫地图
start_pos=None, # 可选:起始位置(x,y),默认自动寻找
end_pos=None, # 可选:目标位置(x,y),默认自动寻找
debug=False # 可选:是否输出调试信息
)
result = algorithm.run(
max_moves=1000, # 最大移动步数
max_stuck=20 # 连续无资源的最大步数
)
```
## 结果格式
算法执行后返回的结果字典包含:
```python
{
'path': [(x1,y1), (x2,y2), ...], # 移动路径 (x,y格式)
'path_yx_format': [(y1,x1), (y2,x2), ...], # 移动路径 (y,x格式兼容现有代码)
'collected_resources': [ # 收集的资源详情
{
'position': (x, y),
'type': 'g5',
'value': 5
},
...
],
'total_value': 15, # 资源总价值
'total_moves': 25, # 总移动步数
'resources_count': 8, # 收集资源数量
'start_pos': (0, 0), # 起始位置
'end_pos': (4, 4), # 目标位置
'final_pos': (2, 3), # 最终位置
'explored_positions_count': 15, # 探索位置数量
'algorithm_name': '3x3视野贪心算法'
}
```
## 性能特点
### 优势
1. **局部最优**:在有限视野内做出最优决策
2. **探索能力**:能够智能探索未知区域
3. **资源优先**:优先收集高价值资源,避免低价值陷阱
4. **防死循环**:具备多种机制防止无限循环
### 限制
1. **视野限制**:可能错过视野外的高价值资源
2. **局部陷阱**:可能陷入局部最优解
3. **路径效率**:为了收集资源,路径可能不是最短的
## 测试用例
项目包含多个测试迷宫:
### 简单测试迷宫
```python
simple_maze = [
['s', '0', 'g5', '1', 't3'],
['0', '1', '0', '0', 'g2'],
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e']
]
```
### 复杂测试迷宫
```python
complex_maze = [
['s', '0', 'g5', '1', 't3', '0', 'g8'],
['0', '1', '0', '0', 'g2', '1', '0'],
['g3', '0', '1', 't2', '0', '0', 'g6'],
['0', 't1', '0', '0', 'g4', '1', '0'],
['1', '0', 'g1', '0', '0', '0', 't5'],
['0', 'g7', '0', '1', '0', 'g9', '0'],
['t4', '0', '0', '0', '1', '0', 'e']
]
```
## 扩展开发
### 自定义评估函数
可以通过继承 `Greedy3x3Algorithm` 类来自定义资源评估逻辑:
```python
class CustomGreedy3x3(Greedy3x3Algorithm):
def _evaluate_resource_value(self, cell):
# 自定义评估逻辑
if cell.startswith('special'):
return 100 # 特殊资源高价值
return super()._evaluate_resource_value(cell)
```
### 自定义移动策略
```python
class CustomGreedy3x3(Greedy3x3Algorithm):
def _find_exploration_target(self):
# 自定义探索策略
adjacent = self.get_adjacent_positions(self.current_pos)
# 选择最靠近终点的位置
if adjacent:
return min(adjacent, key=lambda pos:
abs(pos[0] - self.end_pos[0]) + abs(pos[1] - self.end_pos[1]))
return None
```
## 常见问题
### Q: 算法为什么会在某些位置反复移动?
A: 这是因为算法在视野内找不到新资源时会进行探索性移动。连续20步无资源后会自动停止。
### Q: 如何提高算法的收集效率?
A: 可以调整参数:
- 减小 `max_stuck` 参数,减少无效探索
- 增加迷宫中的资源密度
- 优化迷宫布局,减少死胡同
### Q: 算法支持什么样的迷宫格式?
A: 支持二维列表格式,使用 `[y][x]` 索引方式。单元格内容为字符串。
### Q: 如何集成到现有项目?
A: 可以使用 `tanxin.py` 中的 `Greedy3x3ResourceCollector` 类,它与现有代码兼容。
## 版本信息
- **版本**: 1.0
- **作者**: GitHub Copilot
- **创建日期**: 2025年1月
- **Python版本**: 3.6+
- **依赖**: 无额外依赖使用Python标准库

View File

@ -57,6 +57,9 @@ def get_button_positions(maze_display_size=MAZE_SIZE):
'next_step_button': (control_panel_x, 200), 'next_step_button': (control_panel_x, 200),
'reset_path_button': (control_panel_x + 120, 200), 'reset_path_button': (control_panel_x + 120, 200),
'auto_play_button': (control_panel_x + 250, 200), 'auto_play_button': (control_panel_x + 250, 200),
# 贪心搜索按钮
'greedy_search_button': (control_panel_x + 380, 200),
'greedy_auto_play_button': (control_panel_x + 500, 200),
# 历史迭代控制按钮 # 历史迭代控制按钮
'history_prev_button': (control_panel_x, 260), 'history_prev_button': (control_panel_x, 260),
'history_next_button': (control_panel_x + 120, 260), 'history_next_button': (control_panel_x + 120, 260),
@ -67,5 +70,6 @@ def get_button_positions(maze_display_size=MAZE_SIZE):
'history_progress_text': (control_panel_x, 350), 'history_progress_text': (control_panel_x, 350),
'hint_text': (control_panel_x, 380), 'hint_text': (control_panel_x, 380),
'shortcut_text': (control_panel_x, 410), 'shortcut_text': (control_panel_x, 410),
'save_list_area': (control_panel_x, 440, 400, 330) # x, y, width, height - 增加高度到330 'greedy_info_text': (control_panel_x, 440), # 贪心算法信息显示位置
'save_list_area': (control_panel_x, 470, 400, 300) # x, y, width, height - 调整位置和高度
} }

380
greedy_3x3_algorithm.py Normal file
View File

@ -0,0 +1,380 @@
"""
3x3视野贪心资源收集算法
该模块实现了一个基于3x3视野的贪心算法用于在迷宫中收集资源
算法特点
1. 每次移动时只考虑当前位置周围3x3范围内的资源
2. 优先选择正价值资源金币如果没有则选择损失最小的负价值资源陷阱
3. 只能进行上下左右四个方向的移动
4. 当视野内没有资源时会进行探索性移动
5. 避免无限循环当连续多步无法找到新资源时会自动停止
使用方法
```python
from greedy_3x3_algorithm import Greedy3x3Algorithm
# 创建迷宫(二维数组,[y][x]格式)
maze = [
['s', '0', 'g5', '1', 't3'],
['0', '1', '0', '0', 'g2'],
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e']
]
# 创建算法实例并运行
algorithm = Greedy3x3Algorithm(maze)
result = algorithm.run()
# 获取结果
print(f"总价值: {result['total_value']}")
print(f"路径: {result['path']}")
```
"""
import copy
from collections import deque
class Greedy3x3Algorithm:
"""
3x3视野贪心资源收集算法
该算法在每个位置只考虑周围3x3范围内的资源选择价值最高的资源进行收集
如果视野内没有资源则进行探索性移动
"""
def __init__(self, maze, start_pos=None, end_pos=None, debug=False):
"""
初始化算法
Args:
maze: 二维列表表示迷宫地图 ([y][x]格式)
start_pos: 起始位置 (x, y)默认自动寻找's'
end_pos: 目标位置 (x, y)默认自动寻找'e'
debug: 是否输出调试信息
"""
self.original_maze = copy.deepcopy(maze)
self.maze = copy.deepcopy(maze)
self.rows = len(maze)
self.cols = len(maze[0]) if self.rows > 0 else 0
self.debug = debug
# 寻找起始位置和目标位置
self.start_pos = start_pos or self._find_position('s')
self.end_pos = end_pos or self._find_position('e')
if not self.start_pos:
raise ValueError("无法找到起始位置 's'")
if not self.end_pos:
raise ValueError("无法找到目标位置 'e'")
# 初始化状态
self.current_pos = self.start_pos
self.path = [self.start_pos]
self.collected_resources = []
self.total_value = 0
self.visited_resources = set()
self.explored_positions = set([self.start_pos])
if self.debug:
print(f"3x3视野贪心算法初始化")
print(f"迷宫大小: {self.rows}x{self.cols}")
print(f"起始位置: {self.start_pos}")
print(f"目标位置: {self.end_pos}")
def _find_position(self, target):
"""寻找地图中指定字符的位置,返回(x, y)格式"""
for y in range(self.rows):
for x in range(self.cols):
if self.maze[y][x].lower() == target.lower():
return (x, y)
return None
def get_3x3_vision(self, pos):
"""
获取以pos为中心的3x3视野范围内的所有单元格
Args:
pos: 当前位置 (x, y)
Returns:
dict: {(x, y): cell_value} 形式的字典
"""
x, y = pos
vision = {}
# 遍历3x3范围
for dx in range(-1, 2):
for dy in range(-1, 2):
new_x, new_y = x + dx, y + dy
# 检查边界
if 0 <= new_x < self.cols and 0 <= new_y < self.rows:
vision[(new_x, new_y)] = self.maze[new_y][new_x]
return vision
def get_adjacent_positions(self, pos):
"""
获取当前位置的上下左右四个相邻位置
Args:
pos: 当前位置 (x, y)
Returns:
list: 可移动的相邻位置列表
"""
x, y = pos
adjacent = []
# 上下左右四个方向
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # 上、下、左、右
for dx, dy in directions:
new_x, new_y = x + dx, y + dy
# 检查边界和可移动性
if (0 <= new_x < self.cols and
0 <= new_y < self.rows and
self._can_move_to((new_x, new_y))):
adjacent.append((new_x, new_y))
return adjacent
def _can_move_to(self, pos):
"""检查是否可以移动到指定位置"""
x, y = pos
cell = self.maze[y][x]
return cell != '1' # 不能移动到墙壁
def _evaluate_resource_value(self, cell):
"""评估资源的价值"""
if cell.startswith('g'):
try:
return int(cell[1:])
except ValueError:
return 0
elif cell.startswith('t'):
try:
return -int(cell[1:])
except ValueError:
return 0
else:
return 0
def _find_best_resource_in_vision(self):
"""
在3x3视野范围内找到价值最高的可到达资源
Returns:
tuple: (最佳资源位置, 资源价值) (None, 0)
"""
vision = self.get_3x3_vision(self.current_pos)
adjacent_positions = self.get_adjacent_positions(self.current_pos)
best_pos = None
best_value = float('-inf')
# 只考虑相邻且在视野内的位置
for pos in adjacent_positions:
if pos in vision and pos not in self.visited_resources:
cell = vision[pos]
value = self._evaluate_resource_value(cell)
if value != 0 and value > best_value:
best_value = value
best_pos = pos
return best_pos, best_value if best_pos else 0
def _find_exploration_target(self):
"""当视野内没有资源时,寻找探索目标"""
adjacent = self.get_adjacent_positions(self.current_pos)
# 优先选择未探索的位置
unexplored = [pos for pos in adjacent if pos not in self.explored_positions]
if unexplored:
return unexplored[0]
# 如果所有相邻位置都探索过,选择任意一个
if adjacent:
return adjacent[0]
return None
def _collect_resource(self, pos):
"""收集指定位置的资源"""
x, y = pos
cell = self.maze[y][x]
value = self._evaluate_resource_value(cell)
if value != 0:
self.collected_resources.append({
'position': pos,
'type': cell,
'value': value
})
self.total_value += value
self.visited_resources.add(pos)
if self.debug:
print(f"收集资源: 位置{pos}, 类型{cell}, 价值{value}, 总价值{self.total_value}")
def run(self, max_moves=1000, max_stuck=20):
"""
运行3x3视野贪心资源收集算法
Args:
max_moves: 最大移动步数防止无限循环
max_stuck: 连续无资源的最大步数
Returns:
dict: 包含路径收集的资源等信息的结果字典
"""
if self.debug:
print("\\n开始3x3视野贪心资源收集...")
moves = 0
stuck_count = 0
while moves < max_moves and stuck_count < max_stuck:
moves += 1
# 在3x3视野内寻找最佳资源
best_resource_pos, best_value = self._find_best_resource_in_vision()
if best_resource_pos is not None:
if self.debug:
print(f"{moves}步: 发现视野内资源 位置{best_resource_pos}, 价值{best_value}")
# 移动到资源位置并收集
self.current_pos = best_resource_pos
self.path.append(best_resource_pos)
self.explored_positions.add(best_resource_pos)
self._collect_resource(best_resource_pos)
stuck_count = 0 # 重置无资源计数
else:
# 视野内没有资源,进行探索性移动
exploration_target = self._find_exploration_target()
if exploration_target:
if self.debug:
print(f"{moves}步: 视野内无资源,探索移动到 {exploration_target}")
self.current_pos = exploration_target
self.path.append(exploration_target)
self.explored_positions.add(exploration_target)
stuck_count += 1
else:
if self.debug:
print(f"{moves}步: 无法进行任何移动,结束收集")
break
if self.debug:
if moves >= max_moves:
print(f"达到最大移动步数 {max_moves},结束收集")
elif stuck_count >= max_stuck:
print(f"连续 {max_stuck} 步未找到资源,结束收集")
print("3x3视野资源收集完成")
return self._get_result()
def _get_result(self):
"""获取算法执行结果"""
return {
'path': self.path.copy(),
'path_yx_format': [(y, x) for (x, y) in self.path], # 兼容现有代码的(y,x)格式
'collected_resources': self.collected_resources.copy(),
'total_value': self.total_value,
'total_moves': len(self.path) - 1,
'resources_count': len(self.collected_resources),
'start_pos': self.start_pos,
'end_pos': self.end_pos,
'final_pos': self.current_pos,
'explored_positions_count': len(self.explored_positions),
'algorithm_name': '3x3视野贪心算法'
}
def get_marked_maze(self):
"""
获取标记了路径的迷宫
Returns:
list: 标记后的迷宫S=起点, E=终点, *=已收集资源, .=路径
"""
marked_maze = copy.deepcopy(self.original_maze)
# 标记路径点
for i, (x, y) in enumerate(self.path):
if (x, y) == self.start_pos:
marked_maze[y][x] = 'S' # 起点
elif (x, y) == self.end_pos:
marked_maze[y][x] = 'E' # 终点
elif (x, y) in [r['position'] for r in self.collected_resources]:
marked_maze[y][x] = '*' # 已收集资源
else:
marked_maze[y][x] = '.' # 路径点
return marked_maze
def print_result(self):
"""打印算法执行结果"""
result = self._get_result()
print("\\n=== 3x3视野贪心算法执行结果 ===")
print(f"起始位置: {result['start_pos']}")
print(f"最终位置: {result['final_pos']}")
print(f"总移动步数: {result['total_moves']}")
print(f"探索位置数: {result['explored_positions_count']}")
print(f"收集资源数量: {result['resources_count']}")
print(f"资源总价值: {result['total_value']}")
if result['collected_resources']:
print("\\n收集的资源详情:")
for i, resource in enumerate(result['collected_resources'], 1):
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
else:
print("\\n未收集到任何资源")
# 显示标记后的迷宫
marked_maze = self.get_marked_maze()
print("\\n标记路径后的迷宫:")
print("S: 起点, E: 终点, *: 已收集资源, .: 路径")
for row in marked_maze:
print(' '.join(f"{cell:>2}" for cell in row))
def demo():
"""演示函数"""
# 创建示例迷宫
demo_maze = [
['s', '0', 'g5', '1', 't3'],
['0', '1', '0', '0', 'g2'],
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e']
]
print("=== 3x3视野贪心算法演示 ===")
print("迷宫说明:")
print(" s: 起点, e: 终点")
print(" g数字: 金币资源 (正收益)")
print(" t数字: 陷阱资源 (负收益)")
print(" 0: 可通行路径, 1: 墙壁")
print("\\n原始迷宫:")
for row in demo_maze:
print(' '.join(f"{cell:>2}" for cell in row))
# 运行算法
algorithm = Greedy3x3Algorithm(demo_maze, debug=True)
result = algorithm.run()
# 打印结果
algorithm.print_result()
return algorithm, result
if __name__ == "__main__":
demo()

View File

@ -0,0 +1,448 @@
import copy
from collections import deque
class GreedyResourceCollector:
"""
基于贪心算法的资源收集器
每次移动时选择3x3视野范围内最高价值的资源
只能进行上下左右移动
"""
def __init__(self, maze, start_pos=None, end_pos=None):
"""
初始化贪心资源收集器
Args:
maze: 迷宫地图2D列表
start_pos: 起始位置 (row, col)如果为None则自动寻找
end_pos: 目标位置 (row, col)如果为None则自动寻找
"""
self.original_maze = copy.deepcopy(maze)
self.maze = copy.deepcopy(maze)
self.rows = len(maze)
self.cols = len(maze[0]) if self.rows > 0 else 0
# 寻找起始位置和目标位置
self.start_pos = start_pos or self._find_position('s')
self.end_pos = end_pos or self._find_position('e')
if not self.start_pos:
raise ValueError("无法找到起始位置 's'")
if not self.end_pos:
raise ValueError("无法找到目标位置 'e'")
self.current_pos = self.start_pos
self.path = [self.start_pos]
self.collected_resources = []
self.total_value = 0
self.visited_resources = set()
print(f"起始位置: {self.start_pos}")
print(f"目标位置: {self.end_pos}")
def _find_position(self, target):
"""寻找地图中指定字符的位置"""
for i in range(self.rows):
for j in range(self.cols):
if self.maze[i][j].lower() == target.lower():
return (i, j)
return None
def get_3x3_vision(self, pos):
"""
获取以pos为中心的3x3视野范围内的所有单元格
Args:
pos: 当前位置 (row, col)
Returns:
dict: {(row, col): cell_value} 形式的字典
"""
row, col = pos
vision = {}
# 遍历3x3范围
for dr in range(-1, 2):
for dc in range(-1, 2):
new_row, new_col = row + dr, col + dc
# 检查边界
if 0 <= new_row < self.rows and 0 <= new_col < self.cols:
vision[(new_row, new_col)] = self.maze[new_row][new_col]
return vision
def get_adjacent_cells(self, pos):
"""
获取当前位置的上下左右四个相邻位置
Args:
pos: 当前位置 (row, col)
Returns:
list: 可移动的相邻位置列表
"""
row, col = pos
adjacent = []
# 上下左右四个方向
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for dr, dc in directions:
new_row, new_col = row + dr, col + dc
# 检查边界和可移动性
if (0 <= new_row < self.rows and
0 <= new_col < self.cols and
self.can_move_to((new_row, new_col))):
adjacent.append((new_row, new_col))
return adjacent
def can_move_to(self, pos):
"""
检查是否可以移动到指定位置
Args:
pos: 目标位置 (row, col)
Returns:
bool: 是否可以移动
"""
row, col = pos
cell = self.maze[row][col]
# 不能移动到墙壁
if cell == '1':
return False
return True
def evaluate_resource_value(self, cell):
"""
评估资源的价值
Args:
cell: 单元格内容
Returns:
int: 资源价值正数表示收益负数表示损失
"""
if cell.startswith('g'):
# 金币资源,提取数值
try:
return int(cell[1:])
except ValueError:
return 0
elif cell.startswith('t'):
# 陷阱资源,提取数值并取负
try:
return -int(cell[1:])
except ValueError:
return 0
else:
# 其他类型单元格没有资源价值
return 0
def find_best_resource_in_vision(self):
"""
在3x3视野范围内找到价值最高的可到达资源
如果视野内没有正价值资源则考虑移动到更有利的位置
Returns:
tuple: (最佳资源位置, 资源价值) (None, 0)
"""
vision = self.get_3x3_vision(self.current_pos)
best_pos = None
best_value = float('-inf')
# 首先只考虑正价值资源
for pos, cell in vision.items():
# 跳过已访问的资源
if pos in self.visited_resources:
continue
# 跳过当前位置
if pos == self.current_pos:
continue
# 跳过不可移动的位置
if not self.can_move_to(pos):
continue
# 检查是否为资源
value = self.evaluate_resource_value(cell)
if value > 0 and value > best_value: # 只考虑正价值资源
# 确保可以到达这个位置
path_to_resource = self.find_path_to_target(pos)
if path_to_resource: # 可以到达
best_value = value
best_pos = pos
# 如果视野内没有正价值资源,考虑负价值资源(陷阱)
if best_pos is None:
for pos, cell in vision.items():
if pos in self.visited_resources or pos == self.current_pos:
continue
if not self.can_move_to(pos):
continue
value = self.evaluate_resource_value(cell)
if value < 0 and value > best_value: # 选择损失最小的陷阱
path_to_resource = self.find_path_to_target(pos)
if path_to_resource:
best_value = value
best_pos = pos
# 如果视野内完全没有资源,寻找最近的正价值资源
if best_pos is None:
best_pos, best_value = self.find_nearest_valuable_resource()
return best_pos, best_value if best_pos else 0
def find_nearest_valuable_resource(self):
"""
在整个地图上寻找最近的高价值正资源
Returns:
tuple: (最佳资源位置, 资源价值) (None, 0)
"""
best_pos = None
best_score = float('-inf')
for i in range(self.rows):
for j in range(self.cols):
pos = (i, j)
cell = self.maze[i][j]
# 跳过已访问的资源
if pos in self.visited_resources:
continue
# 跳过当前位置
if pos == self.current_pos:
continue
# 检查是否为正价值资源
value = self.evaluate_resource_value(cell)
if value > 0: # 只考虑正价值资源
# 计算到达该资源的路径
path_to_resource = self.find_path_to_target(pos)
if path_to_resource:
# 计算性价比:价值/距离
distance = len(path_to_resource)
score = value / distance
if score > best_score:
best_score = score
best_pos = pos
if best_pos:
value = self.evaluate_resource_value(self.maze[best_pos[0]][best_pos[1]])
return best_pos, value
return None, 0
def find_path_to_target(self, target_pos):
"""
使用BFS找到到目标位置的最短路径
Args:
target_pos: 目标位置 (row, col)
Returns:
list: 从当前位置到目标位置的路径不包含当前位置
"""
if self.current_pos == target_pos:
return []
queue = deque([(self.current_pos, [])])
visited = {self.current_pos}
while queue:
pos, path = queue.popleft()
# 获取相邻位置
for next_pos in self.get_adjacent_cells(pos):
if next_pos in visited:
continue
new_path = path + [next_pos]
# 找到目标
if next_pos == target_pos:
return new_path
visited.add(next_pos)
queue.append((next_pos, new_path))
return [] # 无法到达
def collect_resource(self, pos):
"""
收集指定位置的资源
Args:
pos: 资源位置 (row, col)
"""
row, col = pos
cell = self.maze[row][col]
value = self.evaluate_resource_value(cell)
if value != 0:
self.collected_resources.append({
'position': pos,
'type': cell,
'value': value
})
self.total_value += value
self.visited_resources.add(pos)
print(f"收集资源: 位置{pos}, 类型{cell}, 价值{value}, 总价值{self.total_value}")
def run_greedy_collection(self):
"""
运行贪心资源收集算法
Returns:
dict: 包含路径收集的资源等信息
"""
print("开始贪心资源收集...")
while True:
# 在3x3视野内寻找最佳资源
best_resource_pos, best_value = self.find_best_resource_in_vision()
if best_resource_pos is None:
print("视野内没有更多资源可收集")
break
print(f"发现最佳资源: 位置{best_resource_pos}, 价值{best_value}")
# 计算到最佳资源的路径
path_to_resource = self.find_path_to_target(best_resource_pos)
if not path_to_resource:
print(f"无法到达资源位置{best_resource_pos}")
self.visited_resources.add(best_resource_pos) # 标记为无法到达
continue
# 移动到资源位置
for next_pos in path_to_resource:
self.current_pos = next_pos
self.path.append(next_pos)
print(f"移动到: {next_pos}")
# 收集资源
self.collect_resource(best_resource_pos)
print("资源收集完成!")
return self.get_collection_result()
def get_collection_result(self):
"""
获取收集结果
Returns:
dict: 包含路径资源统计信息的字典
"""
return {
'path': self.path.copy(),
'collected_resources': self.collected_resources.copy(),
'total_value': self.total_value,
'total_moves': len(self.path) - 1,
'resources_count': len(self.collected_resources),
'start_pos': self.start_pos,
'end_pos': self.end_pos,
'final_pos': self.current_pos
}
def print_result_summary(self):
"""打印收集结果摘要"""
result = self.get_collection_result()
print("\n=== 贪心资源收集结果摘要 ===")
print(f"起始位置: {result['start_pos']}")
print(f"最终位置: {result['final_pos']}")
print(f"总移动步数: {result['total_moves']}")
print(f"收集资源数量: {result['resources_count']}")
print(f"资源总价值: {result['total_value']}")
print("\n收集的资源详情:")
for i, resource in enumerate(result['collected_resources'], 1):
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
print(f"\n完整移动路径: {' -> '.join(map(str, result['path']))}")
def visualize_path_on_maze(self):
"""
在迷宫上可视化移动路径
Returns:
list: 标记了路径的迷宫副本
"""
visual_maze = copy.deepcopy(self.original_maze)
# 标记路径
for i, pos in enumerate(self.path):
row, col = pos
if pos == self.start_pos:
visual_maze[row][col] = 'S' # 起点
elif pos == self.end_pos:
visual_maze[row][col] = 'E' # 终点
elif pos in [r['position'] for r in self.collected_resources]:
# 已收集的资源位置
visual_maze[row][col] = '*'
else:
# 路径点
visual_maze[row][col] = str(i % 10)
return visual_maze
def print_visual_maze(self):
"""打印可视化的迷宫"""
visual_maze = self.visualize_path_on_maze()
print("\n=== 路径可视化迷宫 ===")
print("S: 起点, E: 终点, *: 已收集资源, 数字: 路径步骤")
for row in visual_maze:
print(' '.join(f"{cell:>2}" for cell in row))
def demo_greedy_resource_collection():
"""演示贪心资源收集算法"""
# 创建一个示例迷宫
demo_maze = [
['s', '0', 'g5', '1', 't3'],
['0', '1', '0', '0', 'g2'],
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e']
]
print("=== 贪心资源收集算法演示 ===")
print("迷宫说明:")
print(" s: 起点, e: 终点")
print(" g数字: 金币资源 (正收益)")
print(" t数字: 陷阱资源 (负收益)")
print(" 0: 可通行路径, 1: 墙壁")
print("\n原始迷宫:")
for row in demo_maze:
print(' '.join(f"{cell:>2}" for cell in row))
# 创建贪心收集器并运行
collector = GreedyResourceCollector(demo_maze)
result = collector.run_greedy_collection()
# 打印结果
collector.print_result_summary()
collector.print_visual_maze()
return collector, result
if __name__ == "__main__":
# 运行演示
collector, result = demo_greedy_resource_collection()

75
main.py
View File

@ -7,6 +7,7 @@ from draw import Button, Toast
from save_ui import SaveLoadUI from save_ui import SaveLoadUI
from boss_fight_ui import BossFightUI from boss_fight_ui import BossFightUI
from mechanism_ui import MechanismUI from mechanism_ui import MechanismUI
from greedy_3x3_algorithm import Greedy3x3Algorithm
from config import * from config import *
import sys import sys
import os import os
@ -50,6 +51,10 @@ def create_buttons(button_positions):
button_reset_path = Button(pygame.rect.Rect(*button_positions['reset_path_button'], *BUTTON_CONTROL_SIZE), None) button_reset_path = Button(pygame.rect.Rect(*button_positions['reset_path_button'], *BUTTON_CONTROL_SIZE), None)
button_auto_play = Button(pygame.rect.Rect(*button_positions['auto_play_button'], *BUTTON_CONTROL_SIZE), None) button_auto_play = Button(pygame.rect.Rect(*button_positions['auto_play_button'], *BUTTON_CONTROL_SIZE), None)
# 贪心搜索按钮
button_greedy_search = Button(pygame.rect.Rect(*button_positions['greedy_search_button'], *BUTTON_CONTROL_SIZE), None)
button_greedy_auto_play = Button(pygame.rect.Rect(*button_positions['greedy_auto_play_button'], *BUTTON_CONTROL_SIZE), None)
# 历史迭代控制按钮 # 历史迭代控制按钮
button_history_prev = Button(pygame.rect.Rect(*button_positions['history_prev_button'], *BUTTON_CONTROL_SIZE), None) button_history_prev = Button(pygame.rect.Rect(*button_positions['history_prev_button'], *BUTTON_CONTROL_SIZE), None)
button_history_next = Button(pygame.rect.Rect(*button_positions['history_next_button'], *BUTTON_CONTROL_SIZE), None) button_history_next = Button(pygame.rect.Rect(*button_positions['history_next_button'], *BUTTON_CONTROL_SIZE), None)
@ -63,6 +68,8 @@ def create_buttons(button_positions):
'next_step': button_next_step, 'next_step': button_next_step,
'reset_path': button_reset_path, 'reset_path': button_reset_path,
'auto_play': button_auto_play, 'auto_play': button_auto_play,
'greedy_search': button_greedy_search,
'greedy_auto_play': button_greedy_auto_play,
'history_prev': button_history_prev, 'history_prev': button_history_prev,
'history_next': button_history_next, 'history_next': button_history_next,
'history_auto': button_history_auto, 'history_auto': button_history_auto,
@ -95,6 +102,7 @@ if __name__ == "__main__":
mes4 = Toast("存档加载成功", UI_WIDTH, UI_HEIGHT, font=textFont) mes4 = Toast("存档加载成功", UI_WIDTH, UI_HEIGHT, font=textFont)
mes5 = Toast("加载失败", UI_WIDTH, UI_HEIGHT, font=textFont) mes5 = Toast("加载失败", UI_WIDTH, UI_HEIGHT, font=textFont)
mes_boss = Toast("遇到Boss准备战斗", UI_WIDTH, UI_HEIGHT, font=textFont, duration=3, color=(255, 255, 0), bg_color=(255, 0, 0)) mes_boss = Toast("遇到Boss准备战斗", UI_WIDTH, UI_HEIGHT, font=textFont, duration=3, color=(255, 255, 0), bg_color=(255, 0, 0))
mes_greedy = Toast("贪心搜索完成!", UI_WIDTH, UI_HEIGHT, font=textFont, duration=3, color=(255, 255, 255), bg_color=(0, 128, 0))
# 创建存档界面 # 创建存档界面
save_ui = SaveLoadUI(textFont) save_ui = SaveLoadUI(textFont)
@ -111,6 +119,11 @@ if __name__ == "__main__":
auto_play_timer = 0 auto_play_timer = 0
auto_play_interval = AUTO_PLAY_INTERVAL auto_play_interval = AUTO_PLAY_INTERVAL
# 贪心算法控制变量
greedy_auto_play = False
greedy_auto_timer = 0
greedy_auto_interval = AUTO_PLAY_INTERVAL
# 历史迭代控制变量 # 历史迭代控制变量
history_auto_play = False history_auto_play = False
history_auto_timer = 0 history_auto_timer = 0
@ -152,6 +165,15 @@ if __name__ == "__main__":
auto_play = False # 遇到机关时停止自动播放 auto_play = False # 遇到机关时停止自动播放
auto_play_timer = 0 auto_play_timer = 0
# 贪心算法自动播放逻辑
if greedy_auto_play and hasattr(maze, 'greedy_path') and maze.greedy_path and not maze.show_history and not boss_fight_ui.is_showing and not mechanism_ui.is_showing:
greedy_auto_timer += 1
if greedy_auto_timer >= greedy_auto_interval:
has_next = maze.next_greedy_step()
if not has_next:
greedy_auto_play = False # 贪心路径播放完成后停止自动播放
greedy_auto_timer = 0
# 历史迭代自动播放逻辑 # 历史迭代自动播放逻辑
if history_auto_play and len(maze.history_mazes) > 0 and maze.show_history: if history_auto_play and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_timer += 1 history_auto_timer += 1
@ -188,6 +210,10 @@ if __name__ == "__main__":
buttons['reset_path'].handle_event(event=event) buttons['reset_path'].handle_event(event=event)
buttons['auto_play'].handle_event(event=event) buttons['auto_play'].handle_event(event=event)
# 贪心搜索按钮
buttons['greedy_search'].handle_event(event=event)
buttons['greedy_auto_play'].handle_event(event=event)
# 历史迭代控制按钮 # 历史迭代控制按钮
buttons['history_prev'].handle_event(event=event) buttons['history_prev'].handle_event(event=event)
buttons['history_next'].handle_event(event=event) buttons['history_next'].handle_event(event=event)
@ -263,6 +289,18 @@ if __name__ == "__main__":
auto_play = not auto_play auto_play = not auto_play
auto_play_timer = 0 auto_play_timer = 0
# 贪心搜索控制
if buttons['greedy_search'].pressed == True and len(maze.grid) > 0:
success = maze.run_greedy_search()
if success:
greedy_auto_play = False # 重新生成路径时停止自动播放
mes_greedy.text = f"贪心搜索完成!收集{len(maze.greedy_result['collected_resources'])}个资源,总价值{maze.greedy_result['total_value']}"
mes_greedy.show()
if buttons['greedy_auto_play'].pressed == True and hasattr(maze, 'greedy_path') and maze.greedy_path:
greedy_auto_play = not greedy_auto_play
greedy_auto_timer = 0
# 历史迭代控制 # 历史迭代控制
if buttons['history_prev'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history: if buttons['history_prev'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
maze.prev_history_step() maze.prev_history_step()
@ -326,6 +364,18 @@ if __name__ == "__main__":
# Ctrl+L 打开读档界面 # Ctrl+L 打开读档界面
save_ui.update_save_list(maze) save_ui.update_save_list(maze)
save_ui.toggle_save_list() save_ui.toggle_save_list()
# 贪心算法快捷键
elif event.key == pygame.K_g and len(maze.grid) > 0:
# G键 运行贪心搜索
success = maze.run_greedy_search()
if success:
greedy_auto_play = False
mes_greedy.text = f"贪心搜索完成!收集{len(maze.greedy_result['collected_resources'])}个资源,总价值{maze.greedy_result['total_value']}"
mes_greedy.show()
elif event.key == pygame.K_g and pygame.key.get_pressed()[pygame.K_LSHIFT] and hasattr(maze, 'greedy_path') and maze.greedy_path:
# Shift+G 贪心自动播放
greedy_auto_play = not greedy_auto_play
greedy_auto_timer = 0
if event.type == QUIT: if event.type == QUIT:
running = False running = False
@ -355,6 +405,25 @@ if __name__ == "__main__":
# 显示当前步数信息 # 显示当前步数信息
progress_text = textFont.render(f"路径进度: {maze.path_step}/{len(maze.full_path)}", True, COLOR_BLACK) progress_text = textFont.render(f"路径进度: {maze.path_step}/{len(maze.full_path)}", True, COLOR_BLACK)
screen.blit(progress_text, button_positions['progress_text']) screen.blit(progress_text, button_positions['progress_text'])
# 绘制贪心搜索按钮(仅在有迷宫时显示)
if len(maze.grid) > 0:
# 绘制按钮背景
pygame.draw.rect(screen, COLOR_GOLD, buttons['greedy_search'].rect)
# 绘制按钮文字
greedy_text = textFont.render("贪心搜索", True, COLOR_BLACK)
screen.blit(greedy_text, (buttons['greedy_search'].rect.x + 5, buttons['greedy_search'].rect.y + 15))
# 绘制贪心自动播放按钮(仅在有贪心路径时显示)
if hasattr(maze, 'greedy_path') and maze.greedy_path:
pygame.draw.rect(screen, COLOR_GREEN if greedy_auto_play else COLOR_GRAY, buttons['greedy_auto_play'].rect)
greedy_auto_text = textFont.render("贪心播放" if not greedy_auto_play else "停止", True, COLOR_BLACK)
screen.blit(greedy_auto_text, (buttons['greedy_auto_play'].rect.x + 5, buttons['greedy_auto_play'].rect.y + 15))
# 显示贪心算法信息
greedy_info_text = textFont.render(maze.get_greedy_progress(), True, COLOR_BLACK)
screen.blit(greedy_info_text, button_positions['greedy_info_text'])
# 绘制历史迭代控制按钮(仅在有历史且为历史模式时显示) # 绘制历史迭代控制按钮(仅在有历史且为历史模式时显示)
if len(maze.history_mazes) > 0 and maze.show_history: if len(maze.history_mazes) > 0 and maze.show_history:
@ -384,11 +453,14 @@ if __name__ == "__main__":
# 显示操作提示 # 显示操作提示
if len(maze.full_path) > 0 and not maze.show_history: if len(maze.full_path) > 0 and not maze.show_history:
hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放", True, COLOR_LIGHT_GRAY) hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放 | G键: 贪心搜索", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text']) screen.blit(hint_text, button_positions['hint_text'])
elif len(maze.history_mazes) > 0 and maze.show_history: elif len(maze.history_mazes) > 0 and maze.show_history:
hint_text = textFont.render("←→键: 历史步骤 | P键: 自动播放 | H键: 切换模式", True, COLOR_LIGHT_GRAY) hint_text = textFont.render("←→键: 历史步骤 | P键: 自动播放 | H键: 切换模式", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text']) screen.blit(hint_text, button_positions['hint_text'])
elif len(maze.grid) > 0:
hint_text = textFont.render("G键: 运行贪心搜索 | Shift+G: 贪心自动播放", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text'])
# 显示快捷键提示 # 显示快捷键提示
shortcut_text = textFont.render("Ctrl+S: 保存JSON | Shift+S: 保存CSV | Ctrl+L: 读档", True, COLOR_LIGHT_GRAY) shortcut_text = textFont.render("Ctrl+S: 保存JSON | Shift+S: 保存CSV | Ctrl+L: 读档", True, COLOR_LIGHT_GRAY)
@ -400,6 +472,7 @@ if __name__ == "__main__":
mes4.draw(screen=screen) mes4.draw(screen=screen)
mes5.draw(screen=screen) mes5.draw(screen=screen)
mes_boss.draw(screen=screen) mes_boss.draw(screen=screen)
mes_greedy.draw(screen=screen)
# 绘制存档界面(必须在最后绘制以显示在最上层) # 绘制存档界面(必须在最后绘制以显示在最上层)
save_ui.draw(screen) save_ui.draw(screen)

124
maze.py
View File

@ -3,11 +3,12 @@ from maze_generator import MazeGenerator
from SourceCollector import SourceCollector from SourceCollector import SourceCollector
from tanxin import * from tanxin import *
from simple_save_manager import simple_save_manager from simple_save_manager import simple_save_manager
from greedy_3x3_algorithm import Greedy3x3Algorithm
from config import UI_WIDTH from config import UI_WIDTH
import time import time
import random import random
import json import json
INPUT_DATA = "/home/guan/dev/python_dev/maze_python/saves/input/input_case_1.json" INPUT_DATA = "/Users/gary/dev/maze_python/saves/input_case_1.json"
class Maze: class Maze:
def __init__(self, wall_size, maze_size, file_name): def __init__(self, wall_size, maze_size, file_name):
self.wall_size = wall_size self.wall_size = wall_size
@ -359,6 +360,10 @@ class Maze:
# 绘制迷宫边界线(动态位置) # 绘制迷宫边界线(动态位置)
pygame.draw.line(screen, (0, 0, 0), (self.actual_display_size, 0), (self.actual_display_size, self.actual_display_size), 3) pygame.draw.line(screen, (0, 0, 0), (self.actual_display_size, 0), (self.actual_display_size, self.actual_display_size), 3)
# 绘制贪心路径
if hasattr(self, 'greedy_path') and self.greedy_path and hasattr(self, 'greedy_step'):
self._draw_greedy_path(screen, tile_size)
def toggle_history_mode(self): def toggle_history_mode(self):
"""切换历史展示模式""" """切换历史展示模式"""
@ -414,8 +419,125 @@ class Maze:
self.encountered_bosses = set() self.encountered_bosses = set()
self.encountered_mechanisms = set() self.encountered_mechanisms = set()
# 重置贪心算法相关状态
if hasattr(self, 'greedy_path'):
delattr(self, 'greedy_path')
if hasattr(self, 'greedy_result'):
delattr(self, 'greedy_result')
if hasattr(self, 'greedy_step'):
delattr(self, 'greedy_step')
if hasattr(self, 'is_greedy_path_complete'):
delattr(self, 'is_greedy_path_complete')
# 显示相关状态 # 显示相关状态
self.grid = [] self.grid = []
print("所有状态已重置") print("所有状态已重置")
def run_greedy_search(self):
"""运行3x3视野贪心搜索算法"""
if not self.grid:
print("没有迷宫数据,无法运行贪心搜索")
return False
try:
# 创建贪心算法实例
algorithm = Greedy3x3Algorithm(self.grid, debug=True)
# 运行算法
result = algorithm.run()
# 将结果转换为路径格式 (y, x)
self.greedy_path = result['path_yx_format']
self.greedy_result = result
self.greedy_step = 0
self.is_greedy_path_complete = False
print(f"贪心搜索完成!")
print(f"收集资源数量: {result['resources_count']}")
print(f"资源总价值: {result['total_value']}")
print(f"移动步数: {result['total_moves']}")
print(f"路径长度: {len(self.greedy_path)}")
return True
except Exception as e:
print(f"贪心搜索失败: {e}")
return False
def next_greedy_step(self):
"""显示贪心路径的下一步"""
if not hasattr(self, 'greedy_path') or not self.greedy_path:
return False
if self.greedy_step < len(self.greedy_path):
self.greedy_step += 1
if self.greedy_step >= len(self.greedy_path):
self.is_greedy_path_complete = True
return True
return False
def reset_greedy_path(self):
"""重置贪心路径显示"""
self.greedy_step = 0
self.is_greedy_path_complete = False
def get_greedy_progress(self):
"""获取贪心路径进度信息"""
if not hasattr(self, 'greedy_path') or not self.greedy_path:
return "贪心路径: 未生成"
if hasattr(self, 'greedy_result'):
result = self.greedy_result
return (f"贪心路径: {self.greedy_step}/{len(self.greedy_path)} | "
f"资源: {result['resources_count']} | "
f"价值: {result['total_value']}")
else:
return f"贪心路径: {self.greedy_step}/{len(self.greedy_path)}"
def _draw_greedy_path(self, screen, tile_size):
"""绘制贪心算法路径"""
if not self.greedy_path:
return
# 绘制已走过的路径
for i in range(min(self.greedy_step, len(self.greedy_path))):
y, x = self.greedy_path[i]
# 绘制路径点
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
# 起点用绿色圆圈
if i == 0:
pygame.draw.circle(screen, (0, 255, 0), center, tile_size // 4, 3)
# 当前位置用红色圆圈
elif i == self.greedy_step - 1:
pygame.draw.circle(screen, (255, 0, 0), center, tile_size // 4, 3)
# 其他位置用蓝色小圆点
else:
pygame.draw.circle(screen, (0, 0, 255), center, tile_size // 6)
# 绘制路径连线
if i > 0:
prev_y, prev_x = self.greedy_path[i - 1]
prev_center = (prev_x * tile_size + tile_size // 2, prev_y * tile_size + tile_size // 2)
pygame.draw.line(screen, (100, 100, 255), prev_center, center, 2)
# 如果有贪心算法结果,绘制收集的资源位置
if hasattr(self, 'greedy_result') and self.greedy_result:
for resource in self.greedy_result['collected_resources']:
pos_x, pos_y = resource['position'] # 注意:贪心算法返回的是(x, y)格式
center = (pos_x * tile_size + tile_size // 2, pos_y * tile_size + tile_size // 2)
# 用金色圆圈标记已收集的资源
if resource['value'] > 0:
pygame.draw.circle(screen, (255, 215, 0), center, tile_size // 3, 2)
else:
pygame.draw.circle(screen, (255, 100, 100), center, tile_size // 3, 2)
# 显示资源价值
font = pygame.font.SysFont(None, max(12, tile_size // 4))
value_text = font.render(str(resource['value']), True, (255, 255, 255))
text_rect = value_text.get_rect(center=center)
screen.blit(value_text, text_rect)

337
strict_3x3_greedy.py Normal file
View File

@ -0,0 +1,337 @@
import copy
from collections import deque
class Strict3x3GreedyCollector:
"""
严格的3x3视野贪心资源收集器
每次移动时只考虑3x3视野范围内的资源
如果视野内没有资源则随机移动探索
"""
def __init__(self, maze, start_pos=None, end_pos=None):
"""初始化收集器"""
self.original_maze = copy.deepcopy(maze)
self.maze = copy.deepcopy(maze)
self.rows = len(maze)
self.cols = len(maze[0]) if self.rows > 0 else 0
# 寻找起始位置和目标位置
self.start_pos = start_pos or self._find_position('s')
self.end_pos = end_pos or self._find_position('e')
if not self.start_pos:
raise ValueError("无法找到起始位置 's'")
if not self.end_pos:
raise ValueError("无法找到目标位置 'e'")
self.current_pos = self.start_pos
self.path = [self.start_pos]
self.collected_resources = []
self.total_value = 0
self.visited_resources = set()
self.explored_positions = set([self.start_pos])
print(f"严格3x3视野模式")
print(f"起始位置: {self.start_pos}")
print(f"目标位置: {self.end_pos}")
def _find_position(self, target):
"""寻找地图中指定字符的位置"""
for i in range(self.rows):
for j in range(self.cols):
if self.maze[i][j].lower() == target.lower():
return (i, j)
return None
def get_3x3_vision(self, pos):
"""获取以pos为中心的3x3视野范围内的所有单元格"""
row, col = pos
vision = {}
# 遍历3x3范围
for dr in range(-1, 2):
for dc in range(-1, 2):
new_row, new_col = row + dr, col + dc
# 检查边界
if 0 <= new_row < self.rows and 0 <= new_col < self.cols:
vision[(new_row, new_col)] = self.maze[new_row][new_col]
return vision
def get_adjacent_cells(self, pos):
"""获取当前位置的上下左右四个相邻位置"""
row, col = pos
adjacent = []
# 上下左右四个方向
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for dr, dc in directions:
new_row, new_col = row + dr, col + dc
# 检查边界和可移动性
if (0 <= new_row < self.rows and
0 <= new_col < self.cols and
self.can_move_to((new_row, new_col))):
adjacent.append((new_row, new_col))
return adjacent
def can_move_to(self, pos):
"""检查是否可以移动到指定位置"""
row, col = pos
cell = self.maze[row][col]
# 不能移动到墙壁
return cell != '1'
def evaluate_resource_value(self, cell):
"""评估资源的价值"""
if cell.startswith('g'):
try:
return int(cell[1:])
except ValueError:
return 0
elif cell.startswith('t'):
try:
return -int(cell[1:])
except ValueError:
return 0
else:
return 0
def find_best_resource_in_3x3_vision(self):
"""
严格在3x3视野范围内找到价值最高的资源
Returns:
tuple: (最佳资源位置, 资源价值) (None, 0)
"""
vision = self.get_3x3_vision(self.current_pos)
best_pos = None
best_value = float('-inf')
for pos, cell in vision.items():
# 跳过已访问的资源
if pos in self.visited_resources:
continue
# 跳过当前位置
if pos == self.current_pos:
continue
# 跳过不可移动的位置
if not self.can_move_to(pos):
continue
# 检查是否可以直接到达(相邻位置)
if pos not in self.get_adjacent_cells(self.current_pos):
continue
# 检查是否为资源
value = self.evaluate_resource_value(cell)
if value != 0 and value > best_value:
best_value = value
best_pos = pos
return best_pos, best_value if best_pos else 0
def find_exploration_target(self):
"""
当视野内没有资源时寻找探索目标
优先选择未探索过的位置
"""
adjacent = self.get_adjacent_cells(self.current_pos)
# 优先选择未探索的位置
unexplored = [pos for pos in adjacent if pos not in self.explored_positions]
if unexplored:
return unexplored[0] # 选择第一个未探索的位置
# 如果所有相邻位置都探索过,选择任意一个
if adjacent:
return adjacent[0]
return None
def collect_resource(self, pos):
"""收集指定位置的资源"""
row, col = pos
cell = self.maze[row][col]
value = self.evaluate_resource_value(cell)
if value != 0:
self.collected_resources.append({
'position': pos,
'type': cell,
'value': value
})
self.total_value += value
self.visited_resources.add(pos)
print(f"收集资源: 位置{pos}, 类型{cell}, 价值{value}, 总价值{self.total_value}")
def run_strict_3x3_collection(self, max_moves=1000):
"""
运行严格3x3视野贪心资源收集算法
Args:
max_moves: 最大移动步数防止无限循环
Returns:
dict: 包含路径收集的资源等信息
"""
print("\\n开始严格3x3视野贪心资源收集...")
moves = 0
stuck_count = 0 # 连续无法找到资源的次数
max_stuck = 20 # 最大连续无资源次数
while moves < max_moves and stuck_count < max_stuck:
moves += 1
# 在3x3视野内寻找最佳资源
best_resource_pos, best_value = self.find_best_resource_in_3x3_vision()
if best_resource_pos is not None:
print(f"{moves}步: 发现视野内资源 位置{best_resource_pos}, 价值{best_value}")
# 移动到资源位置并收集
self.current_pos = best_resource_pos
self.path.append(best_resource_pos)
self.explored_positions.add(best_resource_pos)
self.collect_resource(best_resource_pos)
stuck_count = 0 # 重置无资源计数
else:
# 视野内没有资源,进行探索性移动
exploration_target = self.find_exploration_target()
if exploration_target:
print(f"{moves}步: 视野内无资源,探索移动到 {exploration_target}")
self.current_pos = exploration_target
self.path.append(exploration_target)
self.explored_positions.add(exploration_target)
stuck_count += 1
else:
print(f"{moves}步: 无法进行任何移动,结束收集")
break
if moves >= max_moves:
print(f"达到最大移动步数 {max_moves},结束收集")
elif stuck_count >= max_stuck:
print(f"连续 {max_stuck} 步未找到资源,结束收集")
print("严格3x3视野资源收集完成")
return self.get_collection_result()
def get_collection_result(self):
"""获取收集结果"""
return {
'path': self.path.copy(),
'collected_resources': self.collected_resources.copy(),
'total_value': self.total_value,
'total_moves': len(self.path) - 1,
'resources_count': len(self.collected_resources),
'start_pos': self.start_pos,
'end_pos': self.end_pos,
'final_pos': self.current_pos,
'explored_positions': len(self.explored_positions)
}
def print_result_summary(self):
"""打印收集结果摘要"""
result = self.get_collection_result()
print("\\n=== 严格3x3视野贪心收集结果摘要 ===")
print(f"起始位置: {result['start_pos']}")
print(f"最终位置: {result['final_pos']}")
print(f"总移动步数: {result['total_moves']}")
print(f"探索位置数: {result['explored_positions']}")
print(f"收集资源数量: {result['resources_count']}")
print(f"资源总价值: {result['total_value']}")
print("\\n收集的资源详情:")
for i, resource in enumerate(result['collected_resources'], 1):
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
# 显示路径的关键点
path_points = result['path']
if len(path_points) <= 10:
path_str = ' -> '.join(map(str, path_points))
else:
path_str = f"{path_points[0]} -> ... -> {path_points[-1]} (共{len(path_points)}个位置)"
print(f"\\n移动路径: {path_str}")
def visualize_path_on_maze(self):
"""在迷宫上可视化移动路径"""
visual_maze = copy.deepcopy(self.original_maze)
# 标记路径
for i, pos in enumerate(self.path):
row, col = pos
if pos == self.start_pos:
visual_maze[row][col] = 'S' # 起点
elif pos in [r['position'] for r in self.collected_resources]:
# 已收集的资源位置
visual_maze[row][col] = '*'
elif i == len(self.path) - 1:
# 最终位置
visual_maze[row][col] = 'F'
else:
# 路径点
visual_maze[row][col] = '.'
return visual_maze
def print_visual_maze(self):
"""打印可视化的迷宫"""
visual_maze = self.visualize_path_on_maze()
print("\\n=== 严格3x3视野路径可视化迷宫 ===")
print("S: 起点, F: 终点, *: 已收集资源, .: 路径")
for row in visual_maze:
print(' '.join(f"{cell:>2}" for cell in row))
def compare_algorithms():
"""比较不同算法的效果"""
# 创建一个更大的示例迷宫
demo_maze = [
['s', '0', 'g5', '1', 't3', '0', 'g8'],
['0', '1', '0', '0', 'g2', '1', '0'],
['g3', '0', '1', 't2', '0', '0', 'g6'],
['0', 't1', '0', '0', 'g4', '1', '0'],
['1', '0', 'g1', '0', '0', '0', 't5'],
['0', 'g7', '0', '1', '0', 'g9', '0'],
['t4', '0', '0', '0', '1', '0', 'e']
]
print("=== 算法比较演示 ===")
print("迷宫说明:")
print(" s: 起点, e: 终点")
print(" g数字: 金币资源 (正收益)")
print(" t数字: 陷阱资源 (负收益)")
print(" 0: 可通行路径, 1: 墙壁")
print("\\n原始迷宫:")
for row in demo_maze:
print(' '.join(f"{cell:>2}" for cell in row))
print("\\n" + "="*60)
print("严格3x3视野贪心算法:")
print("="*60)
# 运行严格3x3视野算法
strict_collector = Strict3x3GreedyCollector(demo_maze)
strict_result = strict_collector.run_strict_3x3_collection()
strict_collector.print_result_summary()
strict_collector.print_visual_maze()
return strict_collector, strict_result
if __name__ == "__main__":
# 运行比较演示
strict_collector, strict_result = compare_algorithms()

413
tanxin.py
View File

@ -1,8 +1,8 @@
import math import math
from maze import * from maze import *
import math import math
import copy
import math from collections import deque
class GreedyPlayer: class GreedyPlayer:
@ -173,7 +173,338 @@ class GreedyPlayer:
return self.total_reward return self.total_reward
class Greedy3x3ResourceCollector:
"""
基于3x3视野的贪心资源收集器
每次移动时选择3x3视野范围内最高价值的资源
只能进行上下左右移动
"""
def __init__(self, map_data, start=None, end=None):
"""
初始化3x3视野贪心资源收集器
Args:
map_data: 迷宫地图2D列表 (注意这里是[y][x]格式)
start: 起始位置 (x, y)如果为None则自动寻找
end: 目标位置 (x, y)如果为None则自动寻找
"""
self.original_map = copy.deepcopy(map_data)
self.map_data = copy.deepcopy(map_data)
self.rows = len(map_data)
self.cols = len(map_data[0]) if self.rows > 0 else 0
# 寻找起始位置和目标位置
self.start = start or self._find_position('s')
self.end = end or self._find_position('e')
if not self.start:
raise ValueError("无法找到起始位置 's'")
if not self.end:
raise ValueError("无法找到目标位置 'e'")
self.current_pos = self.start
self.path = [self.start]
self.collected_resources = []
self.total_value = 0
self.visited_resources = set()
self.explored_positions = set([self.start])
print(f"3x3视野贪心算法初始化")
print(f"起始位置: {self.start}")
print(f"目标位置: {self.end}")
def _find_position(self, target):
"""寻找地图中指定字符的位置,返回(x, y)格式"""
for y in range(self.rows):
for x in range(self.cols):
if self.map_data[y][x].lower() == target.lower():
return (x, y)
return None
def get_3x3_vision(self, pos):
"""
获取以pos为中心的3x3视野范围内的所有单元格
Args:
pos: 当前位置 (x, y)
Returns:
dict: {(x, y): cell_value} 形式的字典
"""
x, y = pos
vision = {}
# 遍历3x3范围
for dx in range(-1, 2):
for dy in range(-1, 2):
new_x, new_y = x + dx, y + dy
# 检查边界
if 0 <= new_x < self.cols and 0 <= new_y < self.rows:
vision[(new_x, new_y)] = self.map_data[new_y][new_x]
return vision
def get_adjacent_cells(self, pos):
"""
获取当前位置的上下左右四个相邻位置
Args:
pos: 当前位置 (x, y)
Returns:
list: 可移动的相邻位置列表
"""
x, y = pos
adjacent = []
# 上下左右四个方向
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # 上、下、左、右
for dx, dy in directions:
new_x, new_y = x + dx, y + dy
# 检查边界和可移动性
if (0 <= new_x < self.cols and
0 <= new_y < self.rows and
self.can_move_to((new_x, new_y))):
adjacent.append((new_x, new_y))
return adjacent
def can_move_to(self, pos):
"""
检查是否可以移动到指定位置
Args:
pos: 目标位置 (x, y)
Returns:
bool: 是否可以移动
"""
x, y = pos
cell = self.map_data[y][x]
# 不能移动到墙壁
if cell == '1':
return False
return True
def evaluate_resource_value(self, cell):
"""
评估资源的价值
Args:
cell: 单元格内容
Returns:
int: 资源价值正数表示收益负数表示损失
"""
if cell.startswith('g'):
# 金币资源,提取数值
try:
return int(cell[1:])
except ValueError:
return 0
elif cell.startswith('t'):
# 陷阱资源,提取数值并取负
try:
return -int(cell[1:])
except ValueError:
return 0
else:
# 其他类型单元格没有资源价值
return 0
def find_best_resource_in_3x3_vision(self):
"""
在3x3视野范围内找到价值最高的可到达资源
Returns:
tuple: (最佳资源位置, 资源价值) (None, 0)
"""
vision = self.get_3x3_vision(self.current_pos)
best_pos = None
best_value = float('-inf')
# 首先尝试找正价值资源
for pos, cell in vision.items():
# 跳过已访问的资源
if pos in self.visited_resources:
continue
# 跳过当前位置
if pos == self.current_pos:
continue
# 跳过不可移动的位置
if not self.can_move_to(pos):
continue
# 检查是否可以直接到达(相邻位置)
if pos not in self.get_adjacent_cells(self.current_pos):
continue
# 检查是否为资源
value = self.evaluate_resource_value(cell)
if value > 0 and value > best_value: # 优先选择正价值资源
best_value = value
best_pos = pos
# 如果没有正价值资源,考虑负价值资源(选择损失最小的)
if best_pos is None:
for pos, cell in vision.items():
if pos in self.visited_resources or pos == self.current_pos:
continue
if not self.can_move_to(pos):
continue
if pos not in self.get_adjacent_cells(self.current_pos):
continue
value = self.evaluate_resource_value(cell)
if value < 0 and value > best_value: # 选择损失最小的陷阱
best_value = value
best_pos = pos
return best_pos, best_value if best_pos else 0
def find_exploration_target(self):
"""
当视野内没有资源时寻找探索目标
优先选择未探索过的位置
"""
adjacent = self.get_adjacent_cells(self.current_pos)
# 优先选择未探索的位置
unexplored = [pos for pos in adjacent if pos not in self.explored_positions]
if unexplored:
return unexplored[0] # 选择第一个未探索的位置
# 如果所有相邻位置都探索过,选择任意一个
if adjacent:
return adjacent[0]
return None
def collect_resource(self, pos):
"""
收集指定位置的资源
Args:
pos: 资源位置 (x, y)
"""
x, y = pos
cell = self.map_data[y][x]
value = self.evaluate_resource_value(cell)
if value != 0:
self.collected_resources.append({
'position': pos,
'type': cell,
'value': value
})
self.total_value += value
self.visited_resources.add(pos)
print(f"收集资源: 位置{pos}, 类型{cell}, 价值{value}, 总价值{self.total_value}")
def run_3x3_greedy_collection(self, max_moves=1000):
"""
运行3x3视野贪心资源收集算法
Args:
max_moves: 最大移动步数防止无限循环
Returns:
dict: 包含路径收集的资源等信息
"""
print("\\n开始3x3视野贪心资源收集...")
moves = 0
stuck_count = 0 # 连续无法找到资源的次数
max_stuck = 20 # 最大连续无资源次数
while moves < max_moves and stuck_count < max_stuck:
moves += 1
# 在3x3视野内寻找最佳资源
best_resource_pos, best_value = self.find_best_resource_in_3x3_vision()
if best_resource_pos is not None:
print(f"{moves}步: 发现视野内资源 位置{best_resource_pos}, 价值{best_value}")
# 移动到资源位置并收集
self.current_pos = best_resource_pos
self.path.append(best_resource_pos)
self.explored_positions.add(best_resource_pos)
self.collect_resource(best_resource_pos)
stuck_count = 0 # 重置无资源计数
else:
# 视野内没有资源,进行探索性移动
exploration_target = self.find_exploration_target()
if exploration_target:
print(f"{moves}步: 视野内无资源,探索移动到 {exploration_target}")
self.current_pos = exploration_target
self.path.append(exploration_target)
self.explored_positions.add(exploration_target)
stuck_count += 1
else:
print(f"{moves}步: 无法进行任何移动,结束收集")
break
if moves >= max_moves:
print(f"达到最大移动步数 {max_moves},结束收集")
elif stuck_count >= max_stuck:
print(f"连续 {max_stuck} 步未找到资源,结束收集")
print("3x3视野资源收集完成")
return self.get_collection_result()
def get_collection_result(self):
"""获取收集结果"""
return {
'path': self.path.copy(),
'collected_resources': self.collected_resources.copy(),
'total_value': self.total_value,
'total_moves': len(self.path) - 1,
'resources_count': len(self.collected_resources),
'start_pos': self.start,
'end_pos': self.end,
'final_pos': self.current_pos,
'explored_positions': len(self.explored_positions)
}
def get_path(self):
"""返回路径,转换为(y, x)格式以兼容现有代码"""
# 将(x, y)格式的路径转换为(y, x)格式
return [(y, x) for (x, y) in self.path]
def get_total_reward(self):
"""返回总收益"""
return self.total_value
def add_path_to_map(self):
"""在地图上标记路径"""
marked_map = [row.copy() for row in self.map_data]
# 标记路径点
for i, (x, y) in enumerate(self.path):
if marked_map[y][x] == 's':
marked_map[y][x] = 'S' # 标记起点
elif marked_map[y][x] == 'e':
marked_map[y][x] = 'E' # 标记终点
elif (x, y) in [r['position'] for r in self.collected_resources]:
marked_map[y][x] = '*' # 标记已收集资源
else:
marked_map[y][x] = '.' # 标记路径点
self.marked_map = marked_map
return marked_map
# 使用示例 # 使用示例
@ -191,19 +522,95 @@ def main():
map_data = obj.read_csv() map_data = obj.read_csv()
see = MazeGenerator(1,filename ='demo.csv') see = MazeGenerator(1,filename ='demo.csv')
see.read_from_csv() see.read_from_csv()
print("=== 原始迷宫 ===")
see.print_maze() see.print_maze()
print("\\n" + "="*60)
print("使用传统贪心算法:")
print("="*60)
player = GreedyPlayer(map_data) player = GreedyPlayer(map_data)
player.find_path() player.find_path()
see.maze = player.marked_map see.maze = player.marked_map
see.print_maze() see.print_maze()
print(f"传统贪心算法总收益: {player.get_total_reward()}")
print("\\n" + "="*60)
print("使用3x3视野贪心算法:")
print("="*60)
# 使用新的3x3视野算法
greedy_3x3 = Greedy3x3ResourceCollector(map_data)
result = greedy_3x3.run_3x3_greedy_collection()
# 显示结果
see.maze = greedy_3x3.add_path_to_map()
see.print_maze()
print(f"\\n3x3视野算法结果:")
print(f" 总移动步数: {result['total_moves']}")
print(f" 收集资源数量: {result['resources_count']}")
print(f" 资源总价值: {result['total_value']}")
print(f" 探索位置数: {result['explored_positions']}")
print("\\n收集的资源详情:")
for i, resource in enumerate(result['collected_resources'], 1):
print(f" {i}. 位置{resource['position']}: {resource['type']} (价值: {resource['value']})")
def demo_3x3_greedy():
"""演示3x3视野贪心算法"""
# 创建一个示例迷宫
demo_maze = [
['s', '0', 'g5', '1', 't3'],
['0', '1', '0', '0', 'g2'],
['g3', '0', '1', 't2', '0'],
['0', 't1', '0', '0', 'g4'],
['1', '0', 'g1', '0', 'e']
]
print("=== 3x3视野贪心算法演示 ===")
print("迷宫说明:")
print(" s: 起点, e: 终点")
print(" g数字: 金币资源 (正收益)")
print(" t数字: 陷阱资源 (负收益)")
print(" 0: 可通行路径, 1: 墙壁")
print("\\n原始迷宫:")
for row in demo_maze:
print(' '.join(f"{cell:>2}" for cell in row))
# 使用3x3视野贪心算法
collector = Greedy3x3ResourceCollector(demo_maze)
result = collector.run_3x3_greedy_collection()
# 显示标记后的迷宫
marked_maze = collector.add_path_to_map()
print("\\n标记路径后的迷宫:")
print("S: 起点, E: 终点, *: 已收集资源, .: 路径")
for row in marked_maze:
print(' '.join(f"{cell:>2}" for cell in row))
print(f"\\n算法结果:")
print(f" 总移动步数: {result['total_moves']}")
print(f" 收集资源数量: {result['resources_count']}")
print(f" 资源总价值: {result['total_value']}")
return collector, result
if __name__ == "__main__": if __name__ == "__main__":
main() # 运行演示
print("选择运行模式:")
print("1. 完整迷宫生成和算法比较")
print("2. 简单3x3视野算法演示")
choice = input("请输入选择 (1 或 2默认为 2): ").strip()
if choice == "1":
main()
else:
demo_3x3_greedy()