成语接龙游戏代码分析

news/2025/11/1 2:35:09/文章来源:https://www.cnblogs.com/Chensy-CSY/p/19181542

成语接龙游戏代码分析

以下是该成语接龙游戏项目的目录结构(基于提供的代码文件路径整理):

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>() {@Overridepublic 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架构现状

  1. 模型(Model)

    • PlayerRecord:存储玩家记录数据
    • LOCAL_IDIOMS:成语数据
  2. 视图(View)

    • MainFrame:主游戏界面
    • RankingFrame:排行榜界面
  3. 控制器(Controller)

    • IdiomService:游戏逻辑控制
    • RankingService:排行榜数据管理

现状分析:

  • 基本实现了MVC分离,业务逻辑与界面展示分离
  • 服务类(Service)承担了控制器角色,处理核心逻辑
  • 视图层不直接操作数据,通过服务类间接交互

需要改进的地方

  1. 数据存储

    • 目前使用CSV文件存储,可考虑使用数据库提高性能和可靠性
    • 文件操作未处理并发问题,多用户场景下可能出现数据不一致
  2. 代码结构

    • 成语数据硬编码在代码中,应改为外部文件加载
    • MainFrame类代码量较大(300行),可拆分出事件处理类
  3. 功能扩展

    • 缺少难度选择功能
    • 没有玩家信息管理(如多用户、头像等)
    • 缺少游戏帮助和规则说明
  4. 异常处理

    • 文件操作异常仅打印堆栈,未给用户友好提示
    • 输入验证可更严格(如空格、特殊字符处理)
  5. 性能优化

    • 成语校验使用线性查找(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);
    }
    
  6. 数据库:
    成语数据过少,只能判断在本地中的成语,未在数组中的成语会被识别为无效或者已经使用。

重要代码用法详解

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() {@Overridepublic void actionPerformed(ActionEvent e) {processUserInput();}
});// 输入框回车提交
inputField.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {if (isPlaying) {processUserInput();}}
});

事件处理特点:

  • 支持按钮点击和回车键两种提交方式
  • 通过isPlaying状态控制输入是否有效
  • 统一由processUserInput()方法处理输入逻辑,避免代码重复

运行示范

  1. 主界面
    image
  2. 点击开始挑战后,分别为答对和误答的两种情况
    image
    image
    image
  3. 答题结束后,计入成绩
    image
    image

总结

该代码实现了一个功能完整的成语接龙游戏,架构清晰,核心逻辑正确。通过MVC模式分离了数据、视图和控制,便于维护和扩展。主要可改进点在于数据存储方式、代码结构优化和功能扩展,特别是将硬编码的成语数据改为外部文件加载,以及使用更高效的数据结构提升性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/952341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

revit api 创建填充区域

revit api 创建填充区域FilteredElementCollector collector = new FilteredElementCollector(_doc);IList<Element> fillRegionTypes = collector.OfClass(typeof(FilledRegionType)).ToElements();IEnumerable…

revit api 判别一个Revit族的主体对象是墙还是基于面

revit api 判别一个Revit族的主体对象是墙还是基于面通过获取Family的BuiltInParameter.FAMILY_HOSTING_BEHAVIOR 参数值来判别。 若参数值为1,则表明族是基于墙的。 若参数值为0,则表明族可能是基于面的。此时若Fam…

金砖应急响应(东部)

金砖应急响应 东部(ak) 1、黑客跳板机ip地址,与受到攻击服务 先netstart -tnlp查看开启了哪些服务 看到开启了80,以及6379 redis redis拿shell的三种方法 写ssh私钥,公钥链接 知道web路径,写webshell 写计划任务…

revit api创建自定义线样式

revit api创建自定义线样式线型在Revit里有LinePatternElement类来表示。 private LinePatternElement CreateLinePatternElement(string patternName){//Create list of segments which define the line patternList&…

revit api扩展存储extensible storage

revit api扩展存储extensible storagepublic Result Execute(ExternalCommandData commandData,ref string message,ElementSet elements){UIDocument uiDoc = commandData.Application.ActiveUIDocument;Document doc…

window下递归删除 node_modules和.next

window下递归删除 node_modules和.nextPosted on 2025-11-01 00:59 kacoro 阅读(0) 评论(0) 收藏 举报window下递归删除 node_modulesGet-ChildItem $Path -Recurse -Force | Where{$_.Name -Match "node_m…

【题解】Codeforces 2062F Traveling Salescat

题目链接 Codeforces 2062F Traveling Salescat 题目大意 给定一张图含 \(N\) 个点,第 \(i\) 点有属性 \(a_i\) 和 \(b_i\),任意两点间都有无向边,边权为 \(max(a_i + b_j, a_j + b_i)\) ,对于特定整数 \(k\) ,求…

又是一年叶落时(九)

前序: 又是一年叶落时 2017.10.20 又是一年叶落时(二) 2018.10.13 又是一年叶落时(三) 2019.10.19 又是一年叶落时(四) 2020.10.25 又是一年叶落时(五) 2021.11.22虽然隔了4年,但终归又回到了这里。在某种程…

revit api 编程实现窗口缩放视图

revit api 编程实现窗口缩放视图UIView.GetWindowRectangle:获取绘图区的范围,坐标值是屏幕坐标 UIView.GetZoomCorners() : 获取绘图区的范围,坐标是模型坐标系 根据上缅的两个数据可以计算出模型坐标与屏幕坐标…

从大厂到中小公司,活下去的五个生存法则

从大厂到中小公司,活下去的五个生存法则降预期,调心态,活下去四年前,我从北京回到西安,离开大厂转战中型公司。这几年,我见过很多大厂人想回家,从一线跳到新一线、二线。有些慢慢习惯也就呆下来了,有些很不适应…

Tauri2-Vite7Admin客户端管理后台|tauri2.9+vue3+element-plus后台系统

最新研发tauri2.9+vite7.1+pinia3桌面版高颜值中后台系统TauriVue3Admin。 vite7-tauri2-admin最新跨平台技术tauri2.9+vite7.1.12+vue3 setup+pinia3+element-plus+echarts搭建电脑端轻量级管理后台系统模板。提供4种…

[Chrome] [插件] 插件推荐

[Chrome] [插件] 插件推荐$(".postTitle2").removeClass("postTitle2").addClass("singleposttitle");创建时间:2025年11月1日00:24:02 更新时间:2025年11月1日00:24:09目录01 翻译1.…

餐饮不仅仅卖食物,更卖的是服务。

餐饮业本质上属于服务业——这或许是我们最常听到,也最值得深思的一句话。顾客来到餐厅,当然希望品尝美味的食物,但他们的需求远不止于此。一顿满意的用餐体验,是味觉享受与心理满足的双重叠加。而后者,往往来自于…

山石防火墙NAT-NAT64

山石防火墙NAT64测试。测试环境如下: 转换前: 3001::1 -> 2011::1 转换后: 21.1.1.1 -> 30.1.1.1Cilent配置 # interface Vlan-interface10 ip address 20.1.1.2 255.255.255.0 # interface LoopBac…

10月31日日记

1.今天学习java 2.明天休息 3.B树和B+树在数据库索引中怎么应用?

【RabbitMQ】RPC模式(请求/回复)

本章目标理解RabbitMQ RPC模式的工作原理和适用场景。掌握回调队列(Callback Queue)和关联ID(Correlation Id)的使用。实现基于RabbitMQ的异步RPC调用。学习RPC模式下的错误处理和超时机制。构建完整的微服务间同步…

圆锥曲线的离心率1

Problem 已知双曲线 $ E: \frac{ x^2 }{ a^2 } - \frac{ y^2 }{ b^2 }=1 \hspace{0.1cm} (a>0 , b>0)$ 的左、右焦点分别为 $ F_1,F_2 $ , 过右焦点作平行于其中一条渐近线的直线交双曲线于点 $ A $ ,若三角形…

由逃逸分析决定 --是什么意思?

Go 中“由逃逸分析决定”的意思核心概念:逃逸分析是编译器在编译期判断一个变量是否会“逃出”其作用域(函数栈帧)的过程。不逃逸 → 可以分配在栈上,函数返回时自动回收,效率更高。 发生逃逸 → 必须分配在堆上,…

[笔记]CSP-S 2025 第二轮 Final Review

仍在更新中。 Hope everything ok. 依照 NOI 大纲 2025 进行整理,删掉了一些考不到的内容。 数据结构 STL deque(双端队列) deque<int> q; q.size(); q.empty(); q.clear(); q.front(),q.back(); q.emplace_b…

文件多、杂乱,想分类也可以、不想分类也能快速找到文档的解决方案

多数人的常态:我的电脑桌面和云盘里堆满了各种文件夹,命名也比较随意。是的,我们是无法将文件管理得井井有条,至少时间一长,文件一多,就会“乱”。我们要承认和适应自己的这种特点。所以 文档大师 使用的“先收集…