Merge branch 'main' of git.gangary.cn:gary/maze_python
This commit is contained in:
commit
120182ea68
234
boss_fight.py
234
boss_fight.py
@ -1,136 +1,110 @@
|
||||
# -*-coding: GBK -*-
|
||||
import heapq # 导入堆模块,用于实现优先队列
|
||||
from typing import List, Optional, Tuple # 导入类型提示相关模块
|
||||
#-*-coding: GBK -*-
|
||||
import math
|
||||
import heapq
|
||||
|
||||
def boss_fight(B, PlayerSkills):
|
||||
n = len(B)
|
||||
m = len(PlayerSkills)
|
||||
if n == 0:
|
||||
return []
|
||||
|
||||
# 计算最大平均伤害用于启发式函数
|
||||
max_avg = 0.0
|
||||
for a, b in PlayerSkills:
|
||||
if b + 1 > 0:
|
||||
avg = a / (b + 1)
|
||||
if avg > max_avg:
|
||||
max_avg = avg
|
||||
# 如果所有技能伤害为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 = 当前回合数 + 估计剩余回合数
|
||||
f_value = math.ceil(total_remaining / max_avg) if max_avg > 0 else total_remaining
|
||||
|
||||
def boss_strategy(coin: int, blood: int) -> Optional[Tuple[List[str], int]]:
|
||||
# 使用分支限界法寻找最小回合数击败BOSS的技能序列
|
||||
# 初始化优先队列 (f_value, round_count, boss_idx, boss_rem, cooldown, path)
|
||||
queue = [(f_value, 0, 0, B[0], start_cooldown, [])]
|
||||
# 记录访问过的状态(boss_idx, boss_rem, cooldown) -> 最小回合数
|
||||
visited = {}
|
||||
visited[(0, B[0], start_cooldown)] = 0
|
||||
|
||||
all_path = []
|
||||
# 优先队列按f_value排序
|
||||
while queue:
|
||||
# 优先扩展f_value最小的状态(最有希望的状态)
|
||||
f_value, round_count, boss_idx, boss_rem, cooldown, path = heapq.heappop(queue)
|
||||
|
||||
state_key = (boss_idx, boss_rem, cooldown)
|
||||
|
||||
# 参数:
|
||||
# coin: 玩家初始金币数
|
||||
# blood: BOSS初始血量
|
||||
|
||||
# 返回:
|
||||
# 最小回合数的技能序列列表,如果无解则返回None
|
||||
|
||||
start_coin = coin # 记录初始金币数,用于计算消耗金币
|
||||
|
||||
# 初始状态: (回合数, 剩余金币, BOSS血量, 充能, 连续E计数, 技能序列元组)
|
||||
start_state = (0, coin, blood, 0, 0, ())
|
||||
|
||||
# 创建优先队列: 元素为 (优先级, 状态)
|
||||
# 优先级元组: (回合数, 消耗金币, BOSS血量) - 值越小优先级越高
|
||||
heap = []
|
||||
heapq.heappush(heap, ((0, 0, blood), start_state)) # 将初始状态加入优先队列
|
||||
|
||||
# 创建访问集合: 用于避免重复状态
|
||||
# 状态键: (回合数, 剩余金币, BOSS血量, 充能, min(连续E计数,3))
|
||||
visited = set()
|
||||
start_ce_compressed = min(0, 3) # 压缩连续E计数状态
|
||||
visited.add((0, coin, blood, 0, start_ce_compressed)) # 将初始状态加入已访问集合
|
||||
|
||||
# 设置最大回合数限制,避免无限循环
|
||||
max_round = blood * 2 # 每次至少造成1点伤害,最多回合数为血量两倍
|
||||
|
||||
# 主循环: 处理优先队列中的状态
|
||||
while heap:
|
||||
# 从优先队列中取出优先级最高的状态
|
||||
priority, state = heapq.heappop(heap)
|
||||
# 解包状态元组
|
||||
round_cnt, cur_coin, cur_blood, energy, ce, seq_tuple = state
|
||||
|
||||
# 检查是否已击败BOSS
|
||||
if cur_blood <= 0:
|
||||
return (list(seq_tuple), cur_coin) # 返回技能序列和剩余金币
|
||||
|
||||
# 超过最大回合数则跳过当前状态
|
||||
if round_cnt >= max_round:
|
||||
# 状态检查:如果已有更好状态,跳过当前状态
|
||||
if visited.get(state_key, float('inf')) < round_count:
|
||||
continue
|
||||
|
||||
# 尝试使用技能A
|
||||
if cur_coin >= 2: # 检查金币是否足够
|
||||
new_coin = cur_coin - 2 # 扣除金币
|
||||
new_blood = cur_blood - 1 # 减少BOSS血量
|
||||
new_energy = energy + 1 # 增加充能
|
||||
new_ce = 0 # 使用非E技能,重置连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('A',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态(>=3视为3)
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
# 尝试使用技能E
|
||||
cost_e = 3 # 默认消耗金币
|
||||
if ce >= 2: # 检查是否为连续第三次使用E
|
||||
cost_e = 2 # 第三次消耗2金币
|
||||
|
||||
if cur_coin >= cost_e: # 检查金币是否足够
|
||||
new_coin = cur_coin - cost_e # 扣除金币
|
||||
new_blood = cur_blood - 3 # 减少BOSS血量
|
||||
new_energy = energy + 1 # 增加充能
|
||||
new_ce = ce + 1 # 增加连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('E',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
# 尝试使用技能Q
|
||||
if energy >= 3 and cur_coin >= 3: # 检查充能和金币是否足够
|
||||
new_coin = cur_coin - 3 # 扣除金币
|
||||
new_blood = cur_blood - 8 # 减少BOSS血量
|
||||
new_energy = energy - 3 # 消耗充能
|
||||
new_ce = 0 # 使用非E技能,重置连续E计数
|
||||
new_round = round_cnt + 1 # 回合数+1
|
||||
new_seq = seq_tuple + ('Q',) # 添加技能到序列
|
||||
|
||||
# 压缩连续E计数状态
|
||||
ce_compressed = min(new_ce, 3)
|
||||
# 创建状态键用于去重
|
||||
state_key = (new_round, new_coin, new_blood, new_energy, ce_compressed)
|
||||
|
||||
# 检查是否为新状态
|
||||
if state_key not in visited:
|
||||
visited.add(state_key) # 标记为已访问
|
||||
cost = start_coin - new_coin # 计算已消耗金币
|
||||
# 创建新状态
|
||||
new_state = (new_round, new_coin, new_blood, new_energy, new_ce, new_seq)
|
||||
# 将新状态加入优先队列
|
||||
heapq.heappush(heap, ((new_round, cost, new_blood), new_state))
|
||||
|
||||
return None # 队列为空,无解
|
||||
|
||||
# 所有BOSS已击败
|
||||
if boss_idx >= n:
|
||||
return path
|
||||
|
||||
# 等待操作:所有冷却时间减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
|
||||
new_path = path + [-1]
|
||||
heapq.heappush(queue, (new_f, new_round, boss_idx, boss_rem, new_cooldown, new_path))
|
||||
|
||||
# 使用技能
|
||||
for skill_idx, (a, b) in enumerate(PlayerSkills):
|
||||
if cooldown[skill_idx] != 0: # 技能不可用
|
||||
continue
|
||||
|
||||
new_boss_idx = boss_idx
|
||||
new_boss_rem = boss_rem - a
|
||||
|
||||
# 击败当前BOSS
|
||||
if new_boss_rem <= 0:
|
||||
new_boss_idx += 1
|
||||
all_path.append(new_path)
|
||||
new_path = []
|
||||
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] = b # 重置当前技能冷却时间
|
||||
new_cooldown = tuple(new_cooldown_list)
|
||||
new_round = round_count + 1
|
||||
new_path = path + [skill_idx]
|
||||
|
||||
# 所有BOSS已击败,返回路径
|
||||
if new_boss_idx >= n:
|
||||
return all_path
|
||||
|
||||
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_path))
|
||||
|
||||
return None # 无解
|
||||
|
||||
|
||||
# 主程序入口
|
||||
if __name__ == "__main__":
|
||||
# 测试示例: 金币=10, BOSS血量=10
|
||||
result = boss_strategy(20, 20)
|
||||
|
||||
if result is None:
|
||||
print("无法击败BOSS")
|
||||
else:
|
||||
skill_sequence, remaining_coins = result
|
||||
print("最小回合数技能序列:", skill_sequence) # 预期输出: ['A', 'A', 'A', 'Q']
|
||||
print("剩余金币:", remaining_coins) # 预期输出: 1
|
||||
if __name__ == '__main__':
|
||||
B = [10, 20, 30, 40, 50]
|
||||
PlayerSkills = [(5, 2), (8, 3), (3, 1)]
|
||||
ans = boss_fight(B, PlayerSkills)
|
||||
for i in ans:
|
||||
print(i)
|
Loading…
Reference in New Issue
Block a user