【JAVA】【成语接龙】
JPanel 是典型的中层容器,它没有独立的窗口样式(无标题栏、无边框),也不能直接被操作系统显示,必须作为 “子组件” 添加到顶层容器或其他中层容器中才能显示。JFrame(窗体)和 JPanel(面板)都继承自 Container 类,都具备 add() 方法来容纳其他组件,都是 “容器组件”;在 Swing 中,窗体(如 JFrame)和面板(JPanel)都属于 “容器组件”(Contai
1. 程序入口:确保 GUI 线程安全
package chengyujierong;
import javax.swing.SwingUtilities;
public class IdiomSolitaireMain {
public static void main(String[] args) {
// 在事件调度线程中启动GUI
SwingUtilities.invokeLater(() -> new IdiomSolitaireGame().setVisible(true));
}
}
2.文件处理:加载外部成语库
package chengyujierong;
import javax.swing.*;
import java.io.*;
import java.util.Set;
import java.util.regex.Pattern;
public class IdiomFileHandler {
// 匹配中文汉字的正则表达式,用于提取四字成语
private static final Pattern CHINESE_CHAR_PATTERN = Pattern.compile("[\\u4e00-\\u9fa5]+");
public boolean selectAndLoadIdioms(JFrame parent, Set<String> idiomSet) {//parent 参数的核心作用是 指定 “父窗口”
String filePath = selectIdiomFile(parent);
if (filePath == null) return false;
return loadIdiomsFromFile(filePath, idiomSet, parent);
}
public String selectIdiomFile(JFrame parent) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("选择成语文件");
fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || f.getName().toLowerCase().endsWith(".txt");
}
@Override
public String getDescription() {
return "文本文件 (*.txt)";
}
});
int result = fileChooser.showOpenDialog(parent);
if (result == JFileChooser.APPROVE_OPTION) {
return fileChooser.getSelectedFile().getAbsolutePath();
}
return null;
}
public boolean loadIdiomsFromFile(String filePath, Set<String> idiomSet, JFrame parent) {
if (filePath == null || filePath.isEmpty()) return false;
File file = new File(filePath);
if (!file.exists() || !file.canRead()) {
JOptionPane.showMessageDialog(parent, "文件不存在或无法读取: " + filePath,//弹出一个模态对话框(对话框显示时会阻塞其他界面操作,直到用户关闭它)
"错误", JOptionPane.ERROR_MESSAGE);
return false;
}
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
int count = 0;
idiomSet.clear(); // 清空现有集合,避免重复加载
while ((line = br.readLine()) != null) {
String trimmedLine = line.trim();
// 跳过空行,避免无效数据
if (trimmedLine.isEmpty()) continue;
// 提取行中的第一个四字中文字符串(即核心成语)
String idiom = extractFourCharIdiom(trimmedLine);
// 验证提取的成语是否有效(非空且为四字)
if (idiom != null && idiom.length() == 4) {
idiomSet.add(idiom);
count++;
}
}
JOptionPane.showMessageDialog(parent, "成功加载 " + count + " 个成语",
"加载成功", JOptionPane.INFORMATION_MESSAGE);
return count > 0;
} catch (IOException e) {
JOptionPane.showMessageDialog(parent, "文件读取错误: " + e.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
return false;
}
}
/**
* 从包含成语、拼音、释义的行文本中,提取核心的四字成语
* @param line 原始行文本(如:樽酒论文 拼音:zūnjiǔlùnwén 释义:...)
* @return 提取到的四字成语,提取失败则返回null
*/
private String extractFourCharIdiom(String line) {
// 1. 按空格分割行文本,第一个元素通常是核心成语
String[] parts = line.split("\\s+");
if (parts.length > 0) {
String firstPart = parts[0];
// 2. 验证第一个元素是否全为中文汉字,且长度为4
if (CHINESE_CHAR_PATTERN.matcher(firstPart).matches() && firstPart.length() == 4) {
return firstPart;
}
}
// 3. 若按空格分割失败,直接从行首提取连续的四字汉字(应对特殊格式)
StringBuilder chineseChars = new StringBuilder();
for (char c : line.toCharArray()) {
if (c >= '\u4e00' && c <= '\u9fa5') {
chineseChars.append(c);
// 收集到4个汉字后立即返回(确保是四字成语)
if (chineseChars.length() == 4) {
return chineseChars.toString();
}
} else if (chineseChars.length() > 0) {
// 遇到非汉字且已收集到汉字,停止收集(避免包含后续非成语内容)
break;
}
}
// 未提取到有效四字成语
return null;
}
}
⭐⭐⭐ 3.界面搭建:Swing 组件与布局
package chengyujierong;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class IdiomSolitaireGame extends JFrame {
private IdiomGameLogic gameLogic = new IdiomGameLogic();
private IdiomFileHandler fileHandler = new IdiomFileHandler();
private JLabel statusLabel;
private JLabel scoreLabel;
private JTextField inputField;//单行
private JTextArea historyArea;//多行
public IdiomSolitaireGame() {
setTitle("成语接龙");// Swing 顶层窗口组件(如 JFrame、JDialog)自带的方法
setSize(600, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口时,终止整个程序(退出 JVM,所有线程包括图形线程都停止)
setLocationRelativeTo(null);//若传入 null:窗口会在屏幕正中央显示
// 选择并加载成语文件
if (!fileHandler.selectAndLoadIdioms(this, gameLogic.getIdiomSet())) {
JOptionPane.showMessageDialog(this, "成语文件加载失败,程序将退出", "错误", JOptionPane.ERROR_MESSAGE);
System.exit(0);
}
initComponents();
startNewGame();
}
private void initComponents() {
//布局管理器
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
//子组件
// 顶部状态栏
JPanel topPanel = new JPanel(new GridLayout(2, 1, 5, 5));
statusLabel = new JLabel("游戏开始,请输入以指定字开头的成语");
scoreLabel = new JLabel("分数: 0");
topPanel.add(statusLabel);
topPanel.add(scoreLabel);
// 中间历史记录区域
historyArea = new JTextArea();
historyArea.setEditable(false);
historyArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(historyArea);
scrollPane.setBorder(BorderFactory.createTitledBorder("接龙历史"));
// 底部输入区域
JPanel inputPanel = new JPanel(new BorderLayout(5, 5));
inputField = new JTextField();
JButton submitButton = new JButton("提交");
submitButton.addActionListener(this::checkIdiom);
inputField.addActionListener(this::checkIdiom);
inputPanel.add(new JLabel("请输入成语:"), BorderLayout.WEST);
inputPanel.add(inputField, BorderLayout.CENTER);
inputPanel.add(submitButton, BorderLayout.EAST);
// 控制按钮
JPanel buttonPanel = new JPanel();
JButton resetButton = new JButton("重新开始");
resetButton.addActionListener(e -> startNewGame());
JButton changeFileButton = new JButton("更换成语文件");
changeFileButton.addActionListener(e -> {
if (fileHandler.selectAndLoadIdioms(this, gameLogic.getIdiomSet())) {
startNewGame();
}
});
buttonPanel.add(resetButton);
buttonPanel.add(changeFileButton);
// 组装面板
mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(scrollPane, BorderLayout.CENTER);
mainPanel.add(inputPanel, BorderLayout.SOUTH);
mainPanel.add(buttonPanel, BorderLayout.EAST);
add(mainPanel);
}
private void startNewGame() {
gameLogic.resetGame();
scoreLabel.setText("分数: " + gameLogic.getScore());
historyArea.setText("");
String firstIdiom = gameLogic.getRandomIdiom();
if (firstIdiom != null) {
gameLogic.getUsedIdioms().add(firstIdiom);
gameLogic.setLastCharacter(firstIdiom.substring(firstIdiom.length() - 1));
statusLabel.setText("游戏开始,请输入以 '" + gameLogic.getLastCharacter() + "' 开头的成语");
historyArea.append("系统: " + firstIdiom + "\n");
} else {
statusLabel.setText("没有找到可用的成语,游戏无法开始");
}
inputField.setText("");
inputField.requestFocus();
}
private void checkIdiom(ActionEvent e) {
String userInput = inputField.getText().trim();
if (!gameLogic.validateIdiom(userInput)) {
String message = "";
if (userInput.isEmpty()) {
message = "请输入成语";
} else if (userInput.length() != 4) {
message = "成语必须是四个字";
} else if (!gameLogic.getIdiomSet().contains(userInput)) {
message = "这不是一个有效的成语";
} else if (gameLogic.getUsedIdioms().contains(userInput)) {
message = "这个成语已经使用过了";
} else if (!userInput.startsWith(gameLogic.getLastCharacter())) {
message = "成语必须以 '" + gameLogic.getLastCharacter() + "' 开头";
}
JOptionPane.showMessageDialog(this, message, "提示", JOptionPane.ERROR_MESSAGE);
inputField.setText("");
return;
}
processValidIdiom(userInput);
}
private void processValidIdiom(String idiom) {
gameLogic.getUsedIdioms().add(idiom);
gameLogic.addScore(10);
scoreLabel.setText("分数: " + gameLogic.getScore());
historyArea.append("你: " + idiom + "\n");
gameLogic.setLastCharacter(idiom.substring(idiom.length() - 1));
String systemIdiom = gameLogic.findIdiomByFirstChar(gameLogic.getLastCharacter());
if (systemIdiom != null) {
gameLogic.getUsedIdioms().add(systemIdiom);
historyArea.append("系统: " + systemIdiom + "\n");
gameLogic.setLastCharacter(systemIdiom.substring(systemIdiom.length() - 1));
statusLabel.setText("请输入以 '" + gameLogic.getLastCharacter() + "' 开头的成语");
} else {
statusLabel.setText("恭喜你赢了!系统接不上来了!");
int option = JOptionPane.showConfirmDialog(this,
"恭喜你赢了!得分: " + gameLogic.getScore() + "\n是否再来一局?",
"游戏结束", JOptionPane.YES_NO_OPTION);
if (option == JOptionPane.YES_OPTION) {
startNewGame();
}
}
inputField.setText("");
inputField.requestFocus();
historyArea.setCaretPosition(historyArea.getDocument().getLength());
}
}
组件(Component) 和 容器(Container)
一、容器(Container):承载其他组件,控制布局
容器是能“包含其他组件”的特殊组件,核心作用是组织界面结构、控制子组件的排列方式(通过布局管理器)。项目中用到的容器如下:
| 容器类型 | 具体实例 | 所在位置(类+方法) | 核心作用 |
|---|---|---|---|
JFrame |
IdiomSolitaireGame 实例 |
IdiomSolitaireGame 构造方法 |
顶层窗口容器,承载所有界面元素(整个游戏窗口的载体),负责设置标题、大小、关闭行为。 |
JPanel |
mainPanel |
IdiomSolitaireGame → initComponents |
主中间容器,用 BorderLayout 拆分“北/南/中/东”区域,是所有子容器和组件的“总载体”。 |
JPanel |
topPanel |
IdiomSolitaireGame → initComponents |
顶部子容器,用 GridLayout(2行1列)承载 statusLabel(状态)和 scoreLabel(分数),保证垂直对齐。 |
JPanel |
inputPanel |
IdiomSolitaireGame → initComponents |
底部子容器,用 BorderLayout 承载“输入提示标签+inputField(输入框)+submitButton(提交按钮)”,形成输入单元。 |
JPanel |
buttonPanel |
IdiomSolitaireGame → initComponents |
右侧子容器,默认 FlowLayout 承载 resetButton(重新开始)和 changeFileButton(更换文件),保证按钮水平排列。 |
JScrollPane |
scrollPane |
IdiomSolitaireGame → initComponents |
带滚动条的容器,包裹 historyArea(接龙历史),当历史记录过多时自动显示滚动条,方便查看全部内容。 |
在 Swing 中,窗体(如 JFrame)和面板(JPanel)都属于 “容器组件”(Container)。
都是容器,但定位不同
共性:
JFrame(窗体)和 JPanel(面板)都继承自 Container 类,都具备 add() 方法来容纳其他组件,都是 “容器组件”;
区别:
JFrame 是顶层容器,独立存在,是界面的 “根”;
JPanel 是中间容器,依赖顶层容器,是界面的 “细分区域载体”。
二、组件(Component):直接交互或显示内容
组件是用户能直接看到/操作的元素,核心作用是显示信息或接收用户输入,不能直接包含其他组件(需放在容器中)。项目中用到的组件如下:
1. 显示类组件:仅展示信息,无交互
| 组件类型 | 具体实例 | 所在位置(类+方法) | 核心作用 |
|---|---|---|---|
JLabel |
statusLabel |
IdiomSolitaireGame → initComponents |
显示游戏全局状态(如“请输入以‘亲’开头的成语”),内容随游戏进度动态更新。 |
JLabel |
scoreLabel |
IdiomSolitaireGame → initComponents |
显示玩家当前分数(如“分数: 0”),分数变化时同步更新。 |
JLabel |
输入提示标签 | IdiomSolitaireGame → initComponents |
非独立标签,显示“请输入成语:”,辅助说明 inputField 的用途,与输入框绑定。 |
JTextArea |
historyArea |
IdiomSolitaireGame → initComponents |
多行文本域,显示接龙历史记录(系统和玩家的成语),设置为“不可编辑”,避免用户修改。 |
2. 交互类组件:接收用户操作,触发逻辑
| 组件类型 | 具体实例 | 所在位置(类+方法) | 核心作用 |
|---|---|---|---|
JTextField |
inputField |
IdiomSolitaireGame → initComponents |
单行输入框,接收玩家输入的成语,支持“按回车键提交”(绑定 ActionListener)。 |
JButton |
submitButton |
IdiomSolitaireGame → initComponents |
“提交”按钮,点击后触发 checkIdiom 方法,校验玩家输入的成语是否有效。 |
JButton |
resetButton |
IdiomSolitaireGame → initComponents |
“重新开始”按钮,点击后触发 startNewGame 方法,重置游戏状态(清空历史、分数)。 |
JButton |
changeFileButton |
IdiomSolitaireGame → initComponents |
“更换成语文件”按钮,点击后调用 IdiomFileHandler 重新选择文件,加载新成语库。 |
3. 工具类组件:实现特定功能(文件选择、弹窗)
| 组件类型 | 具体实例 | 所在位置(类+方法) | 核心作用 |
|---|---|---|---|
JFileChooser |
文件选择器对象 | IdiomFileHandler → selectIdiomFile |
弹出可视化文件选择窗口,过滤 .txt 文本文件,获取用户选择的成语文件路径。 |
JOptionPane |
弹窗对象 | 多个类(IdiomSolitaireGame/IdiomFileHandler) |
弹出提示弹窗(如“成语必须是四个字”)、成功弹窗(如“加载200个成语”)、确认弹窗(如“是否再来一局”)。 |
加粗样式 三、核心关系:容器与组件的层级结构
整个界面的“容器-组件”层级清晰,从上到下为:JFrame(顶层窗口)→ mainPanel(主容器)→ 各子容器(topPanel/inputPanel/buttonPanel/scrollPane)→ 具体组件(JLabel/JTextArea/JButton 等)
⭐理解:jpanel不是顶层容器,无法独立显示
顶层容器:
是可以独立存在的 “根容器”,有自己的窗口边框、标题栏、关闭按钮,能直接被操作系统识别并显示在屏幕上。常见的顶层容器只有 3 种:JFrame(主窗口)、JDialog(弹窗)、JApplet(已淘汰的小程序容器)。
中层容器:
JPanel 是典型的中层容器,它没有独立的窗口样式(无标题栏、无边框),也不能直接被操作系统显示,必须作为 “子组件” 添加到顶层容器或其他中层容器中才能显示。
// 你的代码中,JPanel 必须通过 add 方法依附于 JFrame
public class IdiomSolitaireGame extends JFrame { // JFrame 是顶层容器
private void initComponents() {
JPanel mainPanel = new JPanel(); // 中层容器
// ... 创建其他 JPanel 并添加到 mainPanel ...
add(mainPanel); // 将 mainPanel 加入顶层容器 JFrame
}
public static void main(String[] args) {
new IdiomSolitaireGame().setVisible(true); // 显示顶层容器,带动所有 JPanel 显示
}
}
| 对比维度 | 独立标签 | 非独立标签 |
|---|---|---|
| 功能定位 | 显示全局状态、核心数据(独立功能) | 辅助说明特定组件(依赖功能) |
| 布局位置 | 全局区域(如顶部、侧边) | 与依赖组件绑定(如输入框旁、按钮下) |
| 依赖关系 | 不依赖其他组件 | 依赖特定组件(如输入框、列表) |
| 内容变化 | 随游戏逻辑动态更新(如分数、状态) | 通常固定不变(仅辅助说明) |
| 项目中案例 | statusLabel(状态)、scoreLabel(分数) | 输入区“请输入成语:”标签 |
⭐理解:布局管理器
布局管理器:控制组件的排列方式
布局管理器用于定义 JPanel 等容器中组件的位置和大小,避免手动计算坐标(适配不同屏幕尺寸)。项目中主要使用 BorderLayout(边界布局) 和 GridLayout(网格布局),具体位置如下:
1. BorderLayout(边界布局):按“北/南/中/东/西”区域划分
最常用的布局,适合将界面拆分为“顶部/底部/中间/侧边”等大块区域,项目中用在 3 个地方:
主面板 mainPanel(拆分整个窗口)
位置:IdiomSolitaireGame 类的 initComponents 方法
代码片段:
// 创建主面板,使用 BorderLayout,组件间间距 10px
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
// 给主面板加内边距,避免组件贴边
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// 往主面板的不同区域添加子组件
mainPanel.add(topPanel, BorderLayout.NORTH); // 顶部:状态栏+分数栏
mainPanel.add(scrollPane, BorderLayout.CENTER); // 中间:接龙历史(自动填充剩余空间)
mainPanel.add(inputPanel, BorderLayout.SOUTH); // 底部:输入区
mainPanel.add(buttonPanel, BorderLayout.EAST); // 右侧:控制按钮
作用:将整个游戏窗口拆分为 4 个区域,中间区域(接龙历史)会自动占据剩余空间,适配窗口大小变化。
文件选择器的默认布局(隐含使用)
位置:IdiomFileHandler 类的 selectIdiomFile 方法JFileChooser(文件选择器)内部默认使用 BorderLayout 排列“文件列表、按钮栏”等组件,无需手动设置,直接调用 showOpenDialog 即可显示整齐的文件选择窗口。
2. GridLayout(网格布局):按“行×列”网格排列组件
适合组件需要整齐对齐的场景,项目中用在顶部状态栏:
位置:IdiomSolitaireGame 类的 initComponents 方法
代码片段:
// 顶部面板使用 GridLayout,2 行 1 列(2行1列的网格),组件间间距 5px
JPanel topPanel = new JPanel(new GridLayout(2, 1, 5, 5));
// 往网格中添加组件(按顺序填充行,第一行放 statusLabel,第二行放 scoreLabel)
topPanel.add(statusLabel); // 第 1 行:状态提示
topPanel.add(scoreLabel); // 第 2 行:分数显示
作用:让“状态标签”和“分数标签”垂直对齐,各占一行,布局简洁规整。
3. FlowLayout(流式布局):默认布局(隐含使用)JPanel 的默认布局是 FlowLayout(组件按顺序从左到右排列,一行放不下自动换行),项目中用在右侧按钮面板:
位置:IdiomSolitaireGame 类的 initComponents 方法
代码片段:
// 按钮面板未显式设置布局,默认使用 FlowLayout
JPanel buttonPanel = new JPanel();
// 往面板添加按钮(按顺序从左到右排列,间距由 FlowLayout 默认控制)
buttonPanel.add(resetButton); // 第一个按钮:重新开始
buttonPanel.add(changeFileButton); // 第二个按钮:更换成语文件
作用:让两个按钮水平排列,适应按钮文字长度,无需手动调整位置。
⭐理解:事件注册监听机制
事件机制用于“捕捉用户操作(如点击按钮、按回车)”并执行对应逻辑,核心是 “事件源(如按钮)→ 事件监听器(如 ActionListener)→ 事件处理方法” 的流程。项目中主要涉及 ActionListener(动作事件监听,如点击、回车),具体位置如下:
1. 按钮点击事件(JButton 的监听)
提交按钮(submitButton):校验玩家输入
位置:IdiomSolitaireGame 类的 initComponents 方法
代码片段:
JButton submitButton = new JButton("提交");
// 注册 ActionListener 监听器:点击按钮时执行 checkIdiom 方法
submitButton.addActionListener(this::checkIdiom);
// (注:this::checkIdiom 是 Lambda 简化写法,等价于匿名内部类)
流程:用户点击“提交”按钮 → 触发 ActionListener → 调用 checkIdiom 方法校验输入的成语。
(2)重新开始按钮(resetButton):重置游戏
(3)更换成语文件按钮(changeFileButton):重新选择文件
流程:用户点击“更换成语文件” → 弹出文件选择窗口 → 选择成功后加载新成语库 → 启动新游戏。
2. 输入框回车事件(JTextField 的监听)
让用户按回车键时等同于点击“提交”,提升操作便捷性:
位置:IdiomSolitaireGame 类的 initComponents 方法
代码片段:
JTextField inputField = new JTextField();
// 给输入框注册 ActionListener:按回车键时执行 checkIdiom 方法
inputField.addActionListener(this::checkIdiom);
流程:用户在输入框输入成语后按回车 → 触发监听器 → 调用 checkIdiom 方法(和点击“提交”按钮逻辑一致)。
流程总结:
用户操作(点击/回车)→ 事件源生成 ActionEvent 对象 → 监听器捕捉事件 → 执行监听器中定义的处理方法。
4.业务逻辑:游戏规则的实现
package chengyujierong;
import java.util.*;
public class IdiomGameLogic {
private Set<String> idiomSet = new HashSet<>();
private Set<String> usedIdioms = new HashSet<>();
private String lastCharacter = "";
private int score = 0;
// 新增:记录系统连续接不上的次数,避免无限循环(可选防护)
private int systemFailCount = 0;
// 新增:连续接不上的最大次数,超过则强制重置(防止成语库耗尽)
private static final int MAX_SYSTEM_FAIL = 3;
public void resetGame() {
usedIdioms.clear();
score = 0;
lastCharacter = "";
systemFailCount = 0; // 重置失败计数
}
public String getRandomIdiom() {
if (idiomSet.isEmpty()) return null;
List<String> idioms = new ArrayList<>(idiomSet);
return idioms.get(new Random().nextInt(idioms.size()));
}
// 核心修改:系统接不上时,自动重新找新成语开启接龙
public String findIdiomByFirstChar(String firstChar) {
List<String> matchedIdioms = new ArrayList<>();
// 先收集所有符合条件(未使用+首字匹配)的成语
for (String idiom : idiomSet) {
if (!usedIdioms.contains(idiom) && idiom.startsWith(firstChar)) {
matchedIdioms.add(idiom);
}
}
// 情况1:有符合条件的成语,正常返回(并重置失败计数)
if (!matchedIdioms.isEmpty()) {
systemFailCount = 0;
// 随机选一个,避免每次都选第一个
return matchedIdioms.get(new Random().nextInt(matchedIdioms.size()));
}
// 情况2:没有符合条件的成语,触发“无限接”逻辑
systemFailCount++;
// 防护:连续失败超过阈值,重置游戏(避免成语库耗尽)
if (systemFailCount >= MAX_SYSTEM_FAIL) {
resetGame();
String newStartIdiom = getRandomIdiom();
if (newStartIdiom != null) {
usedIdioms.add(newStartIdiom);
lastCharacter = newStartIdiom.substring(newStartIdiom.length() - 1);
// 返回特殊标记,告诉GUI需要提示“系统重新开局”
return "[SYSTEM_RESET]:" + newStartIdiom;
}
}
// 情况3:未达失败阈值,找一个全新未使用的成语开启新接龙
List<String> unusedIdioms = new ArrayList<>();
for (String idiom : idiomSet) {
if (!usedIdioms.contains(idiom)) {
unusedIdioms.add(idiom);
}
}
if (!unusedIdioms.isEmpty()) {
String newIdiom = unusedIdioms.get(new Random().nextInt(unusedIdioms.size()));
usedIdioms.add(newIdiom);
lastCharacter = newIdiom.substring(newIdiom.length() - 1);
// 返回特殊标记,告诉GUI需要提示“系统换词接龙”
return "[SYSTEM_SWITCH]:" + newIdiom;
}
// 极端情况:所有成语都已使用(返回null,让GUI提示重置)
return null;
}
public boolean validateIdiom(String idiom) {
if (idiom == null || idiom.trim().isEmpty()) return false;
if (idiom.length() != 4) return false;
if (!idiomSet.contains(idiom)) return false;
if (usedIdioms.contains(idiom)) return false;
if (!idiom.startsWith(lastCharacter) && !lastCharacter.isEmpty()) return false;
return true;
}
// 新增:处理系统特殊标记的工具方法(给GUI调用,解析返回结果)
public String parseSystemResult(String systemResult, StringBuilder tipMsg) {
if (systemResult == null) {
tipMsg.append("所有成语已使用,建议重新开始游戏!");
resetGame();
return null;
}
// 解析“重新开局”标记
if (systemResult.startsWith("[SYSTEM_RESET]:")) {
String newIdiom = systemResult.replace("[SYSTEM_RESET]:", "");
tipMsg.append("系统连续接不上,已重新开局!新起始成语:");
return newIdiom;
}
// 解析“换词接龙”标记
if (systemResult.startsWith("[SYSTEM_SWITCH]:")) {
String newIdiom = systemResult.replace("[SYSTEM_SWITCH]:", "");
tipMsg.append("系统接不上当前开头,换词接龙!新成语:");
return newIdiom;
}
// 正常结果,无提示
tipMsg.setLength(0);
return systemResult;
}
// Getters and Setters(保持不变)
public Set<String> getIdiomSet() {
return idiomSet;
}
public Set<String> getUsedIdioms() {
return usedIdioms;
}
public String getLastCharacter() {
return lastCharacter;
}
public void setLastCharacter(String lastCharacter) {
this.lastCharacter = lastCharacter;
}
public int getScore() {
return score;
}
public void addScore(int points) {
this.score += points;
}
public int getSystemFailCount() {
return systemFailCount;
}
public void setSystemFailCount(int systemFailCount) {
this.systemFailCount = systemFailCount;
}
}
更多推荐


所有评论(0)