西蒙记忆(Simon Says)是一款经典的电子记忆游戏。设备依次播放一段由颜色/音调组成的序列,玩家需要按相同顺序重复该序列。随着回合推进,序列长度逐渐增加,考验玩家的短时记忆与模式识别能力。本文用Java实现完整的西蒙记忆游戏,讲解序列生成、模式匹配与难度自适应算法,包含完整代码与复杂度分析。
游戏机制与核心挑战
游戏核心循环:
1. 系统生成一个随机序列(如:红、绿、蓝、黄…)
2. 向玩家展示该序列
3. 玩家输入自己的答案序列
4. 若完全匹配则进入下一回合(序列长度+1);否则游戏结束
从算法视角看,核心挑战包括:
– 序列生成:如何生成既随机又可预测的序列(用于AI演示或关卡设计)
– 模式匹配:高效比对玩家输入与目标序列
– 难度自适应:根据玩家表现动态调整序列长度与展示速度
– 序列分析:检测并避免连续重复模式,保持游戏趣味性
序列生成算法
基础方案采用伪随机数生成器。进阶方案可引入以下约束:
– 禁止连续重复:相邻元素不得相同(避免玩家靠肌肉记忆点击)
– 模式打散:检测并打断明显的周期性模式(如红绿红绿…)
– 长度自适应:根据历史正确率动态调整增量步长
核心代码实现
import java.util.*;
/**
* 西蒙记忆游戏核心引擎
*/
public class SimonGame {
public enum Color { RED, GREEN, BLUE, YELLOW }
private List<Color> sequence; // 当前目标序列
private Random random;
private int currentRound; // 当前回合
private int score; // 累计得分
private long baseDisplayMs; // 基础展示间隔(毫秒)
private Difficulty difficulty;
public enum Difficulty { EASY, NORMAL, HARD }
public SimonGame(Difficulty diff) {
this.sequence = new ArrayList<>();
this.random = new Random();
this.currentRound = 0;
this.score = 0;
this.difficulty = diff;
this.baseDisplayMs = switch(diff) {
case EASY -> 800;
case NORMAL -> 500;
case HARD -> 300;
};
}
/**
* 生成下一回合的序列(在原有基础上追加新元素)
* 应用约束:禁止与前一个元素相同
*/
public void extendSequence() {
Color last = sequence.isEmpty() ? null : sequence.get(sequence.size() - 1);
Color next;
do {
next = Color.values()[random.nextInt(Color.values().length)];
} while (next == last); // 禁止连续重复
sequence.add(next);
currentRound++;
}
/**
* 生成指定长度的全新序列(用于新游戏)
*/
public void generateSequence(int length) {
sequence.clear();
for (int i = 0; i < length; i++) {
extendSequence();
}
}
/**
* 模式匹配:验证玩家输入序列是否完全匹配目标序列
* @param playerInput 玩家输入的颜色序列
* @return 匹配结果,包含首次错误位置(若完全匹配则为-1)
*/
public MatchResult verifySequence(List<Color> playerInput) {
if (playerInput.size() != sequence.size()) {
return new MatchResult(false, Math.min(playerInput.size(), sequence.size()));
}
for (int i = 0; i < sequence.size(); i++) {
if (playerInput.get(i) != sequence.get(i)) {
return new MatchResult(false, i);
}
}
// 完全匹配,计算得分
int roundScore = calculateRoundScore();
score += roundScore;
return new MatchResult(true, -1);
}
/**
* 计算当前回合得分:基于序列长度与难度系数
*/
private int calculateRoundScore() {
int base = sequence.size() * 10;
int multiplier = switch(difficulty) {
case EASY -> 1;
case NORMAL -> 2;
case HARD -> 3;
};
return base * multiplier;
}
/**
* 获取当前展示间隔(随回合增加而略微缩短,增加挑战性)
*/
public long getDisplayInterval() {
// 每5回合减少10%,最低不低于基础值的50%
double factor = Math.max(0.5, 1.0 - (currentRound / 5) * 0.1);
return (long) (baseDisplayMs * factor);
}
/**
* 检测序列中是否存在过于明显的重复模式(长度>=3)
* 用于关卡质量检测
*/
public boolean hasObviousPattern() {
int n = sequence.size();
for (int period = 2; period <= n / 2; period++) {
boolean isPeriodic = true;
for (int i = period; i < n; i++) {
if (sequence.get(i) != sequence.get(i - period)) {
isPeriodic = false;
break;
}
}
if (isPeriodic) return true;
}
return false;
}
/**
* 获取最近N个元素的频率统计,用于AI策略或难度分析
*/
public Map<Color, Integer> getRecentFrequency(int n) {
Map<Color, Integer> freq = new EnumMap<>(Color.class);
int start = Math.max(0, sequence.size() - n);
for (int i = start; i < sequence.size(); i++) {
freq.merge(sequence.get(i), 1, Integer::sum);
}
return freq;
}
// Getters
public List<Color> getSequence() { return new ArrayList<>(sequence); }
public int getCurrentRound() { return currentRound; }
public int getScore() { return score; }
public record MatchResult(boolean success, int firstErrorIndex) {}
}
/**
* 西蒙记忆AI演示器 - 基于记忆回放的模式复现
*/
public class SimonAI {
private List<SimonGame.Color> memory; // AI的"记忆"缓冲区
private double recallAccuracy; // 回忆准确率(模拟人类记忆衰减)
public SimonAI(double accuracy) {
this.memory = new ArrayList<>();
this.recallAccuracy = Math.max(0.5, Math.min(1.0, accuracy));
}
/**
* 观看序列并尝试记忆
*/
public void observe(List<SimonGame.Color> sequence) {
memory.clear();
for (SimonGame.Color c : sequence) {
// 以recallAccuracy概率正确记忆每个元素
if (Math.random() < recallAccuracy) {
memory.add(c);
} else {
// 记忆错误:随机替换为其他颜色
SimonGame.Color[] values = SimonGame.Color.values();
SimonGame.Color wrong = values[new Random().nextInt(values.length)];
memory.add(wrong);
}
}
}
/**
* 回放记忆的序列
*/
public List<SimonGame.Color> replay() {
return new ArrayList<>(memory);
}
/**
* 带遗忘曲线的记忆模型:序列越长,后期元素的准确率越低
*/
public void observeWithForgetting(List<SimonGame.Color> sequence, double decayRate) {
memory.clear();
for (int i = 0; i < sequence.size(); i++) {
double positionAccuracy = recallAccuracy * Math.exp(-decayRate * i);
if (Math.random() < positionAccuracy) {
memory.add(sequence.get(i));
} else {
SimonGame.Color[] values = SimonGame.Color.values();
memory.add(values[new Random().nextInt(values.length)]);
}
}
}
}
/**
* 游戏主循环与控制台演示
*/
public class SimonMain {
public static void main(String[] args) throws InterruptedException {
SimonGame game = new SimonGame(SimonGame.Difficulty.NORMAL);
Scanner scanner = new Scanner(System.in);
System.out.println("=== 西蒙记忆游戏 ===");
System.out.println("颜色编码: 0=RED, 1=GREEN, 2=BLUE, 3=YELLOW");
while (true) {
game.extendSequence();
List<SimonGame.Color> seq = game.getSequence();
// 展示序列
System.out.print("\n序列: ");
for (SimonGame.Color c : seq) {
System.out.print(c + " ");
Thread.sleep(game.getDisplayInterval());
}
System.out.println();
// 玩家输入
System.out.print("请按顺序输入颜色编号(空格分隔): ");
String[] inputs = scanner.nextLine().trim().split("\s+");
List<SimonGame.Color> playerSeq = new ArrayList<>();
for (String s : inputs) {
playerSeq.add(SimonGame.Color.values()[Integer.parseInt(s)]);
}
// 验证
SimonGame.MatchResult result = game.verifySequence(playerSeq);
if (result.success()) {
System.out.println("正确! 当前得分: " + game.getScore());
} else {
System.out.println("错误! 第 " + (result.firstErrorIndex() + 1) + " 个位置不匹配");
System.out.println("正确序列: " + seq);
System.out.println("最终得分: " + game.getScore());
break;
}
}
scanner.close();
}
}
模式匹配的复杂度分析
序列验证:逐元素比对,时间复杂度 $O(n)$,空间复杂度 $O(1)$(不计输入存储)。
周期性检测:使用KMP算法思想优化的周期检测可将复杂度从 $O(n^2)$ 降至 $O(n)$。核心观察是:若序列存在周期 $p$,则其前缀函数满足 $\pi[n-1] = n – p$。
频率统计:滑动窗口遍历,时间复杂度 $O(n)$。
难度自适应机制
设计基于玩家历史表现的动态难度调整(DDA)系统:
$$\Delta L = \alpha \cdot R_{current} – \beta \cdot (1 – R_{historical})$$
其中 $R$ 为正确率,$\alpha$、$\beta$ 为调节系数。当 $\Delta L > 0$ 时增加序列长度增速,反之减缓。
总结
西蒙记忆游戏的核心算法围绕序列生成、模式匹配与难度自适应展开。本文通过Java实现了一个完整的游戏引擎,包括禁止连续重复的序列生成策略、线性时间的序列比对算法,以及基于遗忘曲线的AI模拟。该框架可扩展为更复杂的模式识别训练系统或认知能力测试工具。