maze_python/boss_fight.py
2025-06-30 17:43:54 +08:00

168 lines
6.6 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.

# -*- coding: utf-8 -*-
import math
import heapq
def boss_fight(B, PlayerSkills):
"""
使用分支限界法求解Boss战斗的最优策略
参数:
B: Boss血量序列 [boss1_hp, boss2_hp, ...]
PlayerSkills: 玩家技能列表 [(damage1, cooldown1), (damage2, cooldown2), ...]
返回:
每个Boss对应的攻击序列列表例如[[0, 1, 0], [2, -1, 1], ...]
其中数字表示技能索引,-1表示等待
如果无解则返回None
"""
n = len(B) # Boss数量
m = len(PlayerSkills) # 技能数量
if n == 0:
return []
# 计算最大平均伤害(用于启发式估计)
max_avg = 0.0
for damage, cooldown in PlayerSkills:
if cooldown + 1 > 0:
avg_damage = damage / (cooldown + 1)
if avg_damage > max_avg:
max_avg = avg_damage
# 如果所有技能伤害为0则无法击败Boss
if max_avg == 0:
return None
# 初始状态Boss索引0血量B[0]所有技能冷却为0回合数0
start_cooldown = tuple([0] * m)
total_remaining = sum(B)
# 启发式函数:当前回合数 + 估计剩余回合数
f_value = math.ceil(total_remaining / max_avg) if max_avg > 0 else total_remaining
# 初始化优先队列 (f_value, round_count, boss_idx, boss_remaining_hp, cooldown_tuple, boss_paths)
# boss_paths是一个列表每个元素是对应Boss的攻击序列
initial_boss_paths = [[] for _ in range(n)]
queue = [(f_value, 0, 0, B[0], start_cooldown, initial_boss_paths)]
# 记录访问过的状态 (boss_idx, boss_remaining_hp, cooldown_tuple) -> 最小回合数
visited = {}
visited[(0, B[0], start_cooldown)] = 0
# A*搜索主循环
while queue:
# 弹出f_value最小的状态进行扩展
f_value, round_count, boss_idx, boss_rem, cooldown, boss_paths = heapq.heappop(queue)
state_key = (boss_idx, boss_rem, cooldown)
# 状态剪枝:如果已有更好的状态到达当前状态,跳过
if visited.get(state_key, float('inf')) < round_count:
continue
# 目标状态所有Boss都被击败
if boss_idx >= n:
return boss_paths
# 动作1等待所有技能冷却时间-1回合数+1
new_cooldown_list = [max(0, c - 1) for c in cooldown]
new_cooldown = tuple(new_cooldown_list)
new_round = round_count + 1
new_state_key = (boss_idx, boss_rem, new_cooldown)
# 如果这个等待状态更优,则加入队列
if new_state_key not in visited or visited[new_state_key] > new_round:
visited[new_state_key] = new_round
# 重新计算启发式值
total_remaining = boss_rem + sum(B[boss_idx+1:])
estimate_rounds = math.ceil(total_remaining / max_avg) if max_avg > 0 else 0
new_f = new_round + estimate_rounds
# 复制boss_paths并给当前Boss添加等待动作
new_boss_paths = [path[:] for path in boss_paths]
new_boss_paths[boss_idx].append(-1) # -1表示等待
heapq.heappush(queue, (new_f, new_round, boss_idx, boss_rem, new_cooldown, new_boss_paths))
# 动作2使用技能
for skill_idx, (damage, skill_cooldown) in enumerate(PlayerSkills):
# 检查技能是否可用冷却时间为0
if cooldown[skill_idx] != 0:
continue
new_boss_idx = boss_idx
new_boss_rem = boss_rem - damage
# 复制boss_paths并给当前Boss添加技能使用
new_boss_paths = [path[:] for path in boss_paths]
new_boss_paths[boss_idx].append(skill_idx)
# 检查是否击败了当前Boss
if new_boss_rem <= 0:
new_boss_idx += 1
if new_boss_idx < n:
new_boss_rem = B[new_boss_idx] # 切换到下一个Boss
else:
new_boss_rem = 0 # 所有Boss都被击败
# 更新技能冷却时间
new_cooldown_list = [max(0, c - 1) for c in cooldown]
new_cooldown_list[skill_idx] = skill_cooldown # 设置使用技能的冷却时间
new_cooldown = tuple(new_cooldown_list)
new_round = round_count + 1
# 如果所有Boss都被击败直接返回结果
if new_boss_idx >= n:
return new_boss_paths
new_state_key = (new_boss_idx, new_boss_rem, new_cooldown)
# 如果这个技能使用状态更优,则加入队列
if new_state_key not in visited or visited[new_state_key] > new_round:
visited[new_state_key] = new_round
# 重新计算启发式值
total_remaining = new_boss_rem + sum(B[new_boss_idx+1:])
estimate_rounds = math.ceil(total_remaining / max_avg) if max_avg > 0 else 0
new_f = new_round + estimate_rounds
heapq.heappush(queue, (new_f, new_round, new_boss_idx, new_boss_rem, new_cooldown, new_boss_paths))
return None # 无解
if __name__ == '__main__':
# 测试用例
B = [10, 20, 30, 40, 50]
PlayerSkills = [(5, 2), (8, 3), (3, 1)]
print("Boss血量序列:", B)
print("玩家技能 (伤害, 冷却):", PlayerSkills)
print()
result = boss_fight(B, PlayerSkills)
for i in result:
print(i)
# if result:
# print("每个Boss的最优攻击序列:")
# for i, boss_sequence in enumerate(result):
# print(f"Boss {i+1} (血量{B[i]}): {boss_sequence}")
# # 显示详细的技能使用说明
# skill_details = []
# total_damage = 0
# for action in boss_sequence:
# if action == -1:
# skill_details.append("等待")
# else:
# damage, cooldown = PlayerSkills[action]
# total_damage += damage
# skill_details.append(f"技能{action}(伤害{damage},冷却{cooldown})")
# print(f" 详细: {' -> '.join(skill_details)}")
# print(f" 总伤害: {total_damage}, Boss血量: {B[i]}")
# print()
# else:
# print("无解 - 无法击败所有Boss")