引言
斗鸡是中国民间流传甚广的对抗性游戏:两名玩家单脚站立,互相用手推挤对方,先失去平衡(双脚着地或倒地)者判负。从算法视角分析,斗鸡本质上是一个连续策略空间的非合作博弈问题——每位玩家需要实时决策推力的方向与大小,同时维持自身的重心稳定。本文用Java实现斗鸡博弈的力学平衡模拟器,核心涉及重心计算、力矩平衡与纳什均衡三大算法模块。
核心算法思路
1. 力学平衡模型
将玩家简化为刚体模型,重心位置由支撑脚、躯干姿态决定。设玩家质量为 m,重心坐标为 (cx, cy),支撑脚坐标为 (fx, fy)。当玩家受到水平推力 F 时,产生的倾覆力矩为:
τ = F × h - m × g × d
其中 h 为重心高度,d 为重心到支撑点的水平距离。当 τ > 0 时玩家失去平衡。
2. 纳什均衡求解
斗鸡博弈的收益矩阵为双人零和博弈。设玩家A选择推力策略 sA,玩家B选择 sB,则收益函数为:
U_A(sA, sB) = stability_A(sA, sB) - stability_B(sA, sB)
纳什均衡即寻找策略对 (sA, sB),使得任何单方面偏离都无法增加自身收益。
3. 最优推力动态规划
将连续策略空间离散化为有限动作集(如轻推、中推、重推、闪避),通过动态规划计算多轮博弈中的最优策略序列。
Java实现
import java.util.*;
/**
* 斗鸡博弈力学平衡模拟器
* 核心算法:重心计算、力矩平衡、纳什均衡求解
*/
public class CockfightingGame {
// 玩家状态
static class Player {
String name;
double mass; // 质量 kg
double height; // 身高 m
double cx, cy; // 重心坐标
double fx, fy; // 支撑脚坐标
double stability; // 稳定度 [0,1]
boolean isFallen; // 是否倒地
Player(String name, double mass, double height) {
this.name = name;
this.mass = mass;
this.height = height;
this.stability = 1.0;
this.isFallen = false;
// 初始重心位于支撑脚正上方
this.cx = 0; this.cy = height * 0.55;
this.fx = 0; this.fy = 0;
}
// 计算当前重心偏移距离
double getCenterOffset() {
return Math.abs(cx - fx);
}
// 计算最大稳定偏移(支撑面半径简化模型)
double getMaxStableOffset() {
return 0.15 * height; // 经验值:约15%身高
}
}
// 推力动作
static class PushAction {
String name;
double force; // 推力大小 N
double direction; // 方向角度(弧度,0=正前方)
double selfRisk; // 自身失衡风险系数
PushAction(String name, double force, double direction, double selfRisk) {
this.name = name;
this.force = force;
this.direction = direction;
this.selfRisk = selfRisk;
}
}
// 动作空间
static final PushAction[] ACTION_SPACE = {
new PushAction("轻推", 30, 0, 0.05),
new PushAction("侧推", 45, Math.PI / 6, 0.08),
new PushAction("重推", 70, 0, 0.15),
new PushAction("闪避", 0, 0, -0.1), // 负值表示恢复稳定
new PushAction("斜推", 55, Math.PI / 4, 0.12)
};
/**
* 核心算法1:计算推力产生的力矩与稳定性变化
* 力矩公式:τ = F * h * sin(θ) - m * g * offset
* 返回目标玩家的稳定性变化量(负值表示变得更不稳定)
*/
public static double calculateStabilityChange(Player target, Player attacker,
PushAction action) {
final double G = 9.8; // 重力加速度
double h = target.cy; // 重心高度
double offset = target.getCenterOffset();
double maxOffset = target.getMaxStableOffset();
// 计算推力在水平方向的有效分量
double dist = Math.sqrt(
Math.pow(target.cx - attacker.cx, 2) +
Math.pow(target.cy - attacker.cy, 2)
);
if (dist < 0.001) dist = 0.001;
// 推力方向向量
double dx = (target.cx - attacker.cx) / dist;
double dy = (target.cy - attacker.cy) / dist;
// 有效水平推力
double effectiveForce = action.force * Math.cos(action.direction);
// 倾覆力矩
double overturningTorque = effectiveForce * h;
// 恢复力矩(重力产生)
double restoringTorque = target.mass * G * (maxOffset - offset);
// 净力矩导致的稳定性变化
double deltaStability;
if (overturningTorque > restoringTorque) {
deltaStability = -0.3; // 大幅失衡
} else {
deltaStability = -overturningTorque / (restoringTorque + 1) * 0.1;
}
return deltaStability;
}
/**
* 核心算法2:构建收益矩阵并求解纳什均衡(简化版)
* 使用纯策略纳什均衡扫描法
* 时间复杂度:O(|A|^2),A为动作空间
*/
public static Map<String, PushAction> findNashEquilibrium(Player p1, Player p2) {
int n = ACTION_SPACE.length;
double[][] payoffMatrix = new double[n][n];
// 构建收益矩阵(p1的收益)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
PushAction a1 = ACTION_SPACE[i];
PushAction a2 = ACTION_SPACE[j];
// p1对p2施加的稳定性影响
double effectOnP2 = calculateStabilityChange(p2, p1, a1);
// p2对p1施加的稳定性影响
double effectOnP1 = calculateStabilityChange(p1, p2, a2);
// p1考虑自身风险后的净收益
double selfRisk = a1.selfRisk;
payoffMatrix[i][j] = -effectOnP2 - effectOnP1 - selfRisk;
}
}
// 寻找纳什均衡:没有任何一方有动力单方面改变策略
Map<String, PushAction> equilibrium = new HashMap<>();
double bestGap = Double.NEGATIVE_INFINITY;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 检查(i,j)是否为纳什均衡
boolean isNash = true;
double p1Payoff = payoffMatrix[i][j];
double p2Payoff = -payoffMatrix[i][j]; // 零和博弈
// p1是否会偏离?
for (int k = 0; k < n; k++) {
if (payoffMatrix[k][j] > p1Payoff + 1e-6) {
isNash = false; break;
}
}
if (!isNash) continue;
// p2是否会偏离?
for (int k = 0; k < n; k++) {
if (-payoffMatrix[i][k] > p2Payoff + 1e-6) {
isNash = false; break;
}
}
if (isNash) {
double gap = p1Payoff - p2Payoff;
if (gap > bestGap) {
bestGap = gap;
equilibrium.put("player1", ACTION_SPACE[i]);
equilibrium.put("player2", ACTION_SPACE[j]);
}
}
}
}
// 若不存在纯策略纳什均衡,返回最大最小策略
if (equilibrium.isEmpty()) {
int bestI = findMaxMinStrategy(payoffMatrix);
int bestJ = findMinMaxStrategy(payoffMatrix);
equilibrium.put("player1", ACTION_SPACE[bestI]);
equilibrium.put("player2", ACTION_SPACE[bestJ]);
}
return equilibrium;
}
// 最大最小策略(保守策略)
private static int findMaxMinStrategy(double[][] matrix) {
int n = matrix.length;
int bestI = 0;
double bestMin = Double.NEGATIVE_INFINITY;
for (int i = 0; i < n; i++) {
double rowMin = Double.POSITIVE_INFINITY;
for (int j = 0; j < n; j++) {
rowMin = Math.min(rowMin, matrix[i][j]);
}
if (rowMin > bestMin) {
bestMin = rowMin;
bestI = i;
}
}
return bestI;
}
// 最小最大策略
private static int findMinMaxStrategy(double[][] matrix) {
int n = matrix.length;
int bestJ = 0;
double bestMax = Double.POSITIVE_INFINITY;
for (int j = 0; j < n; j++) {
double colMax = Double.NEGATIVE_INFINITY;
for (int i = 0; i < n; i++) {
colMax = Math.max(colMax, matrix[i][j]);
}
if (colMax < bestMax) {
bestMax = colMax;
bestJ = j;
}
}
return bestJ;
}
/**
* 核心算法3:多轮博弈动态规划
* dp[t][s1][s2] 表示第t轮、玩家1稳定性s1、玩家2稳定性s2时的最优收益
* 由于状态空间连续,需离散化处理
*/
public static double solveMultiRoundDP(Player p1, Player p2, int rounds) {
// 稳定性离散化为10个等级
int levels = 10;
double[][][] dp = new double[rounds + 1][levels][levels];
// 边界条件:最后一轮
for (int s1 = 0; s1 < levels; s1++) {
for (int s2 = 0; s2 < levels; s2++) {
dp[rounds][s1][s2] = (s1 - s2) * 1.0 / levels;
}
}
// 逆向递推
for (int t = rounds - 1; t >= 0; t--) {
for (int s1 = 0; s1 < levels; s1++) {
for (int s2 = 0; s2 < levels; s2++) {
double bestVal = Double.NEGATIVE_INFINITY;
for (PushAction a1 : ACTION_SPACE) {
for (PushAction a2 : ACTION_SPACE) {
// 状态转移
int ns1 = Math.max(0, Math.min(levels - 1,
s1 + (int)(calculateStabilityChange(
new Player("tmp", 60, 1.7), p2, a2) * levels)));
int ns2 = Math.max(0, Math.min(levels - 1,
s2 + (int)(calculateStabilityChange(
new Player("tmp", 60, 1.7), p1, a1) * levels)));
bestVal = Math.max(bestVal, dp[t + 1][ns1][ns2]);
}
}
dp[t][s1][s2] = bestVal;
}
}
}
int initS1 = (int)(p1.stability * levels) - 1;
int initS2 = (int)(p2.stability * levels) - 1;
return dp[0][initS1][initS2];
}
// 模拟单轮斗鸡博弈
public static void simulateRound(Player p1, Player p2) {
System.out.println("\n=== 斗鸡博弈模拟 ===");
System.out.printf("%s: 稳定性=%.2f, 重心偏移=%.3fm%n",
p1.name, p1.stability, p1.getCenterOffset());
System.out.printf("%s: 稳定性=%.2f, 重心偏移=%.3fm%n",
p2.name, p2.stability, p2.getCenterOffset());
// 求解纳什均衡
Map<String, PushAction> eq = findNashEquilibrium(p1, p2);
PushAction a1 = eq.get("player1");
PushAction a2 = eq.get("player2");
System.out.println("\n纳什均衡策略:");
System.out.printf(" %s 选择 [%s] (力=%.0fN, 风险=%.2f)%n",
p1.name, a1.name, a1.force, a1.selfRisk);
System.out.printf(" %s 选择 [%s] (力=%.0fN, 风险=%.2f)%n",
p2.name, a2.name, a2.force, a2.selfRisk);
// 应用推力效果
double delta1 = calculateStabilityChange(p1, p2, a2);
double delta2 = calculateStabilityChange(p2, p1, a1);
p1.stability = Math.max(0, Math.min(1, p1.stability + delta1 - a1.selfRisk));
p2.stability = Math.max(0, Math.min(1, p2.stability + delta2 - a2.selfRisk));
System.out.printf("\n回合结果:%s 稳定性→%.2f, %s 稳定性→%.2f%n",
p1.name, p1.stability, p2.name, p2.stability);
if (p1.stability < 0.2) p1.isFallen = true;
if (p2.stability < 0.2) p2.isFallen = true;
}
public static void main(String[] args) {
Player player1 = new Player("玩家A", 65, 1.75);
Player player2 = new Player("玩家B", 70, 1.80);
int round = 1;
while (!player1.isFallen && !player2.isFallen && round <= 20) {
System.out.println("\n--- 第 " + round + " 轮 ---");
simulateRound(player1, player2);
round++;
}
System.out.println("\n=== 最终结果 ===");
if (player1.isFallen && player2.isFallen) {
System.out.println("平局!双方同时倒地");
} else if (player1.isFallen) {
System.out.println(player2.name + " 获胜!");
} else if (player2.isFallen) {
System.out.println(player1.name + " 获胜!");
} else {
System.out.println("达到最大回合数,根据稳定性判定:" +
(player1.stability > player2.stability ? player1.name : player2.name) + " 获胜!");
}
}
}
复杂度分析
| 算法模块 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 力矩计算 | O(1) | O(1) |
| 纳什均衡扫描 | O(|A|²) | O(|A|²) |
| 多轮DP求解 | O(T × L² × |A|²) | O(T × L²) |
总结
本文将斗鸡游戏抽象为力学平衡与博弈论的交叉问题,通过刚体力学模型计算稳定性变化,利用纳什均衡理论求解最优推力策略,并以动态规划处理多轮博弈的长期决策。该框架可推广到摔跤、相扑等更广泛的对抗性体育项目的策略分析中。