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 IdiomSolitaireGameinitComponents 主中间容器,用 BorderLayout 拆分“北/南/中/东”区域,是所有子容器和组件的“总载体”。
JPanel topPanel IdiomSolitaireGameinitComponents 顶部子容器,用 GridLayout(2行1列)承载 statusLabel(状态)和 scoreLabel(分数),保证垂直对齐。
JPanel inputPanel IdiomSolitaireGameinitComponents 底部子容器,用 BorderLayout 承载“输入提示标签+inputField(输入框)+submitButton(提交按钮)”,形成输入单元。
JPanel buttonPanel IdiomSolitaireGameinitComponents 右侧子容器,默认 FlowLayout 承载 resetButton(重新开始)和 changeFileButton(更换文件),保证按钮水平排列。
JScrollPane scrollPane IdiomSolitaireGameinitComponents 带滚动条的容器,包裹 historyArea(接龙历史),当历史记录过多时自动显示滚动条,方便查看全部内容。

在 Swing 中,窗体(如 JFrame)和面板(JPanel)都属于 “容器组件”(Container)。
都是容器,但定位不同
共性:
JFrame(窗体)和 JPanel(面板)都继承自 Container 类,都具备 add() 方法来容纳其他组件,都是 “容器组件”;
区别:
JFrame 是顶层容器,独立存在,是界面的 “根”;
JPanel 是中间容器,依赖顶层容器,是界面的 “细分区域载体”。

二、组件(Component):直接交互或显示内容
组件是用户能直接看到/操作的元素,核心作用是显示信息接收用户输入,不能直接包含其他组件(需放在容器中)。项目中用到的组件如下:

1. 显示类组件:仅展示信息,无交互

组件类型 具体实例 所在位置(类+方法) 核心作用
JLabel statusLabel IdiomSolitaireGameinitComponents 显示游戏全局状态(如“请输入以‘亲’开头的成语”),内容随游戏进度动态更新。
JLabel scoreLabel IdiomSolitaireGameinitComponents 显示玩家当前分数(如“分数: 0”),分数变化时同步更新。
JLabel 输入提示标签 IdiomSolitaireGameinitComponents 非独立标签,显示“请输入成语:”,辅助说明 inputField 的用途,与输入框绑定。
JTextArea historyArea IdiomSolitaireGameinitComponents 多行文本域,显示接龙历史记录(系统和玩家的成语),设置为“不可编辑”,避免用户修改。

2. 交互类组件:接收用户操作,触发逻辑

组件类型 具体实例 所在位置(类+方法) 核心作用
JTextField inputField IdiomSolitaireGameinitComponents 单行输入框,接收玩家输入的成语,支持“按回车键提交”(绑定 ActionListener)。
JButton submitButton IdiomSolitaireGameinitComponents “提交”按钮,点击后触发 checkIdiom 方法,校验玩家输入的成语是否有效。
JButton resetButton IdiomSolitaireGameinitComponents “重新开始”按钮,点击后触发 startNewGame 方法,重置游戏状态(清空历史、分数)。
JButton changeFileButton IdiomSolitaireGameinitComponents “更换成语文件”按钮,点击后调用 IdiomFileHandler 重新选择文件,加载新成语库。

3. 工具类组件:实现特定功能(文件选择、弹窗)

组件类型 具体实例 所在位置(类+方法) 核心作用
JFileChooser 文件选择器对象 IdiomFileHandlerselectIdiomFile 弹出可视化文件选择窗口,过滤 .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;
    }
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐