游戏简介与算法思路
石头剪刀布是人类最古老、最普遍的博弈游戏之一。看似纯粹靠运气,但心理学研究表明,人类玩家在出招时并非完全随机,而是存在可预测的模式:赢了倾向于保持、输了倾向于改变,甚至会在连续出相同手势后切换。本文用Java实现一个带AI对手的石头剪刀布游戏,核心算法包含三层预测策略:马尔可夫链状态转移预测玩家下一手、频率分析捕捉长期偏好、纳什均衡混合策略作为最终保底,让AI真正具备”读心”能力。
核心算法一:马尔可夫链状态转移
马尔可夫链的核心假设是:下一状态的概率仅依赖于当前状态。在石头剪刀布中,如果玩家上一轮出了”石头”,本轮出”剪刀”的条件概率可能显著高于1/3。我们维护一个3×3的转移计数矩阵,记录从手势A到手势B的历史出现次数:
石头 剪刀 布
石头 [c00 c01 c02]
剪刀 [c10 c11 c12]
布 [c20 c21 c22]
每当玩家出一轮后,更新对应转移计数。预测时,根据玩家上一轮出的手势,选择转移计数最高的下一手势作为预测目标,AI则出能克制该预测的招式。
核心算法二:频率分析与混合策略
当历史数据不足或马尔可夫预测置信度较低时,系统回退到频率分析层:统计玩家各手势的历史出现频次,选择出现频率最高的手势进行反制。若连频率分析也无法提供有效信号(如完全均匀分布),AI则采用纳什均衡混合策略——完全随机出招,确保长期期望收益不为负。
Java完整实现
以下代码提供可直接运行的完整项目,包含预测引擎、游戏循环与统计模块:
import java.util.*;
/**
* 手势枚举
*/
enum Move {
ROCK(0, "石头"), SCISSORS(1, "剪刀"), PAPER(2, "布");
final int idx;
final String label;
Move(int idx, String label) { this.idx = idx; this.label = label; }
/**
* 判断本手势是否击败对手
*/
boolean beats(Move other) {
return (this.idx + 1) % 3 == other.idx; // 石头赢剪刀, 剪刀赢布, 布赢石头
}
static Move fromIdx(int i) { return values()[i]; }
}
/**
* 马尔可夫链预测器:基于状态转移矩阵预测玩家下一手
*/
class MarkovPredictor {
private final int[][] trans = new int[3][3]; // trans[当前][下一]计数
private Move lastMove = null;
private int totalTrans = 0;
void record(Move move) {
if (lastMove != null) {
trans[lastMove.idx][move.idx]++;
totalTrans++;
}
lastMove = move;
}
/**
* 返回预测的玩家下一手;若无足够数据则返回null
*/
Move predict() {
if (lastMove == null || totalTrans < 3) return null;
int[] row = trans[lastMove.idx];
int max = -1, best = -1;
for (int i = 0; i < 3; i++) {
if (row[i] > max) { max = row[i]; best = i; }
}
return best >= 0 ? Move.fromIdx(best) : null;
}
double confidence() {
if (lastMove == null || totalTrans == 0) return 0;
int[] row = trans[lastMove.idx];
int sum = Arrays.stream(row).sum();
if (sum == 0) return 0;
int max = Arrays.stream(row).max().orElse(0);
return (double) max / sum;
}
}
/**
* 频率分析器:统计玩家历史出招频率
*/
class FrequencyAnalyzer {
private final int[] freq = new int[3];
private int total = 0;
void record(Move move) {
freq[move.idx]++;
total++;
}
Move predict() {
if (total == 0) return null;
int max = -1, best = -1;
for (int i = 0; i < 3; i++) {
if (freq[i] > max) { max = freq[i]; best = i; }
}
return best >= 0 ? Move.fromIdx(best) : null;
}
double confidence() {
if (total == 0) return 0;
int max = Arrays.stream(freq).max().orElse(0);
return (double) max / total;
}
}
/**
* AI决策引擎:三层策略融合
*/
class AiEngine {
private final MarkovPredictor markov = new MarkovPredictor();
private final FrequencyAnalyzer freq = new FrequencyAnalyzer();
private final Random rand = new Random();
Move decide() {
Move predicted = null;
double conf = 0;
// 第一层:马尔可夫链预测
Move m = markov.predict();
if (m != null && markov.confidence() > 0.45) {
predicted = m;
conf = markov.confidence();
}
// 第二层:频率分析回退
if (predicted == null) {
Move f = freq.predict();
if (f != null && freq.confidence() > 0.40) {
predicted = f;
conf = freq.confidence();
}
}
// 第三层:纳什均衡随机策略
if (predicted == null) {
return Move.fromIdx(rand.nextInt(3));
}
// 出能克制predicted的招式
return Move.fromIdx((predicted.idx + 2) % 3); // predicted=石头→布, 剪刀→石头, 布→剪刀
}
void learn(Move playerMove) {
markov.record(playerMove);
freq.record(playerMove);
}
}
/**
* 游戏主控与统计
*/
public class RockPaperScissors {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
AiEngine ai = new AiEngine();
int wins = 0, losses = 0, draws = 0;
System.out.println("=== 石头剪刀布 AI对战 ===");
System.out.println("输入: 0=石头 1=剪刀 2=布 其他=退出");
while (true) {
System.out.print("\n请出招: ");
String in = sc.nextLine().trim();
if (!in.matches("[012]")) break;
Move player = Move.fromIdx(Integer.parseInt(in));
Move aiMove = ai.decide();
ai.learn(player); // AI学习本轮玩家行为
System.out.printf("你出 %s, AI出 %s", player.label, aiMove.label);
if (player.beats(aiMove)) {
System.out.println(" → 你赢了!");
wins++;
} else if (aiMove.beats(player)) {
System.out.println(" → AI赢了!");
losses++;
} else {
System.out.println(" → 平局");
draws++;
}
int total = wins + losses + draws;
System.out.printf("当前战绩: 胜%d 负%d 平%d (胜率%.1f%%)%n",
wins, losses, draws, total > 0 ? 100.0 * wins / total : 0);
}
System.out.println("游戏结束,感谢对战!");
}
}
算法策略详解
马尔可夫链层在数据量达到3轮后开始生效。当玩家连续两次出”石头”后第三次仍出”石头”的概率若被观测到偏高,矩阵会累积这一偏好,AI下一次在玩家出”石头”后将倾向于出”布”进行反制。置信度阈值设为0.45,避免在数据稀疏时盲目跟从噪声。
频率分析层捕捉的是长期偏好而非相邻轮次的依赖关系。部分玩家潜意识中偏爱某一种手势(如习惯性出”石头”),频率层能在马尔可夫链无显著信号时提供稳定预测。
纳什均衡层是理论保底。当玩家出招完全随机且均匀时,任何确定性策略的长期期望收益都不超过0。此时AI退化为均匀随机,避免被对手利用自身规律。这也体现了博弈论中混合策略均衡的核心思想:当对手无法被预测时,最优应对就是让自己也无法被预测。
复杂度分析
- 时间复杂度:每轮决策和更新均为 O(1)。马尔可夫矩阵查询和频率数组遍历都是固定3个元素,与对局总轮数无关。
- 空间复杂度:O(1)。仅需维护 3×3 转移矩阵和两个长度为3的数组,不随数据量增长。
扩展方向
- 高阶马尔可夫链:将状态从单轮扩展为最近两轮的组合(9种状态),捕捉更长的序列模式。
- 指数衰减权重:给近期转移更高权重,让AI更快适应玩家策略变化。
- 强化学习融合:用Q-Learning动态调整三层策略的切换阈值,根据实际胜率在线优化。
总结
本文用Java实现了一个具备三层预测策略的石头剪刀布AI。马尔可夫链挖掘相邻轮次的转移规律,频率分析捕捉长期偏好,纳什均衡随机策略确保理论下限。整个实现仅百余行代码,却完整展示了从概率建模到博弈决策的算法思维——这正是算法魅力的所在:用简洁的逻辑,在不确定性中寻找优势。