maze_python/tanxin.py
2025-07-01 10:12:59 +08:00

618 lines
20 KiB
Python
Raw 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.

import math
from maze import *
import math
import copy
from collections import deque
class GreedyPlayer:
def __init__(self, map_data, start=None, end=None):
"""初始化GreedyPlayer对象"""
self.map_data = map_data
self.rows = len(map_data)
self.cols = len(map_data[0]) if self.rows > 0 else 0
self.start = start
self.end = end
self.path = []
self.total_reward = 0
self.visited = set()
self.marked_map = []
# 如果未指定起点和终点,自动查找
if not self.start or not self.end:
self._find_start_end()
def _find_start_end(self):
"""自动查找地图中的起点(s)和终点(e)"""
for y in range(self.rows):
for x in range(self.cols):
if self.map_data[y][x] == 's' or self.map_data[y][x] == 'S':
self.start = (x, y)
elif self.map_data[y][x] == 'e' or self.map_data[y][x] == 'E':
self.end = (x, y)
print(f"起点: {self.start}, 终点: {self.end}")
def get_visible_cells(self, x, y, visibility=1):
"""获取以(x,y)为中心的上下左右四个方向的单元格信息"""
visible = {}
# 只考虑上下左右四个方向dx或dy为±1另一个为0
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < self.cols and 0 <= ny < self.rows:
cell = self.map_data[ny][nx]
distance = 1 # 上下左右移动距离为1
visible[(nx, ny)] = (cell, distance)
return visible
def evaluate_cell(self, cell, distance):
"""评估单元格的价值,返回奖励/路径的比值"""
if cell == 's' or cell == 'e':
return 0 # 起点和终点不参与资源评估
if cell.startswith('t'):
try:
value = -int(cell[1:]) # t表示损失转为负值
return value / distance
except ValueError:
return 0
elif cell.startswith('g'):
try:
value = int(cell[1:]) # g表示收益转为正值
return value / distance
except ValueError:
return 0
return 0 # 0、l、b等不产生资源价值
def find_path(self):
"""基于贪心策略的路径规划(只能上下左右移动)"""
if not self.start or not self.end:
raise ValueError("地图中未找到起点或终点")
current = self.start
self.path = [current]
self.visited = {current}
self.total_reward = 0
while current != self.end:
x, y = current
visible = self.get_visible_cells(x, y)
best_cell = None
best_value = -float('inf')
for (nx, ny), (cell, distance) in visible.items():
# 跳过已访问的位置
if (nx, ny) in self.visited:
continue
# 只允许在0、t、g、l、b上行走
if cell not in ['0'] and not cell.startswith(('t', 'g', 'l', 'b')):
continue
# 评估单元格价值
value = self.evaluate_cell(cell, distance)
# 终点具有最高优先级
if cell == 'e':
value = float('inf')
# 选择贪心值最大的单元格
if value > best_value:
best_value = value
best_cell = (nx, ny)
# 无法找到可行路径
if best_cell is None:
print("无法找到通往终点的路径!")
break
# 更新当前位置和路径
current = best_cell
self.path.append(current)
self.visited.add(current)
# 更新总收益(跳过起点和终点)
if len(self.path) > 1 and len(self.path) < len(self.path) + 1:
cell = self.map_data[current[1]][current[0]]
if cell.startswith('t'):
self.total_reward -= int(cell[1:])
elif cell.startswith('g'):
self.total_reward += int(cell[1:])
self.add_path_to_map()
return self.path
def add_path_to_map(self):
"""在地图上标记路径,上下移动用|,左右移动用-"""
if not self.path:
print("没有路径可标记")
return
# 创建地图副本,避免修改原始地图
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' # 标记终点
else:
marked_map[y][x] = '*' # 标记路径点
# 标记路径线(上下左右)
for i in range(len(self.path) - 1):
x1, y1 = self.path[i]
x2, y2 = self.path[i + 1]
# 左右移动
if x1 != x2 and y1 == y2:
start, end = (x1, x2) if x1 < x2 else (x2, x1)
for x in range(start, end + 1):
if marked_map[y1][x] not in ['S', 'E']:
marked_map[y1][x] = '-'
# 上下移动
elif y1 != y2 and x1 == x2:
start, end = (y1, y2) if y1 < y2 else (y2, y1)
for y in range(start, end + 1):
if marked_map[y][x1] not in ['S', 'E']:
marked_map[y][x1] = '|'
# 保存标记后的地图
self.marked_map = marked_map
return marked_map
def get_path(self):
"""返回找到的路径"""
return self.path
def get_total_reward(self):
"""返回总收益"""
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
# 使用示例
def main():
obj = MazeGenerator(20,'demo.csv',name="龙脊峡谷迷宫")
obj.generate(
seed=123,
boss_count=2,
traps_range=(5, 10),
mechanisms_range=(3, 7),
skill_traps=8
)
obj.export_to_csv()
map_data = obj.read_csv()
see = MazeGenerator(1,filename ='demo.csv')
see.read_from_csv()
print("=== 原始迷宫 ===")
see.print_maze()
print("\\n" + "="*60)
print("使用传统贪心算法:")
print("="*60)
player = GreedyPlayer(map_data)
player.find_path()
see.maze = player.marked_map
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__":
# 运行演示
print("选择运行模式:")
print("1. 完整迷宫生成和算法比较")
print("2. 简单3x3视野算法演示")
choice = input("请输入选择 (1 或 2默认为 2): ").strip()
if choice == "1":
main()
else:
demo_3x3_greedy()