168 lines
6.6 KiB
Python
168 lines
6.6 KiB
Python
# -*- 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") |