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

491 lines
26 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 pygame import Surface
from pygame.constants import QUIT
from maze import Maze
from draw import Button, Toast
from save_ui import SaveLoadUI
from boss_fight_ui import BossFightUI
from mechanism_ui import MechanismUI
from greedy_3x3_algorithm import Greedy3x3Algorithm
from config import *
import sys
import os
screen: Surface = None # 窗口实例
clock = None # 时钟实例
textFont = None # 字体
def pygameInit(title: str = "pygame"):
"""初始化 pygame"""
pygame.init()
pygame.mixer.init() # 声音初始化
pygame.display.set_caption(title)
global screen, clock, textFont # 修改全局变量
screen = pygame.display.set_mode((UI_WIDTH, UI_HEIGHT))
clock = pygame.time.Clock()
# Initialize font with UTF-8 support
pygame.font.init()
textFont = pygame.font.Font(FONT_FILE, FONT_SIZE)
def create_buttons(button_positions):
"""根据按钮位置创建所有按钮"""
# 创建按钮纹理(只需要一次)
button_start_texture = pygame.image.load(START_BUTTON_IMAGE).convert_alpha()
button_start_texture = pygame.transform.scale(button_start_texture, BUTTON_START_SIZE)
button_save_texture = pygame.image.load(SAVE_BUTTON_IMAGE).convert_alpha()
button_save_texture = pygame.transform.scale(button_save_texture, BUTTON_SAVE_SIZE)
button_load_texture = pygame.image.load(LOAD_BUTTON_IMAGE).convert_alpha()
button_load_texture = pygame.transform.scale(button_load_texture, BUTTON_SAVE_SIZE)
# 创建按钮对象
button_start = Button(pygame.rect.Rect(*button_positions['start_button'], *BUTTON_START_SIZE), button_start_texture)
button_save = Button(pygame.rect.Rect(*button_positions['save_button'], *BUTTON_SAVE_SIZE), button_save_texture)
button_load = Button(pygame.rect.Rect(*button_positions['load_button'], *BUTTON_SAVE_SIZE), button_load_texture)
button_next_step = Button(pygame.rect.Rect(*button_positions['next_step_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_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_next = Button(pygame.rect.Rect(*button_positions['history_next_button'], *BUTTON_CONTROL_SIZE), None)
button_history_auto = Button(pygame.rect.Rect(*button_positions['history_auto_button'], *BUTTON_CONTROL_SIZE), None)
button_history_toggle = Button(pygame.rect.Rect(*button_positions['history_toggle_button'], *BUTTON_CONTROL_SIZE), None)
return {
'start': button_start,
'save': button_save,
'load': button_load,
'next_step': button_next_step,
'reset_path': button_reset_path,
'auto_play': button_auto_play,
'greedy_search': button_greedy_search,
'greedy_auto_play': button_greedy_auto_play,
'history_prev': button_history_prev,
'history_next': button_history_next,
'history_auto': button_history_auto,
'history_toggle': button_history_toggle
}
def update_save_ui_positions(save_ui, button_positions):
"""更新存档界面的位置"""
save_ui.list_area = pygame.Rect(*button_positions['save_list_area'])
if __name__ == "__main__":
pygameInit("maze")
maze = Maze(wall_size=WALL_SIZE, maze_size=MAZE_SIZE, file_name=DEFAULT_MAZE_FILE)
# 加载图片资源(将根据需要动态缩放)
image_wall = pygame.image.load(WALL_IMAGE).convert_alpha()
image_coin = pygame.image.load(COIN_IMAGE).convert_alpha()
image_trap = pygame.image.load(TRAP_IMAGE).convert_alpha()
image_boss = pygame.image.load(BOSS_IMAGE).convert_alpha()
image_lock = pygame.image.load(LOCK_IMAGE).convert_alpha()
# 初始按钮位置和按钮
button_positions = get_button_positions()
buttons = create_buttons(button_positions)
# 提示信息
mes1 = Toast("没有生成迷宫,无法保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes2 = Toast("迷宫已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes3 = Toast("存档已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes4 = 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_greedy = Toast("贪心搜索完成!", UI_WIDTH, UI_HEIGHT, font=textFont, duration=3, color=(255, 255, 255), bg_color=(0, 128, 0))
# 创建存档界面
save_ui = SaveLoadUI(textFont)
update_save_ui_positions(save_ui, button_positions)
# 创建Boss战斗界面
boss_fight_ui = BossFightUI()
# 创建机关界面
mechanism_ui = MechanismUI(textFont)
# 路径控制变量
auto_play = False
auto_play_timer = 0
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_timer = 0
history_auto_interval = AUTO_PLAY_INTERVAL // 2 # 历史播放速度稍快一些
# 当前显示尺寸跟踪
current_display_size = MAZE_SIZE
running = True
while running:
clock.tick(FPS) # 限制帧数
screen.fill(COLOR_WHITE) # 铺底
# 检查迷宫显示尺寸是否发生变化,如果变化则更新按钮位置
if maze.get_actual_display_size() != current_display_size:
current_display_size = maze.get_actual_display_size()
button_positions = get_button_positions(current_display_size)
buttons = create_buttons(button_positions)
update_save_ui_positions(save_ui, button_positions)
print(f"UI布局已更新迷宫显示尺寸: {current_display_size}")
# 更新Boss战斗界面
boss_fight_ui.update()
# 自动播放逻辑
if auto_play and len(maze.full_path) > 0 and not maze.show_history and not boss_fight_ui.is_showing and not mechanism_ui.is_showing:
auto_play_timer += 1
if auto_play_timer >= auto_play_interval:
has_next, boss_encountered, boss_info, mechanism_encountered, mechanism_info = maze.next_path_step()
if not has_next:
auto_play = False # 路径播放完成后停止自动播放
elif boss_encountered and boss_info:
# 显示Boss战斗界面
boss_fight_ui.show_boss_fight(boss_info['boss_data'], boss_info['player_skills'])
auto_play = False # 遇到boss时停止自动播放
elif mechanism_encountered and mechanism_info:
# 显示机关界面
mechanism_ui.show_mechanism()
auto_play = False # 遇到机关时停止自动播放
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:
history_auto_timer += 1
if history_auto_timer >= history_auto_interval:
if not maze.next_history_step():
history_auto_play = False # 历史播放完成后停止自动播放
history_auto_timer = 0
for event in pygame.event.get():
# 首先让机关界面处理事件
if mechanism_ui.handle_event(event):
continue
# 然后让Boss战斗界面处理事件
if boss_fight_ui.handle_event(event):
continue
# 然后让存档界面处理事件
save_result = save_ui.handle_event(event, maze)
if save_result == "save_success":
mes3.show()
elif save_result == "load_success":
mes4.show()
auto_play = False # 加载游戏后停止自动播放
elif save_result == "load_failed":
mes5.show()
# 如果存档界面、Boss战斗界面或机关界面正在显示不处理其他按钮事件
if not save_ui.show_save_list and not boss_fight_ui.is_showing and not mechanism_ui.is_showing:
buttons['start'].handle_event(event=event)
buttons['save'].handle_event(event=event)
buttons['load'].handle_event(event=event)
buttons['next_step'].handle_event(event=event)
buttons['reset_path'].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_next'].handle_event(event=event)
buttons['history_auto'].handle_event(event=event)
buttons['history_toggle'].handle_event(event=event)
if buttons['start'].pressed == True:
maze.generate()
auto_play = False # 生成新迷宫时停止自动播放
history_auto_play = False # 停止历史播放
auto_play_timer = 0 # 重置计时器
history_auto_timer = 0 # 重置历史计时器
print("已重置所有播放状态")
if buttons['save'].pressed == True:
if len(maze.grid) == 0:
mes1.show()
else:
# 自动保存为JSON格式
result = maze.save_game(format_type="json")
if result:
mes2.text = "迷宫已保存为JSON格式"
mes2.show()
else:
mes5.text = "保存失败"
mes5.show()
if buttons['load'].pressed == True:
# 直接读取maze_15_15_2.json文件
sample_file = "saves/maze_15_15_2.json"
if os.path.exists(sample_file):
if maze.load_game(sample_file):
# 加载成功后检查是否需要重新生成路径
if len(maze.full_path) == 0 and maze.generater.maze:
# 只有当没有路径信息时才重新生成
from SourceCollector import SourceCollector
maze.source_collector = SourceCollector(maze=maze.generater.maze)
maze.source_collector.run()
maze.full_path = maze.source_collector.get_path()
maze.path_step = 0
maze.is_path_complete = False
# 不要重置grid保持加载的包含路径的网格
mes4.text = f"已加载 {sample_file} 并生成路径"
mes4.show()
auto_play = False
else:
mes4.text = f"已加载 {sample_file}"
mes4.show()
auto_play = False
else:
mes5.text = f"无法加载 {sample_file}"
mes5.show()
else:
# 如果maze_15_15_2.json不存在则打开存档选择界面
save_ui.update_save_list(maze)
save_ui.toggle_save_list()
# 路径控制
if buttons['next_step'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
has_next, boss_encountered, boss_info, mechanism_encountered, mechanism_info = maze.next_path_step()
if boss_encountered and boss_info:
# 显示Boss战斗界面
boss_fight_ui.show_boss_fight(boss_info['boss_data'], boss_info['player_skills'])
elif mechanism_encountered and mechanism_info:
# 显示机关界面
mechanism_ui.show_mechanism()
if buttons['reset_path'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
maze.reset_path()
auto_play = False
if buttons['auto_play'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
auto_play = not auto_play
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:
maze.prev_history_step()
if buttons['history_next'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
maze.next_history_step()
if buttons['history_auto'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_play = not history_auto_play
history_auto_timer = 0
if buttons['history_toggle'].pressed == True and len(maze.history_mazes) > 0:
maze.toggle_history_mode()
auto_play = False # 切换模式时停止路径播放
history_auto_play = False # 停止历史播放
# 键盘控制
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and len(maze.full_path) > 0 and not maze.show_history:
has_next, boss_encountered, boss_info, mechanism_encountered, mechanism_info = maze.next_path_step()
if boss_encountered and boss_info:
# 显示Boss战斗界面
boss_fight_ui.show_boss_fight(boss_info['boss_data'], boss_info['player_skills'])
elif mechanism_encountered and mechanism_info:
# 显示机关界面
mechanism_ui.show_mechanism()
elif event.key == pygame.K_r and len(maze.full_path) > 0 and not maze.show_history:
maze.reset_path()
auto_play = False
elif event.key == pygame.K_a and len(maze.full_path) > 0 and not maze.show_history:
auto_play = not auto_play
auto_play_timer = 0
# 历史迭代快捷键
elif event.key == pygame.K_LEFT and len(maze.history_mazes) > 0 and maze.show_history:
maze.prev_history_step()
elif event.key == pygame.K_RIGHT and len(maze.history_mazes) > 0 and maze.show_history:
maze.next_history_step()
elif event.key == pygame.K_h and len(maze.history_mazes) > 0:
maze.toggle_history_mode()
auto_play = False
history_auto_play = False
elif event.key == pygame.K_p and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_play = not history_auto_play
history_auto_timer = 0
# 保存快捷键
elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LCTRL]:
# Ctrl+S 保存包含路径的JSON
if len(maze.grid) > 0:
result = maze.save_game(format_type="json")
if result:
mes3.text = "JSON格式存档已保存"
mes3.show()
elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LSHIFT]:
# Shift+S 保存为CSV格式兼容旧版本
if len(maze.grid) > 0:
result = maze.save_game(format_type="csv")
if result:
mes3.text = "CSV格式存档已保存"
mes3.show()
elif event.key == pygame.K_l and pygame.key.get_pressed()[pygame.K_LCTRL]:
# Ctrl+L 打开读档界面
save_ui.update_save_list(maze)
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:
running = False
maze.draw(screen=screen, wall_texture=image_wall, coin_texture=image_coin, trap_texture=image_trap, boss_texture=image_boss, lock_texture=image_lock)
buttons['start'].draw(screen=screen)
buttons['save'].draw(screen=screen)
buttons['load'].draw(screen=screen)
# 绘制路径控制按钮(仅在有路径且非历史模式时显示)
if len(maze.full_path) > 0 and not maze.show_history:
# 绘制按钮背景
pygame.draw.rect(screen, COLOR_GRAY, buttons['next_step'].rect)
pygame.draw.rect(screen, COLOR_GRAY, buttons['reset_path'].rect)
pygame.draw.rect(screen, COLOR_GREEN if auto_play else COLOR_GRAY, buttons['auto_play'].rect)
# 绘制按钮文字
next_text = textFont.render("下一步", True, COLOR_BLACK)
reset_text = textFont.render("重置", True, COLOR_BLACK)
auto_text = textFont.render("自动播放" if not auto_play else "停止", True, COLOR_BLACK)
screen.blit(next_text, (buttons['next_step'].rect.x + 10, buttons['next_step'].rect.y + 15))
screen.blit(reset_text, (buttons['reset_path'].rect.x + 25, buttons['reset_path'].rect.y + 15))
screen.blit(auto_text, (buttons['auto_play'].rect.x + 5, buttons['auto_play'].rect.y + 15))
# 显示当前步数信息
progress_text = textFont.render(f"路径进度: {maze.path_step}/{len(maze.full_path)}", True, COLOR_BLACK)
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:
# 绘制按钮背景
pygame.draw.rect(screen, COLOR_GRAY, buttons['history_prev'].rect)
pygame.draw.rect(screen, COLOR_GRAY, buttons['history_next'].rect)
pygame.draw.rect(screen, COLOR_GREEN if history_auto_play else COLOR_GRAY, buttons['history_auto'].rect)
# 绘制按钮文字
prev_text = textFont.render("上一步", True, COLOR_BLACK)
next_text = textFont.render("下一步", True, COLOR_BLACK)
auto_text = textFont.render("自动播放" if not history_auto_play else "停止", True, COLOR_BLACK)
screen.blit(prev_text, (buttons['history_prev'].rect.x + 10, buttons['history_prev'].rect.y + 15))
screen.blit(next_text, (buttons['history_next'].rect.x + 10, buttons['history_next'].rect.y + 15))
screen.blit(auto_text, (buttons['history_auto'].rect.x + 5, buttons['history_auto'].rect.y + 15))
# 显示历史进度信息
history_progress_text = textFont.render(f"生成进度: {maze.history_step}/{len(maze.history_mazes)}", True, COLOR_BLACK)
screen.blit(history_progress_text, button_positions['history_progress_text'])
# 绘制历史模式切换按钮(仅在有历史时显示)
if len(maze.history_mazes) > 0:
pygame.draw.rect(screen, COLOR_GOLD if maze.show_history else COLOR_GRAY, buttons['history_toggle'].rect)
toggle_text = textFont.render("历史模式" if not maze.show_history else "路径模式", True, COLOR_BLACK)
screen.blit(toggle_text, (buttons['history_toggle'].rect.x + 5, buttons['history_toggle'].rect.y + 15))
# 显示操作提示
if len(maze.full_path) > 0 and not maze.show_history:
hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放 | G键: 贪心搜索", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text'])
elif len(maze.history_mazes) > 0 and maze.show_history:
hint_text = textFont.render("←→键: 历史步骤 | P键: 自动播放 | H键: 切换模式", True, COLOR_LIGHT_GRAY)
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)
screen.blit(shortcut_text, button_positions['shortcut_text'])
mes1.draw(screen=screen)
mes2.draw(screen=screen)
mes3.draw(screen=screen)
mes4.draw(screen=screen)
mes5.draw(screen=screen)
mes_boss.draw(screen=screen)
mes_greedy.draw(screen=screen)
# 绘制存档界面(必须在最后绘制以显示在最上层)
save_ui.draw(screen)
# 绘制Boss战斗界面必须在最上层
boss_fight_ui.draw(screen)
# 绘制机关界面(必须在最上层)
mechanism_ui.draw(screen)
pygame.display.flip()
pygame.quit()