成语接龙游戏代码分析
成语接龙游戏代码分析
以下是该成语接龙游戏项目的目录结构(基于提供的代码文件路径整理):
com.idiomgame/
├── main/
│ └── IdiomGameMain.java // 程序入口类,负责启动GUI
├── ui/
│ ├── MainFrame.java // 主游戏界面,包含游戏交互逻辑
│ └── RankingFrame.java // 排行榜界面,展示玩家成绩排名
├── model/
│ └── PlayerRecord.java // 数据模型类,存储玩家记录信息(姓名、成语数量、用时)
└── service/
├── IdiomService.java // 成语业务逻辑类,处理成语校验、接龙规则、机器响应等核心功能
└── RankingService.java // 排行榜服务类,负责记录的增删、排序及文件持久化
该结构遵循了功能模块化的设计思想,将不同职责的代码分门别类:
main包:程序启动入口ui包:图形用户界面相关类model包:数据模型类,存储业务数据service包:业务逻辑处理类,实现核心功能
功能概述
该代码实现了一个基于Java Swing的成语接龙游戏,主要功能包括:
- 人机对战的成语接龙核心逻辑
- 游戏记录(成语数量、用时)的本地存储与排行榜展示
- 图形化用户界面,支持游戏开始/结束、成绩提交、排行榜查看等操作
重点代码讲解
1. 核心游戏逻辑(IdiomService.java)
// 校验用户输入是否为有效成语
public boolean isValidIdiom(String userIdiom) {
if (userIdiom == null || userIdiom.trim().length() != 4) {
return false; // 非四字文本直接排除
}
String idiom = userIdiom.trim();
// 检查是否在本地词库中
boolean existsInLibrary = false;
for (String localIdiom : LOCAL_IDIOMS) {
if (localIdiom.equals(idiom)) {
existsInLibrary = true;
break;
}
}
// 检查是否已被使用
return existsInLibrary && !usedIdioms.contains(idiom);
}
// 校验接龙是否正确
public boolean checkIdiomConnection(String lastIdiom, String currentIdiom) {
if (lastIdiom == null || currentIdiom == null || lastIdiom.length() != 4 || currentIdiom.length() != 4) {
return false;
}
// 取尾字和首字进行匹配
char lastChar = lastIdiom.charAt(lastIdiom.length() - 1);
char firstChar = currentIdiom.charAt(0);
return lastChar == firstChar;
}
这部分代码实现了游戏的核心规则:
- 验证成语是否为四字且存在于词库中
- 验证接龙是否符合"前一个成语尾字与后一个成语首字相同"的规则
- 管理已使用成语集合,避免重复使用
2. 排行榜功能(RankingService.java)
// 添加新记录并保持前10名
public void addRecord(PlayerRecord record) {
records.add(record);
sortRecords(); // 排序
if (records.size() > MAX_RECORDS) {
records = records.subList(0, MAX_RECORDS); // 只保留前10
}
saveToFile(); // 持久化到文件
}
// 排序规则:按成语数量降序,数量相同按时间升序
private void sortRecords() {
Collections.sort(records, new Comparator<PlayerRecord>() {
@Override
public int compare(PlayerRecord r1, PlayerRecord r2) {
if (r1.getIdiomCount() != r2.getIdiomCount()) {
return Integer.compare(r2.getIdiomCount(), r1.getIdiomCount());
} else {
return Long.compare(r1.getTimeSpent(), r2.getTimeSpent());
}
}
});
}
排行榜功能特点:
- 采用文件存储(CSV格式),支持数据持久化
- 只保留排名前10的记录
- 排序规则合理:先按成语数量(多者优先),再按用时(少者优先)
3. 用户界面(MainFrame.java)
// 游戏流程控制
private void startGame() {
idiomService.resetGame();
historyArea.setText("");
inputField.setText("");
isPlaying = true;
// 更新按钮状态
startButton.setEnabled(false);
endButton.setEnabled(true);
submitButton.setEnabled(true);
// 机器先出第一个成语
lastIdiom = idiomService.getMachineResponse(null);
idiomService.recordUsedIdiom(lastIdiom);
historyArea.append("机器: " + lastIdiom + "\n");
historyArea.append("请接下一个成语(以" + lastIdiom.charAt(lastIdiom.length() - 1) + "开头):\n");
gameStartTime = new Date();
}
界面实现了完整的游戏流程控制:
- 状态管理(游戏中/非游戏中)
- 按钮状态联动
- 游戏历史记录展示
- 时间统计与成绩提交
MVC架构现状
-
模型(Model):
PlayerRecord:存储玩家记录数据LOCAL_IDIOMS:成语数据
-
视图(View):
MainFrame:主游戏界面RankingFrame:排行榜界面
-
控制器(Controller):
IdiomService:游戏逻辑控制RankingService:排行榜数据管理
现状分析:
- 基本实现了MVC分离,业务逻辑与界面展示分离
- 服务类(Service)承担了控制器角色,处理核心逻辑
- 视图层不直接操作数据,通过服务类间接交互
需要改进的地方
-
数据存储:
- 目前使用CSV文件存储,可考虑使用数据库提高性能和可靠性
- 文件操作未处理并发问题,多用户场景下可能出现数据不一致
-
代码结构:
- 成语数据硬编码在代码中,应改为外部文件加载
MainFrame类代码量较大(300行),可拆分出事件处理类
-
功能扩展:
- 缺少难度选择功能
- 没有玩家信息管理(如多用户、头像等)
- 缺少游戏帮助和规则说明
-
异常处理:
- 文件操作异常仅打印堆栈,未给用户友好提示
- 输入验证可更严格(如空格、特殊字符处理)
-
性能优化:
- 成语校验使用线性查找(O(n)),可改为HashSet存储提高效率(O(1))
// 建议改进:初始化时将成语存入HashSet private Set<String> idiomSet; public IdiomService() { idiomSet = new HashSet<>(Arrays.asList(LOCAL_IDIOMS)); } // 优化后验证方法 public boolean isValidIdiom(String userIdiom) { // ... 其他校验 return idiomSet.contains(idiom) && !usedIdioms.contains(idiom); } -
数据库:
成语数据过少,只能判断在本地中的成语,未在数组中的成语会被识别为无效或者已经使用。
重要代码用法详解
1. 成语接龙核心算法
// 机器接龙实现
public String getMachineResponse(String lastIdiom) {
// 机器先出第一个成语
if (lastIdiom == null) {
List<String> unusedIdioms = new ArrayList<>();
for (String idiom : LOCAL_IDIOMS) {
if (!usedIdioms.contains(idiom)) {
unusedIdioms.add(idiom);
}
}
if (unusedIdioms.isEmpty()) return null;
String selected = unusedIdioms.get(random.nextInt(unusedIdioms.size()));
usedIdioms.add(selected);
return selected;
}
// 机器接用户的成语
char targetFirstChar = lastIdiom.charAt(lastIdiom.length() - 1);
List<String> candidateIdioms = new ArrayList<>();
for (String idiom : LOCAL_IDIOMS) {
if (!usedIdioms.contains(idiom) && idiom.charAt(0) == targetFirstChar) {
candidateIdioms.add(idiom);
}
}
if (candidateIdioms.isEmpty()) return null;
String selected = candidateIdioms.get(random.nextInt(candidateIdioms.size()));
usedIdioms.add(selected);
return selected;
}
工作原理:
- 首次调用(lastIdiom为null)时随机选择一个未使用的成语
- 后续调用时,根据上一个成语的尾字筛选符合条件的候选成语
- 从候选成语中随机选择一个并标记为已使用
- 若无候选成语则返回null,表示机器接不上来(用户获胜)
2. 排行榜文件操作
// 从CSV文件加载排行榜
private void loadFromFile() {
File file = new File(RANKING_FILE);
if (!file.exists()) return;
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
br.readLine(); // 跳过标题行
while ((line = br.readLine()) != null) {
String[] parts = line.split(",");
if (parts.length == 3) {
try {
String name = parts[0];
int count = Integer.parseInt(parts[1]);
long time = Long.parseLong(parts[2]);
records.add(new PlayerRecord(name, count, time));
} catch (NumberFormatException e) {
// 忽略格式错误的行
}
}
}
sortRecords();
} catch (IOException e) {
e.printStackTrace();
}
}
文件格式说明:
- 使用UTF-8编码的CSV文件
- 第一行为标题行:"姓名,成语数量,用时(毫秒)"
- 后续每行一条记录,格式为"姓名,数量,时间"
- 加载时进行格式验证,忽略错误行
3. 界面事件处理
// 提交按钮事件处理
submitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
processUserInput();
}
});
// 输入框回车提交
inputField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (isPlaying) {
processUserInput();
}
}
});
事件处理特点:
- 支持按钮点击和回车键两种提交方式
- 通过
isPlaying状态控制输入是否有效 - 统一由
processUserInput()方法处理输入逻辑,避免代码重复
运行示范
- 主界面
![image]()
- 点击开始挑战后,分别为答对和误答的两种情况
![image]()
![image]()
![image]()
- 答题结束后,计入成绩
![image]()
![image]()
总结
该代码实现了一个功能完整的成语接龙游戏,架构清晰,核心逻辑正确。通过MVC模式分离了数据、视图和控制,便于维护和扩展。主要可改进点在于数据存储方式、代码结构优化和功能扩展,特别是将硬编码的成语数据改为外部文件加载,以及使用更高效的数据结构提升性能。







浙公网安备 33010602011771号