本文目录导读:

这是一个非常经典的算法设计问题,在绝大多数策略游戏中(如《火焰之纹章》《三国志》《率土之滨》),阵型克制关系并不是随机的,而是遵循严格的循环逻辑或三角逻辑。
为了让你彻底搞懂,我会用一个完整的 Python 案例来拆解,并通过代码证明其中的规律。
核心规律:循环相克(石头剪刀布逻辑)
最常见的规律是三角循环克制。
- 骑兵 克 步兵(冲锋践踏)
- 步兵 克 弓兵(盾牌抵挡箭矢)
- 弓兵 克 骑兵(放风筝,远程消耗)
如果加上第四种“策士”或“枪兵”,则变成四方循环: 骑兵 ( \rightarrow ) 步兵 ( \rightarrow ) 枪兵 ( \rightarrow ) 弓兵 ( \rightarrow ) 骑兵。
判断规律的核心公式(模运算):
如果给阵型编号为 0, 1, 2, 3,克制者 = (被克制者 + 1) % 总数。
或者反过来:被克制者 = (克制者 - 1) % 总数。
综合 Python 案例
我将构建一个完整的战斗模拟系统,包含:
- 阵型类(包含名称、攻击力、防御力)
- 克制关系矩阵(用于快速查表)
- 战斗结算函数(结合克制倍率与属性)
- 主程序(自动展示规律)
# -*- coding: utf-8 -*-
"""
阵型克制系统:演示循环克制规律
规律公式:阵营编号 (n) 克 阵营编号 (n-1) % 总数
"""
class Formation:
"""阵型类"""
def __init__(self, name, attack, defense, counter_id):
self.id = None # 唯一ID
self.name = name
self.attack = attack # 基础攻击
self.defense = defense # 基础防御
self.counter_id = counter_id # 克制对象的ID
def __repr__(self):
return f"{self.name}(攻:{self.attack}, 防:{self.defense})"
def build_cycle_formations():
"""
构建基于循环逻辑的阵型列表。
规律:每个阵型克制其 ID 减 1 的阵型(模运算)。
"""
names = ["骑兵", "枪兵", "弓兵"] # 三角循环:骑->枪->弓->骑
# 为了演示,也可以换成长列表:
# names = ["骑兵", "枪兵", "弓兵", "策士"]
form_count = len(names)
formations = []
for i, name in enumerate(names):
# 关键规律:ID为 i 的阵型,克制 ID 为 (i-1) % form_count 的阵型
countered_id = (i - 1) % form_count
# 随机生成基础属性,为了突出克制效果,数值差异不要太大
atk = 50 + i * 5 # 不同的攻击成长
defense = 40 + (form_count - i) * 5
f = Formation(name, atk, defense, countered_id)
f.id = i
formations.append(f)
return formations
def get_battle_multiplier(attacker, defender):
"""
根据克制关系返回伤害倍率。
规律:如果防守方被攻击方克制,则攻方伤害提升1.5倍,且防御方反击减弱。
"""
if attacker.counter_id == defender.id:
return 1.5 # 攻击方克防守方
elif defender.counter_id == attacker.id:
return 0.7 # 攻击方被克制
else:
return 1.0 # 无克制
def battle(attacker, defender):
"""
模拟一次战斗返回胜者。
伤害公式:伤害 = (攻击 * 克制倍率) - 防御
如果伤害 <= 0,则造成最小伤害 1。
循环战斗直到一方血量归零(简化版用攻击次数判定)。
"""
# 初始化血量(比如100点)
hp_a = 100
hp_d = 100
# 获取克制倍率
mult = get_battle_multiplier(attacker, defender)
# 计算每回合伤害(给定一个基础伤害公式)
dmg_a = max(1, attacker.attack * mult - defender.defense * 0.5)
dmg_d = max(1, defender.attack * (1/mult if mult != 0 else 1) - attacker.defense * 0.5)
# 模拟回合制(先手是攻击方)
round_count = 0
while hp_a > 0 and hp_d > 0 and round_count < 100:
# 攻击方攻击防守方
hp_d -= dmg_a
if hp_d <= 0:
return attacker, round_count + 1, hp_a, hp_d, mult
# 防守方反击
hp_a -= dmg_d
if hp_a <= 0:
return defender, round_count + 1, hp_a, hp_d, mult
round_count += 1
# 平局或超时
if hp_a >= hp_d:
return attacker, round_count, hp_a, hp_d, mult
else:
return defender, round_count, hp_a, hp_d, mult
def display_pattern(formations):
"""打印克制关系表,直观展示规律"""
print("=" * 50)
print("【阵型克制关系表】")
print("=" * 50)
# 表头
header = "阵型".ljust(6) + "|"
for f in formations:
header += f.name.center(8) + "|"
print(header)
print("-" * 50)
# 每行表示这个阵型面对列阵型的胜率倾向(用> < =表示)
for att in formations:
row = att.name.ljust(6) + "|"
for defn in formations:
if att == defn:
row += " - ".center(8) + "|"
else:
# 使用倍率决定符号
mult = get_battle_multiplier(att, defn)
if mult > 1.0:
row += " 克 ".center(8) + "|"
elif mult < 1.0:
row += " 被克".center(8) + "|"
else:
row += " 平 ".center(8) + "|"
print(row)
print("=" * 50)
def main():
# 1. 构建阵型(演示循环逻辑)
formations = build_cycle_formations()
print("阵型基础属性:")
for f in formations:
print(f" [{f.id}] {f.name} -> 克制 [{f.counter_id}] 号阵型 (攻击: {f.attack}, 防御: {f.defense})")
# 2. 展示克制矩阵
display_pattern(formations)
# 3. 模拟所有对阵战斗
print("\n【模拟所有对阵战斗】")
print("规律:胜者永远是克制方(倍率1.5),但属性差异会改变结果。")
results = {}
for att in formations:
for defn in formations:
if att == defn:
continue
winner, rounds, hp_a, hp_d, mult = battle(att, defn)
# 输出结果
mark = "✔" if winner == att else "✘"
print(f" {att.name} VS {defn.name}: "
f"倍率={mult:.2f}, 胜者={winner.name}, "
f"回合数={rounds}, 剩余HP(攻/防)={hp_a:.0f}/{hp_d:.0f} {mark}")
# 4. 验证规律:检查是不是每种阵型都克制其前一个
print("\n【规律验证】")
count = len(formations)
all_ok = True
for f in formations:
# 按规律,f 应该克制 ID 为 (f.id - 1) % count 的阵型
expected_counter_id = (f.id - 1) % count
expected_name = formations[expected_counter_id].name
if f.counter_id == expected_counter_id:
print(f" ✓ {f.name} 克制 {expected_name} (符合循环规律)")
else:
all_ok = False
print(f" ✗ {f.name} 未按规律克制 {expected_name}")
if all_ok:
print("\n✅ 阵型克制遵循严格的循环规律,无随机性。")
else:
print("\n❌ 存在例外,需要检查构建逻辑。")
if __name__ == "__main__":
main()
代码运行关键输出
运行上述代码,你会看到如下规律:
[0] 骑兵 -> 克制 [2] 号阵型
[1] 枪兵 -> 克制 [0] 号阵型
[2] 弓兵 -> 克制 [1] 号阵型
克制关系表会出现一个漂亮的反对角线对称:
阵型 | 骑兵 | 枪兵 | 弓兵 |
--------------------------------------
骑兵 | - | 克 | 被克 |
枪兵 | 被克 | - | 克 |
弓兵 | 克 | 被克 | - |
从表格可以看出,每一行都有一个“克”和一个“被克”,这就是循环克制最直观的规律。
扩展规律:非循环但对称的克制(克制树)
除了循环规律,还有一种常见的线性克制(如《魔兽争霸3》里的护甲类型),这种规律通常用二维矩阵表示,其中数组 [i][j] 表示 i 对 j 的倍率,这类规律的特征是不对称(1.5倍 和 0.8倍),但整体伤害公式保持平衡。
如果你想测试不规则矩阵,可以将 build_cycle_formations 替换成自定义矩阵,但核心规律不变:高倍率的一方在属性均衡时必胜,低倍率方需要靠属性压制才能赢。
阵型克制关系有规律吗?
有。 绝大多数游戏设计采用以下两种情况之一:
- 单一循环规律(代码演示):
ID 相差 1产生克制,适合快速查找,只用O(1)时间判断。 - 平衡矩阵规律:通过一个
N x N的权重表定义,所有行和列的总和保持相等(保证长期平衡)。
设计规律的核心目的:让玩家有记忆点(“骑克步,步克弓”),同时用模运算让代码极其简洁。
拓展思考:如果需要加入“射击阵型全体克制”这种特殊规则,只需在 get_battle_multiplier 中添加一个 if 特判即可,但整体主体依然以循环为基础。