每日算法 — 使用java实现斗鸡:力学平衡博弈与纳什均衡策略

引言

斗鸡是中国民间流传甚广的对抗性游戏:两名玩家单脚站立,互相用手推挤对方,先失去平衡(双脚着地或倒地)者判负。从算法视角分析,斗鸡本质上是一个连续策略空间的非合作博弈问题——每位玩家需要实时决策推力的方向与大小,同时维持自身的重心稳定。本文用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²)

总结

本文将斗鸡游戏抽象为力学平衡与博弈论的交叉问题,通过刚体力学模型计算稳定性变化,利用纳什均衡理论求解最优推力策略,并以动态规划处理多轮博弈的长期决策。该框架可推广到摔跤、相扑等更广泛的对抗性体育项目的策略分析中。