462 lines
17 KiB
Python
462 lines
17 KiB
Python
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'):
|
||
# boss:b92p31 格式
|
||
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'])
|
||
|
||
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会将元组转换为列表)
|
||
result['path_sequence'] = [tuple(pos) if isinstance(pos, list) else pos for pos in raw_path]
|
||
result['path_positions'] = {i+1: tuple(pos) if isinstance(pos, list) else pos
|
||
for i, pos in enumerate(result['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]
|
||
|
||
# 解析路径信息
|
||
path_data = self.extract_path_from_grid(grid)
|
||
|
||
# 创建原始迷宫(移除路径信息)
|
||
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('g10') # 默认金币值
|
||
elif cell == 'T':
|
||
internal_row.append('t15') # 默认陷阱值
|
||
elif cell == 'L':
|
||
internal_row.append('l20') # 默认机关值
|
||
elif cell == 'B':
|
||
internal_row.append('b50') # 默认BOSS值
|
||
else:
|
||
# 未知类型,默认为通路
|
||
internal_row.append('0')
|
||
internal_maze.append(internal_row)
|
||
|
||
return internal_maze
|
||
|
||
# 全局简化存档管理器实例
|
||
simple_save_manager = SimpleSaveManager()
|