maze_python/simple_save_manager.py

576 lines
21 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 csv
import json
import os
from datetime import datetime
from config import DEFAULT_MAZE_FILE
class SimpleSaveManager:
"""简化的存档管理器 - 支持JSON和CSV格式的迷宫"""
def __init__(self):
self.save_directory = "saves"
self.ensure_save_directory()
def ensure_save_directory(self):
"""确保存档目录存在"""
if not os.path.exists(self.save_directory):
os.makedirs(self.save_directory)
def save_maze_with_path(self, maze_instance, save_name=None, format_type="json"):
"""
保存包含路径信息的迷宫到文件
Args:
maze_instance: Maze类的实例
save_name: 存档名称如果为None则使用时间戳
format_type: 文件格式 "json""csv"
Returns:
str: 保存的文件路径
"""
if save_name is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
save_name = f"maze_save_{timestamp}"
# 根据格式类型确定文件扩展名
if format_type.lower() == "json":
if not save_name.endswith('.json'):
save_name += '.json'
return self._save_maze_json(maze_instance, save_name)
else:
if not save_name.endswith('.csv'):
save_name += '.csv'
return self._save_maze_csv(maze_instance, save_name)
def create_path_grid(self, maze_instance):
"""
创建包含路径信息的网格
Args:
maze_instance: Maze类的实例
Returns:
list: 包含路径信息的二维网格
"""
if not maze_instance.generater.maze or not maze_instance.full_path:
return maze_instance.generater.maze if maze_instance.generater.maze else []
# 从原始迷宫开始
path_grid = [row[:] for row in maze_instance.generater.maze] # 深拷贝
# 将完整路径信息添加到网格中
for idx, (y, x) in enumerate(maze_instance.full_path):
current_cell = path_grid[y][x]
path_info = f"p{idx + 1}" # 路径编号从1开始
if current_cell.startswith('s'):
# 起点:保留起点标记,但也添加路径信息
path_grid[y][x] = f"s{path_info}"
elif current_cell.startswith('e'):
# 终点:保留终点标记,但也添加路径信息
path_grid[y][x] = f"e{path_info}"
elif current_cell.startswith('g'):
# 金币g10p31 格式
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('t'):
# 陷阱t19p31 格式
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('l'):
# 机关l24p31 格式
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('b'):
# bossb92p31 格式
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell == '0':
# 空地直接是p31
path_grid[y][x] = path_info
else:
# 其他情况也添加路径信息
path_grid[y][x] = f"{current_cell}{path_info}"
return path_grid
def load_maze_from_file(self, file_path):
"""
从文件加载迷宫,自动检测文件格式
Args:
file_path: 文件路径
Returns:
dict: 包含迷宫数据和路径信息的字典
"""
if file_path.endswith('.json'):
return self.load_maze_from_json(file_path)
elif file_path.endswith('.csv'):
return self.load_maze_from_csv(file_path)
else:
print(f"不支持的文件格式: {file_path}")
return None
def load_maze_from_json(self, json_file):
"""
从JSON文件加载迷宫
Args:
json_file: JSON文件路径
Returns:
dict: 包含迷宫数据和路径信息的字典
"""
try:
with open(json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 检查数据结构
if 'maze' not in data:
print("JSON文件格式错误缺少maze字段")
return None
# 转换JSON格式到内部格式
internal_maze = self.convert_from_json_format(data['maze'])
# 检查并修复迷宫边界
internal_maze, boundary_added = self.ensure_maze_boundaries(internal_maze)
result = {
'original_grid': internal_maze,
'path_grid': internal_maze, # 对于JSON格式初始时路径网格与原始网格相同
'path_sequence': [],
'path_positions': {},
'metadata': data.get('metadata', {}),
'format': 'json'
}
# 如果有路径数据,也加载路径信息
if 'path_data' in data:
raw_path = data['path_data'].get('full_path', [])
# 确保路径数据是元组格式 (JSON会将元组转换为列表)
path_sequence = [tuple(pos) if isinstance(pos, list) else pos for pos in raw_path]
# 如果添加了边界,调整路径坐标
if boundary_added:
path_sequence = self.adjust_path_coordinates(path_sequence, boundary_added)
result['path_sequence'] = path_sequence
result['path_positions'] = {i+1: pos for i, pos in enumerate(path_sequence)}
# 如果有路径,创建包含路径的网格
if result['path_sequence']:
result['path_grid'] = self.create_path_grid_from_data(internal_maze, result['path_sequence'])
print(f"成功从 {os.path.abspath(json_file)} 加载迷宫")
if result['path_sequence']:
print(f"路径长度: {len(result['path_sequence'])}")
return result
except Exception as e:
print(f"JSON加载失败: {str(e)}")
return None
def load_maze_from_csv(self, csv_file):
"""
从CSV文件加载迷宫解析路径信息
Args:
csv_file: CSV文件路径
Returns:
dict: 包含迷宫数据和路径信息的字典
"""
try:
with open(csv_file, 'r', newline='', encoding='utf-8') as f:
reader = csv.reader(f)
grid = [list(row) for row in reader]
# 检查并修复迷宫边界
grid, boundary_added = self.ensure_maze_boundaries(grid)
# 解析路径信息
path_data = self.extract_path_from_grid(grid)
# 如果添加了边界但有路径数据,需要调整路径坐标
# 注意对于CSV路径信息是从网格中提取的添加边界后坐标已经自动调整了
# 所以这里不需要额外调整
# 创建原始迷宫(移除路径信息)
original_grid = self.create_original_grid(grid)
result = {
'original_grid': original_grid,
'path_grid': grid,
'path_sequence': path_data['path_sequence'],
'path_positions': path_data['path_positions'],
'format': 'csv'
}
print(f"成功从 {os.path.abspath(csv_file)} 加载迷宫")
print(f"路径长度: {len(path_data['path_sequence'])}")
return result
except Exception as e:
print(f"CSV加载失败: {str(e)}")
return None
def create_path_grid_from_data(self, original_grid, path_sequence):
"""从原始网格和路径序列创建包含路径信息的网格"""
path_grid = [row[:] for row in original_grid] # 深拷贝
for idx, (y, x) in enumerate(path_sequence):
if y >= len(path_grid) or x >= len(path_grid[0]):
continue
current_cell = path_grid[y][x]
path_info = f"p{idx + 1}" # 路径编号从1开始
if current_cell.startswith('s'):
path_grid[y][x] = f"s{path_info}"
elif current_cell.startswith('e'):
path_grid[y][x] = f"e{path_info}"
elif current_cell.startswith('g'):
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('t'):
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('l'):
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell.startswith('b'):
path_grid[y][x] = f"{current_cell}{path_info}"
elif current_cell == '0':
path_grid[y][x] = path_info
else:
path_grid[y][x] = f"{current_cell}{path_info}"
return path_grid
def extract_path_from_grid(self, grid):
"""从网格中提取路径信息"""
path_positions = {} # {step_number: (y, x)}
for y in range(len(grid)):
for x in range(len(grid[y])):
cell = grid[y][x]
# 查找所有路径信息 p数字
import re
# 使用正则表达式找到所有p后跟数字的模式
path_matches = re.findall(r'p(\d+)', cell)
for path_match in path_matches:
try:
step_number = int(path_match)
path_positions[step_number] = (y, x)
except ValueError:
continue
# 按步数排序创建路径序列
path_sequence = []
for step in sorted(path_positions.keys()):
path_sequence.append(path_positions[step])
return {
'path_sequence': path_sequence,
'path_positions': path_positions
}
def create_original_grid(self, path_grid):
"""从包含路径的网格创建原始迷宫网格"""
original_grid = []
for row in path_grid:
original_row = []
for cell in row:
# 移除路径信息,恢复原始状态
if 'p' in cell:
p_index = cell.find('p')
original_cell = cell[:p_index] if p_index > 0 else '0'
else:
original_cell = cell
original_row.append(original_cell)
original_grid.append(original_row)
return original_grid
def get_save_list(self):
"""获取所有存档文件列表支持JSON和CSV格式"""
saves = []
if os.path.exists(self.save_directory):
for filename in os.listdir(self.save_directory):
if filename.endswith('.csv') or filename.endswith('.json'):
file_path = os.path.join(self.save_directory, filename)
try:
stat = os.stat(file_path)
# 确定文件格式
file_format = 'json' if filename.endswith('.json') else 'csv'
# 移除扩展名作为名称
name = filename.replace('.json', '').replace('.csv', '')
saves.append({
'filename': filename,
'path': file_path,
'name': name,
'save_time': datetime.fromtimestamp(stat.st_mtime).isoformat(),
'size': stat.st_size,
'format': file_format
})
except:
continue
return sorted(saves, key=lambda x: x['save_time'], reverse=True)
def delete_save(self, save_file):
"""删除存档文件"""
try:
if os.path.exists(save_file):
os.remove(save_file)
print(f"存档已删除: {save_file}")
return True
except Exception as e:
print(f"删除失败: {str(e)}")
return False
def _save_maze_json(self, maze_instance, save_name):
"""保存迷宫为JSON格式"""
save_file = os.path.join(self.save_directory, save_name)
try:
# 转换迷宫格式
json_maze = self.convert_to_json_format(maze_instance)
# 创建保存数据结构
save_data = {
"maze": json_maze,
"metadata": {
"save_name": save_name.replace('.json', ''),
"save_time": datetime.now().isoformat(),
"maze_size": maze_instance.size,
"path_length": len(maze_instance.full_path) if maze_instance.full_path else 0
}
}
# 如果有路径信息,也保存路径
if maze_instance.full_path:
save_data["path_data"] = {
"full_path": maze_instance.full_path,
"current_step": maze_instance.path_step,
"is_path_complete": maze_instance.is_path_complete
}
# 保存为JSON文件
with open(save_file, 'w', encoding='utf-8') as f:
json.dump(save_data, f, indent=2, ensure_ascii=False)
print(f"迷宫已保存至: {os.path.abspath(save_file)}")
return save_file
except Exception as e:
print(f"JSON保存失败: {str(e)}")
return None
def _save_maze_csv(self, maze_instance, save_name):
"""保存迷宫为CSV格式原有功能"""
save_file = os.path.join(self.save_directory, save_name)
try:
# 生成包含路径信息的网格
path_grid = self.create_path_grid(maze_instance)
# 保存到CSV文件
with open(save_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
for row in path_grid:
writer.writerow(row)
print(f"迷宫已保存至: {os.path.abspath(save_file)}")
return save_file
except Exception as e:
print(f"CSV保存失败: {str(e)}")
return None
def convert_to_json_format(self, maze_instance):
"""
将内部迷宫格式转换为JSON格式
内部格式 -> JSON格式:
'1' -> '#' (墙壁)
'0' -> ' ' (通路)
's' -> 'S' (起点)
'e' -> 'E' (终点)
'g数字' -> 'G' (金币)
't数字' -> 'T' (陷阱)
'l数字' -> 'L' (机关)
'b数字' -> 'B' (BOSS)
"""
if not maze_instance.generater.maze:
return []
json_maze = []
for row in maze_instance.generater.maze:
json_row = []
for cell in row:
cell_str = str(cell)
if cell_str == '1':
json_row.append('#')
elif cell_str == '0':
json_row.append(' ')
elif cell_str == 's':
json_row.append('S')
elif cell_str == 'e':
json_row.append('E')
elif cell_str.startswith('g'):
json_row.append('G')
elif cell_str.startswith('t'):
json_row.append('T')
elif cell_str.startswith('l'):
json_row.append('L')
elif cell_str.startswith('b'):
json_row.append('B')
else:
# 未知类型,默认为通路
json_row.append(' ')
json_maze.append(json_row)
return json_maze
def convert_from_json_format(self, json_maze):
"""
将JSON格式转换为内部迷宫格式
JSON格式 -> 内部格式:
'#' -> '1' (墙壁)
' ' -> '0' (通路)
'S' -> 's' (起点)
'E' -> 'e' (终点)
'G' -> 'g10' (金币默认值10)
'T' -> 't15' (陷阱默认值15)
'L' -> 'l20' (机关默认值20)
'B' -> 'b50' (BOSS默认值50)
"""
internal_maze = []
for row in json_maze:
internal_row = []
for cell in row:
if cell == '#':
internal_row.append('1')
elif cell == ' ':
internal_row.append('0')
elif cell == 'S':
internal_row.append('s')
elif cell == 'E':
internal_row.append('e')
elif cell == 'G':
internal_row.append('g30') # 默认金币值
elif cell == 'T':
internal_row.append('t20') # 默认陷阱值
elif cell == 'L':
internal_row.append('l20') # 默认机关值
elif cell == 'B':
internal_row.append('b') # 默认BOSS值
else:
# 未知类型,默认为通路
internal_row.append('0')
internal_maze.append(internal_row)
return internal_maze
def ensure_maze_boundaries(self, maze_grid):
"""
确保迷宫四周都是墙壁,如果不是则自动添加边界墙
Args:
maze_grid: 迷宫网格数据
Returns:
tuple: (修复后的迷宫网格, 是否添加了边界)
"""
if not maze_grid or len(maze_grid) == 0:
return maze_grid, False
# 检查是否需要添加边界
needs_boundary = False
height = len(maze_grid)
width = len(maze_grid[0]) if height > 0 else 0
if width == 0:
return maze_grid, False
# 检查四条边是否全是墙壁('#' 或 '1')
# 上边界
for col in range(width):
if maze_grid[0][col] not in ['#', '1']:
needs_boundary = True
break
# 下边界
if not needs_boundary:
for col in range(width):
if maze_grid[height-1][col] not in ['#', '1']:
needs_boundary = True
break
# 左边界
if not needs_boundary:
for row in range(height):
if maze_grid[row][0] not in ['#', '1']:
needs_boundary = True
break
# 右边界
if not needs_boundary:
for row in range(height):
if maze_grid[row][width-1] not in ['#', '1']:
needs_boundary = True
break
if not needs_boundary:
print("迷宫边界检查通过,无需修复")
return maze_grid, False
print("检测到迷宫边界不完整,自动添加边界墙壁...")
# 创建新的迷宫,四周加上墙壁
new_height = height + 2
new_width = width + 2
new_maze = []
# 添加上边界
new_maze.append(['#'] * new_width)
# 添加中间行(左右加墙)
for row in maze_grid:
new_row = ['#'] + row + ['#']
new_maze.append(new_row)
# 添加下边界
new_maze.append(['#'] * new_width)
print(f"边界修复完成:{width}x{height} -> {new_width}x{new_height}")
return new_maze, True
def adjust_path_coordinates(self, path_sequence, boundary_added):
"""
当添加边界后,调整路径坐标
Args:
path_sequence: 原始路径序列
boundary_added: 是否添加了边界
Returns:
list: 调整后的路径序列
"""
if not boundary_added or not path_sequence:
return path_sequence
# 所有坐标向右下偏移1个单位
adjusted_path = []
for pos in path_sequence:
if isinstance(pos, (list, tuple)) and len(pos) == 2:
adjusted_path.append((pos[0] + 1, pos[1] + 1))
else:
adjusted_path.append(pos) # 保持原样(可能是错误数据)
print(f"路径坐标已调整,共 {len(adjusted_path)} 个点")
return adjusted_path
# 全局简化存档管理器实例
simple_save_manager = SimpleSaveManager()