成语接龙游戏代码分析
成语接龙游戏代码分析
以下是该成语接龙游戏项目的目录结构(基于提供的代码文件路径整理):
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号