maze_python/maze.py

422 lines
18 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 pygame
from maze_generator import MazeGenerator
from SourceCollector import SourceCollector
from tanxin import *
from simple_save_manager import simple_save_manager
from config import UI_WIDTH
import time
import random
import json
INPUT_DATA = "/home/guan/dev/python_dev/maze_python/saves/input/input_case_1.json"
class Maze:
def __init__(self, wall_size, maze_size, file_name):
self.wall_size = wall_size
self.maze_size = maze_size
self.size = int(maze_size / wall_size)
self.file_name = file_name
# 动态显示尺寸(会根据实际地图大小调整)
self.actual_display_size = maze_size
self.actual_wall_size = wall_size
self.grid = []
self.generater = MazeGenerator(self.size, self.file_name)
self.source_collector = None
self.path_step = 0 # 当前显示到路径的第几步
self.full_path = [] # 完整路径
self.is_path_complete = False # 路径是否完全显示
# 历史迭代展示相关
self.history_mazes = [] # 迷宫生成历史
self.history_step = 0 # 当前历史步骤
self.show_history = False # 是否正在展示历史
self.history_auto_play = False # 历史自动播放
self.history_timer = 0 # 历史播放计时器
# Boss战斗相关
self.boss_data = [] # Boss血量序列
self.player_skills = [] # 玩家技能序列
# 已遇到的Boss和机关位置记录避免重复触发
self.encountered_bosses = set() # 已遇到的Boss位置 (y, x)
self.encountered_mechanisms = set() # 已遇到的机关位置 (y, x)
def update_display_size(self):
"""根据当前迷宫大小更新显示尺寸"""
if len(self.grid) > 0:
self.size = len(self.grid)
# 计算合适的墙壁大小,确保迷宫不会太大或太小
max_display_size = min(800, UI_WIDTH - 400) # 留出400像素给控制面板
min_wall_size = 20
max_wall_size = 60
# 根据迷宫大小计算墙壁尺寸
ideal_wall_size = max_display_size // self.size
self.actual_wall_size = max(min_wall_size, min(max_wall_size, ideal_wall_size))
self.actual_display_size = self.size * self.actual_wall_size
print(f"迷宫大小: {self.size}x{self.size}, 墙壁尺寸: {self.actual_wall_size}, 显示尺寸: {self.actual_display_size}")
def get_actual_display_size(self):
"""获取当前实际显示尺寸"""
return self.actual_display_size
def generate(self):
# 重置所有状态
self.reset_all_states()
# 生成新迷宫
seed = int(time.time() * 1000) % (2**32)
self.generater.generate(seed=seed)
# 获取生成历史
self.history_mazes = self.generater.get_history_mazes()
# 生成路径
self.source_collector = SourceCollector(maze=self.generater.maze)
self.source_collector.run()
self.full_path = self.source_collector.get_path()
# 在路径生成完成后在路径上放置Boss
if self.full_path and len(self.full_path) > 2:
placed_bosses = self.generater.place_boss_on_path(self.full_path, boss_count=1)
if placed_bosses:
print(f"已在路径上放置 {len(placed_bosses)} 个Boss")
with open(INPUT_DATA, 'r', encoding='utf-8') as f:
file = json.load(f)
# 设置Boss数据和玩家技能
self.boss_data = list(file['B'])
self.player_skills = [list(skill) for skill in file['PlayerSkills']]
print(f"Boss数据: {self.boss_data}")
print(f"玩家技能: {self.player_skills}")
# 在路径上放置机关只放置1个
placed_mechanisms = self.generater.place_mechanisms_on_path(self.full_path, mechanisms_count=1)
if placed_mechanisms:
print(f"已在路径上放置 {len(placed_mechanisms)} 个机关")
# 设置显示状态
self.grid = self.generater.maze # 使用原始迷宫数据
print(self.grid)
self.update_display_size() # 更新显示尺寸
print(f"路径长度: {len(self.full_path)}")
print(f"生成历史步数: {len(self.history_mazes)}")
print("新迷宫生成完成")
def next_path_step(self):
"""显示路径的下一步"""
if self.path_step < len(self.full_path):
# 获取当前要显示的位置
current_y, current_x = self.full_path[self.path_step]
current_position = (current_y, current_x)
# 检查当前位置是否有boss或机关
boss_encountered = False
mechanism_encountered = False
boss_info = None
mechanism_info = None
if self.generater.maze:
current_cell = str(self.generater.maze[current_y][current_x])
# 检查Boss只有在之前没有遇到过这个位置的Boss时才触发
if current_cell.lower().startswith('b') and current_position not in self.encountered_bosses:
boss_encountered = True
self.encountered_bosses.add(current_position) # 记录已遇到的Boss位置
# 如果有boss数据和玩家技能数据创建boss信息
if self.boss_data and self.player_skills:
boss_info = {
'boss_data': self.boss_data,
'player_skills': self.player_skills
}
print(f"遇到Boss位置: ({current_y}, {current_x}), Boss数据: {self.boss_data}, 玩家技能: {self.player_skills}")
else:
print(f"遇到Boss但缺少战斗数据Boss数据: {self.boss_data}, 玩家技能: {self.player_skills}")
boss_info = None
# 检查机关(只有在之前没有遇到过这个位置的机关时才触发)
elif current_cell.lower().startswith('l') and current_position not in self.encountered_mechanisms:
mechanism_encountered = True
self.encountered_mechanisms.add(current_position) # 记录已遇到的机关位置
mechanism_info = {
'position': (current_y, current_x),
'cell_value': current_cell
}
print(f"遇到机关!位置: ({current_y}, {current_x}), 机关值: {current_cell}")
self.path_step += 1
self.update_grid_with_path()
# 返回是否有下一步、是否遇到boss、boss信息、是否遇到机关、机关信息
return True, boss_encountered, boss_info, mechanism_encountered, mechanism_info
return False, False, None, False, None
def reset_path(self):
"""重置路径显示"""
self.path_step = 0
self.is_path_complete = False
self.grid = self.generater.maze if self.generater.maze else []
# 重置已遇到的Boss和机关记录允许重新体验
self.encountered_bosses = set()
self.encountered_mechanisms = set()
print("路径重置Boss和机关遭遇记录已清除")
def auto_advance_path(self):
"""自动推进路径显示"""
if not self.is_path_complete:
has_next, boss_encountered, boss_info = self.next_path_step()
if not has_next:
self.is_path_complete = True
# 注意这里不处理boss遭遇因为这个方法可能不在主循环中调用
return boss_encountered, boss_info
return False, None
def update_grid_with_path(self):
"""根据当前步数更新网格显示"""
if not self.full_path or not self.generater.maze:
return
# 从原始迷宫开始
self.grid = [row[:] for row in self.generater.maze] # 深拷贝
# 只显示到当前步数的路径
for idx in range(min(self.path_step, len(self.full_path))):
y, x = self.full_path[idx]
if self.grid[y][x].startswith('s') or self.grid[y][x].startswith('e'):
continue
if self.grid[y][x].startswith('g') or self.grid[y][x].startswith('t'):
self.grid[y][x] = f"{self.grid[y][x]}p{idx}"
continue
self.grid[y][x] = f"p{idx}"
def export_to_csv(self, filename):
"""导出迷宫到CSV文件兼容旧版本"""
self.generater.export_to_csv(filename=filename)
def save_game(self, save_name=None, format_type="json"):
"""保存包含路径信息的迷宫到文件"""
if len(self.grid) == 0:
print("没有生成迷宫,无法保存")
return None
return simple_save_manager.save_maze_with_path(self, save_name, format_type)
def load_game(self, save_file):
"""从存档文件加载游戏状态支持JSON和CSV格式"""
load_data = simple_save_manager.load_maze_from_file(save_file)
if load_data is None:
return False
try:
# 恢复迷宫数据
self.generater.maze = load_data['original_grid']
self.size = len(load_data['original_grid'])
# 恢复路径数据
self.full_path = load_data['path_sequence']
self.path_step = len(self.full_path) # 显示完整路径
self.is_path_complete = True
# 重新创建SourceCollector以便后续操作
self.source_collector = SourceCollector(maze=self.generater.maze)
# 使用包含路径信息的网格作为显示网格
self.grid = load_data['path_grid']
self.update_display_size() # 更新显示尺寸
# 加载Boss数据和玩家技能
self.boss_data = load_data.get('boss_data', [])
self.player_skills = load_data.get('player_skills', [])
# 读档时不展示历史迭代(清空历史数据)
self.history_mazes = []
self.history_step = 0
self.show_history = False
file_format = load_data.get('format', '未知')
print(f"成功加载游戏状态 ({file_format}格式),路径长度: {len(self.full_path)}")
if self.boss_data:
print(f"Boss数据: {self.boss_data}")
if self.player_skills:
print(f"玩家技能: {self.player_skills}")
print(f"当前显示完整路径")
return True
except Exception as e:
print(f"加载游戏状态失败: {str(e)}")
return False
def get_save_list(self):
"""获取所有可用的存档列表"""
return simple_save_manager.get_save_list()
def draw(self, screen, wall_texture, coin_texture, trap_texture, boss_texture, lock_texture):
if len(self.grid) == 0:
return
# 使用动态计算的墙壁尺寸
tile_size = self.actual_wall_size
# 根据需要缩放贴图
if wall_texture.get_width() != tile_size:
wall_texture = pygame.transform.scale(wall_texture, (tile_size, tile_size))
coin_texture = pygame.transform.scale(coin_texture, (tile_size, tile_size))
trap_texture = pygame.transform.scale(trap_texture, (tile_size, tile_size))
boss_texture = pygame.transform.scale(boss_texture, (tile_size, tile_size))
lock_texture = pygame.transform.scale(lock_texture, (tile_size, tile_size))
for y in range(self.size):
for x in range(self.size):
if self.grid[y][x] == '1':
screen.blit(wall_texture, (x * tile_size, y * tile_size))
continue
if self.grid[y][x].startswith('b'):
screen.blit(boss_texture, (x * tile_size, y * tile_size))
continue
if self.grid[y][x].startswith('l'):
screen.blit(lock_texture, (x * tile_size, y * tile_size))
continue
if self.grid[y][x].startswith('g'):
screen.blit(coin_texture, (x * tile_size, y * tile_size))
font = pygame.font.SysFont(None, tile_size // 2)
path = self.grid[y][x].rfind('p')
if path == -1:
continue
path = self.grid[y][x][path+1:]
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
text = font.render(path, True, (255, 255, 255))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
if self.grid[y][x].startswith('t'):
screen.blit(trap_texture, (x * tile_size, y * tile_size))
font = pygame.font.SysFont(None, tile_size // 2)
path = self.grid[y][x].rfind('p')
if path == -1:
continue
path = self.grid[y][x][path+1:]
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
text = font.render(path, True, (255, 255, 255))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
if self.grid[y][x].startswith('|') or self.grid[y][x].startswith('-'):
font = pygame.font.SysFont(None, tile_size // 2)
num = int(self.grid[y][x][1:])
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
pygame.draw.circle(screen, (255, 215, 0), center, radius)
if num:
text = font.render(str(num), True, (0, 0, 0))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
continue
if self.grid[y][x].startswith('s'):
font = pygame.font.SysFont(None, tile_size // 2)
text = "s"
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
pygame.draw.circle(screen, (255, 215, 0), center, radius)
if text:
text = font.render(text, True, (0, 0, 0))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
continue
if self.grid[y][x].startswith('e'):
font = pygame.font.SysFont(None, tile_size // 2)
text = "e"
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
pygame.draw.circle(screen, (255, 215, 0), center, radius)
if text:
text = font.render(text, True, (0, 0, 0))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
if self.grid[y][x].startswith('p'):
font = pygame.font.SysFont(None, tile_size // 2)
text = self.grid[y][x][1:]
center = (x * tile_size + tile_size // 2, y * tile_size + tile_size // 2)
radius = tile_size // 3
pygame.draw.circle(screen, (255, 215, 0), center, radius)
if text:
text = font.render(text, True, (0, 0, 0))
text_rect = text.get_rect(center=center)
screen.blit(text, text_rect)
# 绘制迷宫边界线(动态位置)
pygame.draw.line(screen, (0, 0, 0), (self.actual_display_size, 0), (self.actual_display_size, self.actual_display_size), 3)
def toggle_history_mode(self):
"""切换历史展示模式"""
if len(self.history_mazes) > 0:
self.show_history = not self.show_history
if self.show_history:
# 切换到历史模式,显示当前历史步骤
self.update_grid_with_history()
else:
# 切换到路径模式,显示当前路径步骤
self.update_grid_with_path()
print(f"切换到{'历史' if self.show_history else '路径'}模式")
def next_history_step(self):
"""显示历史的下一步"""
if self.history_step < len(self.history_mazes) - 1:
self.history_step += 1
self.update_grid_with_history()
return True
return False
def prev_history_step(self):
"""显示历史的上一步"""
if self.history_step > 0:
self.history_step -= 1
self.update_grid_with_history()
return True
return False
def update_grid_with_history(self):
"""根据当前历史步数更新网格显示"""
if not self.history_mazes or self.history_step >= len(self.history_mazes):
return
# 显示指定历史步骤的迷宫状态
self.grid = [row[:] for row in self.history_mazes[self.history_step]] # 深拷贝
def reset_all_states(self):
"""重置所有状态(用于生成新迷宫时)"""
# 历史相关状态
self.history_mazes = []
self.history_step = 0
self.show_history = False
self.history_auto_play = False
self.history_timer = 0
# 路径相关状态
self.path_step = 0
self.is_path_complete = False
self.full_path = []
# 重置已遇到的Boss和机关记录
self.encountered_bosses = set()
self.encountered_mechanisms = set()
# 显示相关状态
self.grid = []
print("所有状态已重置")