很久之前就想开发一款自己发明玩法的棋类游戏,暂时起个简单直接的名字,就叫“夹挑棋”吧。

最原本起名“7-14”,因为默认7*7的棋盘,胜利条件是把对方7个棋子变成自己的,就是14个,所以起这个名字,还有一个原因也是想模仿“2048”,简单好理解。但是这样没有体现出玩法,就暂定“夹挑棋”吧,某种程度上致敬黑白棋、五子棋,甚至围棋。

一、简单描述玩法就是:

1、棋盘规格:默认状态7*7的棋盘,黑白双方各有一行棋子。
2、移动规则:只能移动现有棋子,按照直线移动,直到遇到其他棋子,且不能跨子。
3、变子规则:移动棋子后,(1)形成“夹”规则,即己方2个棋子夹住对方1个棋子,则对方这个棋子变为我方棋子;(2)形成“挑”规则,即己方1个棋子,插入对方2个棋子中间,则对方这2个棋子变为我方棋子。

游戏规则说明

很早之前用Unity搭配C#实现了简单玩法,还做成了个手机小游戏,但只是给自己和周围亲友玩,没有发布各类平台。实现了最基本的垂直、水平方向移动和变子功能,以及一个十分简单的人机。按照原定设想,还要实现斜方向的规则,并且要有3个等级的人机AI,增加对战趣味和策略性。

但工作后实在太忙,这个事情就暂时搁置。直到最近各类AI大模型展露出代码编程开发游戏的潜力,尤其是这次Deepseek横空出世,我的这个想法又开始蠢蠢欲动。现在考虑的就是只实现玩法,也不寻求发布,用最简单、最快速、最容易部署、且最方便大家检验的模式,即用1个HTML代码文件,实现网页直接打开即玩。

经过两周工作和生活之余挤出的闲暇时间,我利用Deepseek,挑战不写一行代码,纯提需求和修改意见(游戏中所有页面、动画、UI控件等),历经多次斗智斗勇,终于完成此游戏的html代码1.0版本,并且支持移动端游玩(棋盘尽量小一些)。

在电脑上如果棋盘太大,导致棋盘被遮挡或者变形,可以修改页面大小(快捷键:Ctrl+鼠标滚轮滚动)。

二、此1.0版本基本实现了以下功能:

1、3个等级的“人机对战”:初级:随机移动;中级:贪心算法;高级:剪枝算法。其中高级AI思考时间可能比较长,尤其是棋子较多时。
2、“斜向变子”、“斜向移动”:游戏默认状态为只能水平、垂直运动以及变子,勾选此项,则立刻开启斜向规则,即可以斜方向直线移动和变子。
3、“辐射连击”:触发变子规则后,变化为己方的棋子,认同为也落下1子,可以触发后续变子规则,直到没有可变化的棋子为止(增加策略性、可玩性和“爽感”)。
4、“自定义棋盘”功能:可以自己Diy棋盘大小和棋子排布,棋盘最大19*19,双方棋子行数一致,且排布最少留有1行可移动空间。具体布局还能通过选择下方黑、白、灰(消除)棋子灵活设置。
5、“随机棋盘”:棋盘规格、棋子行数均随机生成。
6、“打乱棋盘”:将当前棋盘打乱,不改变棋盘规格和双方棋子数量。
7、“重新开始”:按照最近一次“棋盘设置”功能内确认的棋类状态重新开始(包括自定义、随即和打乱),如果要恢复默认棋盘,需要在“棋盘设置”界面选择。
8、“悔棋”功能:恢复为本次落子之前的状态。
9、存档功能:点击“保存”,记录当前棋盘状态;点击“读取”,加载最近一次保存的棋盘状态。

游戏主界面↓

游戏主界面

标题棋盘设置选项↓

棋盘设置选项

自定义棋盘界面↓

自定义棋盘功能

三、后续想法

1、同时还有很多其他需求没有实现,如“持续落子”功能(在勾选后,可以由一方持续落子)、人机对战增加“AI大模型对手”、以及联网对战等等;获胜条件也可以再斟酌,现行的比较简单直接,或许可以规定时间、步数、或达到某一条件(如占领对方最后一排等)。
2、也可以分阶段进行游戏:第一阶段,双方轮流落子,在整个棋盘,或者在一定区域内(如自己半区),各落一定数量的棋子,此时不触发变子规则;第二阶段,由落子的后手方先移动棋子,此时则需要按基本规则进行游戏。
3、甚至直接改变游戏模式,如模仿三消类游戏(类比“开心消消乐”),通过布满双方棋子的随机棋盘(或许可以有更多颜色或图案,可支持多个棋手游戏),通过棋手移动相邻棋子的形式,触发变子规则,达到消除对手棋子的目的。其他还可以结合泡泡龙模式、俄罗斯方块模式等,就是说通过这种简单易上手的基础规则,触类旁通,实现多种游戏效果。

以上都是一些简单想法,希望抛砖引玉,让更多感兴趣的人进行交流。因时间紧张,且不是专业人员,此版本可能还存在不少BUG,尤其是移动端,测试较少,请多多谅解,主要以学习为主。而且历经多次跟Deepseek对话修改,代码中应有不少冗余部分,请自行鉴别。

四、完整代码

整个html代码如下,直接复制进文本文件,改后缀为html即可打开。(或者CSDN页面可以直接运行打开?不清楚,反正Deepseek对话页面是可以的。之后试一下CSDN的InsCode功能。)

<!DOCTYPE html>
<html>

<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>夹挑棋1.0</title>
    <style>
        body {
            margin: 0;
            padding: 10px;
            touch-action: manipulation;
            font-family: "微软雅黑", Arial, sans-serif;
            background: #f0f0f0;
            display: flex;
            flex-direction: column;
            height: 100vh;
        }

        #status {
            text-align: center;
            font-size: 24px;
            padding: 15px;
            background: linear-gradient(145deg, #ffffff, #e6e6e6);
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            z-index: 100;
            flex-shrink: 0;
            border-radius: 8px;
            margin: 5px;
        }

        #score {
            text-align: center;
            padding: 10px;
            background: #fff;
            margin: 5px 0;
            border-radius: 6px;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }

        #board-container {
            flex: 1;
            overflow: auto;
            position: relative;
            margin: 10px 0 60px;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #f8f8f8;
            border-radius: 10px;
            box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
            margin-top: 60px !important;
            align-items: flex-start;
            /* 顶部对齐 */
        }

        #board {
            border-collapse: collapse;
            background: #fff;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            touch-action: none;
            border-radius: 8px;
            overflow: hidden;
        }

        td {
            border: 1px solid #e0e0e0;
            position: relative;
            width: 40px;
            height: 40px;
            transition: background 0.2s;
            background: #fcfcfc;
        }

        .piece {
            width: 80%;
            height: 80%;
            border-radius: 50%;
            position: absolute;
            top: 10%;
            left: 10%;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            cursor: pointer;
            box-sizing: border-box;
        }

        .black {
            background: #333;
            box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2),
                inset 0 -2px 4px rgba(0, 0, 0, 0.2);
        }

        .white {
            background: #fff;
            border: 1px solid #d0d0d0;
            box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1),
                inset 0 -2px 4px rgba(255, 255, 255, 0.5);
        }

        .selected {
            transform: scale(1.1);
            box-shadow: 0 0 0 3px #ff4444,
                0 4px 12px rgba(255, 68, 68, 0.3);
            animation: selectBlink 0.5s ease-in-out 1;
        }

        @keyframes selectBlink {
            0% {
                box-shadow: 0 0 0 0px #ff4444;
            }

            50% {
                box-shadow: 0 0 0 8px rgba(255, 68, 68, 0.3);
            }

            100% {
                box-shadow: 0 0 0 3px #ff4444;
            }
        }

        .last-move {
            box-shadow: 0 0 0 3px #4CAF50;
        }

        .valid-move {
            background: #c8e6c9 !important;
        }

        .flipping {
            animation: flip 0.5s ease-in-out;
        }

        .move-arrow {
            position: absolute;
            height: 6px;
            background: linear-gradient(to right,
                    rgba(0, 255, 136, 0.8) 0%,
                    rgba(0, 204, 102, 0.6) 70%,
                    transparent 100%);
            transform-origin: 0 50%;
            pointer-events: none;
            border-radius: 3px;
            filter: drop-shadow(0 2px 4px rgba(0, 200, 100, 0.3));
        }

        .move-arrow::before {
            content: '';
            position: absolute;
            right: -10px;
            top: -8px;
            width: 0;
            height: 0;
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-left: 14px solid rgba(0, 204, 102, 0.8);
        }

        .move-arrow::after {
            content: '';
            position: absolute;
            right: -10px;
            top: -5px;
            width: 0;
            height: 0;
            border-top: 7px solid transparent;
            border-bottom: 7px solid transparent;
            border-left: 10px solid rgba(0, 255, 136, 0.8);
        }

        .clear-piece {
            background: #f0f0f0;
            border: 2px dashed #999 !important;
            position: relative;
        }

        .clear-piece::after {
            content: "×";
            position: absolute;
            font-size: 24px;
            color: #666;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-weight: bold;
        }

        @keyframes flip {
            0% {
                transform: rotateY(0deg) scale(1);
            }

            50% {
                transform: rotateY(180deg) scale(0.5);
            }

            100% {
                transform: rotateY(360deg) scale(1);
            }
        }

        /* 控件样式 */
        #controls {
            flex-shrink: 0;
            background: white;
            padding: 10px;
            box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.1);
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
            gap: 8px;
            border-radius: 8px;
            margin: 5px;
        }

        .control-group {
            display: flex;
            flex-direction: column;
            margin: 0 5px;
            gap: 5px;
        }

        .center-controls {
            display: flex;
            flex-direction: column;
            margin: 0 5px;
            gap: 5px;
            align-items: center;
        }


        button,
        select,
        input {
            padding: 8px 10px;
            margin: 2px;
            border: 1px solid #ddd;
            border-radius: 6px;
            background: #f8f8f8;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.2s;
        }

        button:hover,
        select:hover {
            background: #f0f0f0;
            transform: translateY(-1px);
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
        }

        input[type="checkbox"] {
            width: 18px;
            height: 18px;
            vertical-align: middle;
            margin-right: 5px;
        }

        .modal-dialog {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 12px;
            z-index: 1000;
            display: none;
            flex-direction: column;
            gap: 12px;
            min-width: 280px;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
        }


        #custom-controls {
            display: none;
            position: fixed;
            bottom: 0;
            left: 0;
            right: 0;
            background: white;
            padding: 15px;
            box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.1);
            grid-template-columns: repeat(4, 1fr);
            gap: 10px;
            align-items: start;
            max-height: 45vh;
            overflow-y: auto;
            border-radius: 12px 12px 0 0;
        }

        /* 自定义界面专用样式 */
        #custom-controls[style*="display: grid"]~#board-container {
            margin-top: 20px;
            margin-bottom: 150px;
            /* 加大底部留白 */
        }

        /* 移动端适配 */
        @media (max-width: 600px) {
            #board-container {
                margin: 5px 0 80px;
            }

            #custom-controls[style*="display: grid"]~#board-container {
                margin-bottom: 200px;
            }
        }

        /* 棋盘包裹层适配 */
        .board-wrapper {
            width: 90%;
            max-width: 90vh;
            /* 防止过大 */
            padding-top: 90%;
        }


        .custom-section {
            display: flex;
            flex-direction: column;
            gap: 8px;
            align-items: center;
            min-width: 120px;
        }

        .color-option {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border: 2px solid transparent;
            cursor: pointer;
            position: relative;
            margin: 0 3px;
            transition: transform 0.2s;
        }

        .color-option.selected {
            transform: scale(1.15);
            box-shadow: 0 0 0 2px #4CAF50;
        }

        .slider-group {
            display: flex;
            flex-direction: column;
            gap: 2px;
            width: 100%;
        }

        .slider-group label {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin: 1px 0;
        }

        input[type="range"] {
            width: 80px;
            height: 6px;
            margin: 0 2px;
        }

        input[type="number"] {
            width: 50px;
            padding: 4px;
            margin: 0 2px;
        }

        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.3);
            display: none;
            backdrop-filter: blur(2px);
        }

        .color-row {
            display: flex;
            gap: 16px;
            margin-bottom: 8px;
        }

        .button-group {
            display: flex;
            flex-direction: column;
            gap: 6px;
            width: 100%;
        }

        /* 规则弹窗样式 */
        #rule-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            background: rgba(0, 0, 0, 0.9);
            display: none;
            /* 默认隐藏 */
            justify-content: center;
            align-items: center;
            z-index: 10000;
            /* 确保在棋盘上方 */
        }

        .rule-content {
            background: #fff;
            padding: 2rem;
            border-radius: 1rem;
            width: min(90%, 700px);
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
            position: relative;
        }

        .rule-content .piece {
            width: 30px;
            height: 30px;
            position: static;
            margin: 5px;
        }

        .rule-section {
            margin: 1.5rem 0;
            padding: 1rem;
            background: #f8f9fa;
            border-radius: 8px;
        }

        .demo-row {
            display: flex;
            gap: 10px;
            align-items: center;
            margin: 1rem 0;
        }

        .start-btn {
            background: #4CAF50;
            color: white;
            padding: 12px 24px;
            border: none;
            border-radius: 25px;
            font-size: 1.1rem;
            cursor: pointer;
            margin-top: 1rem;
        }
    </style>
</head>

<body>
    <div id="rule-modal" class="modal-overlay">
        <div class="rule-content">
            <h2>📖 游戏规则说明</h2>

            <div class="rule-section">
                <h3>🎯 游戏目标</h3>
                <p>通过移动棋子,将对方棋子转化为己方颜色。当一方无法移动或棋子数为零时,另一方获胜。</p>
            </div>

            <div class="rule-section">
                <h3>🚶 移动规则</h3>
                <ul>
                    <li>横向/纵向直线移动,可跨越多个空格</li>
                    <li>遇到棋子必须停止</li>
                    <li><em>斜向移动需开启对应选项</em></li>
                </ul>
            </div>
            <div class="rule-section">
                <h3>🔄 变子规则</h3>
                <div class="example">
                    <div class="demo-row">
                        <div class="piece black"></div>
                        <div class="piece white"></div>
                        <div class="piece black"></div>
                        <span></span>
                        <div class="piece black"></div>
                        <div class="piece black"></div>
                        <div class="piece black"></div>
                    </div>
                    <p>"夹"规则:当己方棋子在两侧时,中间敌方棋子转化</p>
                </div>
                <div class="example">
                    <div class="demo-row">
                        <div class="piece white"></div>
                        <div class="piece black"></div>
                        <div class="piece white"></div>
                        <span></span>
                        <div class="piece black"></div>
                        <div class="piece black"></div>
                        <div class="piece black"></div>
                    </div>
                    <p>"挑"规则:当己方棋子在中间时,两侧敌方棋子转化</p>
                </div>
            </div>

            <button class="start-btn" onclick="Game.hideRules()">开始游戏</button>
        </div>
    </div>
    <div id="status">当前回合:黑方</div>
    <div id="score">黑方 0 : 0 白方</div>
    <div id="board-container">
        <table id="board"></table>
    </div>

    <!-- 主控栏 -->
    <div id="controls">
        <div class="control-group">
            <select id="gameMode">
                <option value="pve">人机对战</option>
                <option value="pvp">双人对战</option>
            </select>
            <div id="aiLevelGroup" class="control-group">
                <select id="aiLevel">
                    <option value="1">初级 AI</option>
                    <option value="2">中级 AI</option>
                    <option value="3">高级 AI</option>
                </select>
            </div>
        </div>

        <div class="control-group">
            <button onclick="Game.showBoardSettings()">棋盘设置</button>
            <button onclick="Game.showRules()">游戏玩法</button>
        </div>

        <div class="control-group">
            <button onclick="Game.handleRestart()">重新开始</button>
            <button onclick="Game.undoMove()">悔棋</button>
        </div>

        <div class="control-group center-controls">
            <label><input type="checkbox" id="diagonalRule"> 斜向变子</label>
            <label><input type="checkbox" id="diagonalMove"> 斜向移动</label>
            <label><input type="checkbox" id="radiationMode" onchange="Game.config.radiation = this.checked;">
                辐射连击</label>
        </div>

        <div class="control-group">
            <button onclick="Game.saveGame()">保存</button>
            <button onclick="Game.loadGame()">读取</button>
        </div>
    </div>

    <!-- 棋盘设置菜单 -->
    <div class="modal-overlay" id="board-settings-overlay"></div>
    <div class="modal-dialog" id="board-settings-menu">
        <button onclick="Game.startCustomBoard()">自定义棋盘</button>
        <button onclick="Game.handleRandomBoard()">随机棋盘</button>
        <button onclick="Game.handleShuffleBoard()">打乱棋盘</button>
        <button onclick="Game.handleDefaultBoard()">默认棋盘</button>
        <button onclick="Game.hideBoardSettings()">取消</button>
    </div>

    <!-- 自定义棋盘控制栏 -->
    <div id="custom-controls">
        <div class="custom-section">
            <div class="slider-group">
                <label>宽度:
                    <input type="range" id="customWidthSlider" min="3" max="19" value="7">
                    <input type="number" id="customWidth" min="3" max="19" value="7">
                </label>
                <label>高度:
                    <input type="range" id="customHeightSlider" min="3" max="19" value="7">
                    <input type="number" id="customHeight" min="3" max="19" value="7">
                </label>
                <label>行数:
                    <input type="range" id="pieceRowsSlider" min="1" max="9" value="1">
                    <input type="number" id="pieceRows" min="1" max="9" value="1">
                </label>
            </div>
        </div>

        <div class="custom-section">
            <div class="color-row">
                <div class="color-option black selected" onclick="Game.selectColor(0)"></div>
                <div class="color-option white" onclick="Game.selectColor(1)"></div>
                <div class="color-option clear-piece" onclick="Game.selectColor(null)"></div>
            </div>
            <div class="control-group">
                <label><input type="radio" name="firstPlayer" value="0" checked> 黑先</label>
                <label><input type="radio" name="firstPlayer" value="1"> 白先</label>
            </div>
        </div>

        <div class="custom-section">
            <div class="button-group">
                <button onclick="Game.clearBoard()">清空棋盘</button>
                <button onclick="Game.restoreDefault()">恢复默认</button>
            </div>
        </div>

        <div class="custom-section">
            <div class="button-group">
                <button onclick="Game.confirmCustom()" style="background:#4CAF50;color:white;">确认布局</button>
                <button onclick="Game.cancelCustom()" style="background:#f44336;color:white;">取消布局</button>
            </div>
        </div>
    </div>

    <!-- 游戏模式切换确认 -->
    <div class="modal-overlay" id="mode-overlay" style="display:none;"></div>
    <div class="modal-dialog" id="mode-dialog" style="display:none;">
        <p>是否保留当前棋局?</p>
        <button onclick="Game.handleModeChange('keep')">保留棋盘</button>
        <button onclick="Game.handleModeChange('restart')">重新开始</button>
        <button onclick="Game.handleModeChange('cancel')">取消</button>
    </div>
    <script>
        class Game {
            static config = {
                mode: 'pve',
                aiLevel: 1,
                boardWidth: 7,
                boardHeight: 7,
                pieceRows: 1,
                diagonalRule: false,
                diagonalMove: false,
                radiation: false,
                moveHistory: [],
                scores: [0, 0],
                currentPlayer: 0,
                board: [],
                processing: false,
                selectedPiece: null,
                customizing: false,
                customColor: 0,
                originalState: null,
                lastCustomState: null,
                lastMove: null
            };

            static init() {
                // 在初始化时显示游戏规则
                if (!localStorage.getItem('rulesShown')) {
                    this.showRules();
                    localStorage.setItem('rulesShown', 'true');
                }
                this.resetBoard(true);
                this.bindEvents();
                this.updateView();
            }


            static showRules() {
                document.getElementById('rule-modal').style.display = 'flex';
                document.body.style.overflow = 'hidden'; // 禁止背景滚动
            }

            static hideRules() {
                document.getElementById('rule-modal').style.display = 'none';
                document.body.style.overflow = 'auto';
            }


            static getCurrentState() {
                return {
                    boardWidth: this.config.boardWidth,
                    boardHeight: this.config.boardHeight,
                    pieceRows: this.config.pieceRows,
                    board: JSON.parse(JSON.stringify(this.config.board)),
                    currentPlayer: this.config.currentPlayer,
                    firstPlayer: this.config.currentPlayer
                };
            }

            static resetBoard(initial = false) {
                const { boardWidth, boardHeight, pieceRows } = this.config;

                // 自动修正行数值
                const validRows = Math.min(pieceRows, Math.floor((boardHeight - 1) / 2));
                this.config.pieceRows = Math.max(validRows, 1);

                // 初始化棋盘数组
                this.config.board = Array.from({ length: boardHeight }, (_, i) =>
                    Array.from({ length: boardWidth }, (_, j) => {
                        if (initial) {
                            if (i < this.config.pieceRows) return 1;    // 顶部白棋
                            if (i >= boardHeight - this.config.pieceRows) return 0; // 底部黑棋
                        }
                        return null; // 中间区域为空
                    })
                );

                // 强制刷新视图
                this.updateView();
            }

            static updateView() {

                // 尺寸合法性检查
                this.config.boardWidth = Math.max(3, Math.min(19, this.config.boardWidth));
                this.config.boardHeight = Math.max(3, Math.min(19, this.config.boardHeight));

                const boardElement = document.getElementById('board');
                const { boardWidth, boardHeight, board, lastMove } = this.config;

                boardElement.innerHTML = '';
                for (let i = 0; i < boardHeight; i++) {
                    const row = boardElement.insertRow();
                    for (let j = 0; j < boardWidth; j++) {
                        const cell = row.insertCell();
                        cell.dataset.row = i;
                        cell.dataset.col = j;
                        cell.onclick = (e) => this.handleClick(i, j);

                        if (board[i][j] !== null) {
                            const piece = document.createElement('div');
                            piece.className = `piece ${board[i][j] ? 'white' : 'black'}`;
                            if (lastMove && lastMove[0] === i && lastMove[1] === j) {
                                piece.classList.add('last-move');
                            }
                            cell.appendChild(piece);
                        }
                    }
                }

                document.getElementById('score').textContent =
                    `黑方 ${this.config.scores[0]} : ${this.config.scores[1]} 白方`;
                document.getElementById('status').textContent =
                    `当前回合:${this.config.currentPlayer ? '白方' : '黑方'}`;

                const container = document.getElementById('board-container');
                const cellWidth = Math.min(container.offsetWidth / boardWidth - 2, 60);
                const cellHeight = Math.min(container.offsetHeight / boardHeight - 2, 60);
                document.querySelectorAll('#board td').forEach(td => {
                    td.style.width = `${cellWidth}px`;
                    td.style.height = `${cellHeight}px`;
                });
            }

            static handleClick(row, col) {
                if (this.config.processing) return;

                if (this.config.customizing) {
                    this.config.board[row][col] = this.config.customColor;
                    this.updateView();
                    return;
                }

                if (this.config.board[row][col] === this.config.currentPlayer) {
                    this.selectPiece(row, col);
                }
                else if (this.config.selectedPiece &&
                    document.querySelector(`[data-row="${row}"][data-col="${col}"]`).classList.contains('valid-move')) {
                    this.movePiece(row, col);
                }
            }

            static selectPiece(row, col) {
                this.clearSelection();
                this.config.selectedPiece = { row, col };
                document.querySelector(`[data-row="${row}"][data-col="${col}"] .piece`)
                    .classList.add('selected');
                this.showValidMoves(row, col);
            }

            static showValidMoves(row, col) {
                this.clearValidMoves();
                const directions = this.config.diagonalMove ?
                    [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]] :
                    [[-1, 0], [1, 0], [0, -1], [0, 1]];

                directions.forEach(([dx, dy]) => {
                    let steps = 1;
                    while (true) {
                        const newRow = row + dx * steps;
                        const newCol = col + dy * steps;
                        if (!this.isValid([newRow, newCol])) break;
                        if (this.config.board[newRow][newCol] !== null) break;

                        const cell = document.querySelector(`[data-row="${newRow}"][data-col="${newCol}"]`);
                        cell.classList.add('valid-move');
                        steps++;
                    }
                });
            }

            static async movePiece(newRow, newCol) {
                const { selectedPiece, board } = this.config;
                if (!selectedPiece) return;

                this.config.processing = true;
                this.config.moveHistory.push({
                    board: JSON.parse(JSON.stringify(board)),
                    scores: [...this.config.scores],
                    player: this.config.currentPlayer
                });

                board[selectedPiece.row][selectedPiece.col] = null;
                board[newRow][newCol] = this.config.currentPlayer;
                this.config.lastMove = [newRow, newCol];

                const startCell = document.querySelector(`[data-row="${selectedPiece.row}"][data-col="${selectedPiece.col}"]`);
                const endCell = document.querySelector(`[data-row="${newRow}"][data-col="${newCol}"]`);
                this.createPathAnimation(startCell, endCell);

                this.updateView();

                await this.processConversion(newRow, newCol);

                this.config.currentPlayer = 1 - this.config.currentPlayer;
                this.clearSelection();
                this.updateView();

                if (this.config.mode === 'pve' && this.config.currentPlayer === 1) {
                    setTimeout(() => this.aiTurn(), 500);
                }

                await this.checkGameOver();
                this.config.processing = false;
            }

            static createPathAnimation(startCell, endCell) {
                const getCenter = cell => {
                    const rect = cell.getBoundingClientRect();
                    return {
                        x: rect.left + rect.width / 2 + window.scrollX,
                        y: rect.top + rect.height / 2 + window.scrollY
                    };
                };

                const start = getCenter(startCell);
                const end = getCenter(endCell);
                const dx = end.x - start.x;
                const dy = end.y - start.y;
                const angle = Math.atan2(dy, dx);
                const distance = Math.hypot(dx, dy);

                const arrow = document.createElement('div');
                arrow.className = 'move-arrow';
                arrow.style.cssText = `
            width: ${distance}px;
            left: ${start.x}px;
            top: ${start.y}px;
            transform: rotate(${angle}rad);
            transform-origin: 0 50%;
        `;

                document.body.appendChild(arrow);
                setTimeout(() => arrow.remove(), 300);
            }

            static async processConversion(startRow, startCol) {
                const radiationMode = this.config.radiation;
                const currentPlayer = this.config.currentPlayer; // 锁定当前玩家状态
                const enemyPlayer = 1 - currentPlayer;
                let totalFlipped = [];

                // 使用广度优先搜索处理连锁反应
                const queue = [{ row: startRow, col: startCol }];
                const visited = new Set();

                while (queue.length > 0) {
                    const { row, col } = queue.shift();
                    const key = `${row},${col}`;
                    if (visited.has(key)) continue;
                    visited.add(key);

                    // 获取当前棋子的所有可转换目标
                    const flipped = this.checkConversions(row, col);

                    if (flipped.length > 0) {
                        await this.flipPieces(flipped);
                        totalFlipped.push(...flipped);

                        // 更新分数(实时更新)
                        this.config.scores[currentPlayer] += flipped.length;
                        this.updateView();

                        // 如果启用辐射模式,将新转换的棋子加入队列
                        if (radiationMode) {
                            flipped.forEach(([r, c]) => {
                                queue.push({ row: r, col: c });
                            });
                        }
                    }
                }
                return totalFlipped.length > 0;
            }

            static checkConversions(row, col) {
                const directions = this.config.diagonalRule ?
                    [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]] :
                    [[-1, 0], [1, 0], [0, -1], [0, 1]];

                const current = this.config.currentPlayer;
                const enemy = 1 - current;
                const piecesToFlip = [];

                for (const [dx, dy] of directions) {
                    // "挑"规则检查
                    const front = { row: row + dx, col: col + dy };
                    const back = { row: row - dx, col: col - dy };
                    if (this.isValid(front) && this.isValid(back) &&
                        this.getPiece(front.row, front.col) === enemy &&
                        this.getPiece(back.row, back.col) === enemy) {
                        piecesToFlip.push([front.row, front.col]);
                        piecesToFlip.push([back.row, back.col]);
                    }

                    // "夹"规则检查
                    const far = { row: row + dx * 2, col: col + dy * 2 };
                    if (this.isValid(far) &&
                        this.getPiece(far.row, far.col) === current &&
                        this.getPiece(row + dx, col + dy) === enemy) {
                        piecesToFlip.push([row + dx, col + dy]);
                    }
                }

                return piecesToFlip.filter(([r, c]) => this.getPiece(r, c) !== current);
            }

            // 新增辅助方法
            static getPiece(row, col) {
                return this.isValid({ row, col }) ? this.config.board[row][col] : null;
            }

            static async flipPieces(pieces) {
                const promises = pieces.map(([row, col]) => {
                    return new Promise(resolve => {
                        const cell = document.querySelector(`[data-row="${row}"][data-col="${col}"]`);
                        const piece = cell.querySelector('.piece');

                        piece.classList.add('flipping');
                        setTimeout(() => {
                            this.config.board[row][col] = this.config.currentPlayer;
                            piece.className = `piece ${this.config.currentPlayer ? 'white' : 'black'}`;
                            piece.classList.remove('flipping');
                            resolve();
                        }, 500);
                    });
                });
                await Promise.all(promises);
                this.updateView();
            }

            static async checkGameOver() {
                if (this.config.customizing) return false;

                await new Promise(resolve => setTimeout(resolve, 500));

                const blackCount = this.config.board.flat().filter(c => c === 0).length;
                const whiteCount = this.config.board.flat().filter(c => c === 1).length;

                // 完善胜负判断
                const blackCanMove = this.canMove(0);
                const whiteCanMove = this.canMove(1);
                let gameOver = false;

                if (blackCount === 0 || whiteCount === 0) {
                    gameOver = true;
                } else if (!blackCanMove && !whiteCanMove) {
                    gameOver = true;
                } else if (this.config.currentPlayer === 0 && !blackCanMove) {
                    gameOver = true;
                } else if (this.config.currentPlayer === 1 && !whiteCanMove) {
                    gameOver = true;
                }

                if (gameOver) {
                    const winner = blackCount > whiteCount ? '黑方' : '白方';
                    return this.showGameOver(winner);
                }
                return false;
            }

            static showGameOver(winner) {
                const msg = this.config.mode === 'pve' ?
                    `${winner}胜利!${winner === '黑方' ? '玩家' : 'AI'}获胜!是否重新开始?` :
                    `${winner}胜利!是否重新开始?`;

                if (confirm(msg)) this.handleRestart();
                return true;
            }

            static canMove(player) {
                for (let i = 0; i < this.config.boardHeight; i++) {
                    for (let j = 0; j < this.config.boardWidth; j++) {
                        if (this.config.board[i][j] === player) {
                            if (this.getValidMoves(i, j).length > 0) return true;
                        }
                    }
                }
                return false;
            }

            static getValidMoves(row, col) {
                const moves = [];
                const directions = this.config.diagonalMove ?
                    [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]] :
                    [[-1, 0], [1, 0], [0, -1], [0, 1]];

                directions.forEach(([dx, dy]) => {
                    let steps = 1;
                    while (true) {
                        const newRow = row + dx * steps;
                        const newCol = col + dy * steps;
                        if (!this.isValid([newRow, newCol])) break;
                        if (this.config.board[newRow][newCol] !== null) break;
                        moves.push([newRow, newCol]);
                        steps++;
                    }
                });
                return moves;
            }

            static isValid(position) {
                let row, col;
                if (Array.isArray(position)) {
                    [row, col] = position;
                } else {
                    ({ row, col } = position);
                }
                return row >= 0 && row < this.config.boardHeight &&
                    col >= 0 && col < this.config.boardWidth;
            }

            static clearSelection() {
                this.config.selectedPiece = null;
                document.querySelectorAll('.selected, .valid-move').forEach(el => {
                    el.classList.remove('selected', 'valid-move');
                });
            }

            static clearValidMoves() {
                document.querySelectorAll('.valid-move').forEach(el => {
                    el.classList.remove('valid-move');
                });
            }

            static handleShuffleBoard() {
                const { board, boardWidth, boardHeight } = this.config;
                const pieces = [];

                for (let i = 0; i < boardHeight; i++) {
                    for (let j = 0; j < boardWidth; j++) {
                        if (board[i][j] !== null) pieces.push(board[i][j]);
                        board[i][j] = null;
                    }
                }

                while (pieces.length) {
                    const idx = Math.floor(Math.random() * pieces.length);
                    const [row, col] = this.getRandomEmptyCell();
                    board[row][col] = pieces.splice(idx, 1)[0];
                }

                this.config.currentPlayer = 0;
                this.updateView();
                this.checkGameOver();
                this.config.lastCustomState = this.getCurrentState();
                this.hideBoardSettings();
            }

            static handleRandomBoard() {
                do {
                    this.config.boardWidth = Math.max(5, Math.floor(Math.random() * 10) + 5);
                    this.config.boardHeight = Math.max(5, Math.floor(Math.random() * 10) + 5);
                    this.config.pieceRows = Math.min(
                        Math.floor(Math.random() * 3) + 1,
                        Math.floor((this.config.boardHeight - 1) / 2)
                    );
                } while (this.config.pieceRows < 1);

                this.resetBoard(true);
                this.config.currentPlayer = 0;
                this.config.lastCustomState = this.getCurrentState();
                this.updateView();
                this.hideBoardSettings();
            }

            static handleDefaultBoard() {
                this.config.boardWidth = 7;
                this.config.boardHeight = 7;
                this.config.pieceRows = 1;
                this.resetBoard(true);
                this.config.currentPlayer = 0;
                this.config.lastCustomState = this.getCurrentState();
                this.updateView();
                this.hideBoardSettings();
            }

            static undoMove() {
                if (this.config.moveHistory.length === 0) return;
                const lastState = this.config.moveHistory.pop();
                this.config.board = lastState.board;
                this.config.scores = lastState.scores;
                this.config.currentPlayer = lastState.player;
                this.updateView();
            }

            static saveGame() {
                const saveData = {
                    config: {
                        boardWidth: this.config.boardWidth,
                        boardHeight: this.config.boardHeight,
                        pieceRows: this.config.pieceRows,
                        mode: this.config.mode,
                        aiLevel: this.config.aiLevel,
                        currentPlayer: this.config.currentPlayer
                    },
                    board: JSON.parse(JSON.stringify(this.config.board)),
                    scores: [...this.config.scores]
                };
                localStorage.setItem('gameState', JSON.stringify(saveData));
                alert('游戏已保存');
            }

            static loadGame() {
                if (!confirm('加载存档将覆盖当前进度!')) return;
                const saved = JSON.parse(localStorage.getItem('gameState'));
                if (!saved) return alert('找不到存档');

                Object.assign(this.config, saved.config);
                this.config.board = saved.board;
                this.config.scores = saved.scores;
                this.updateView();
            }

            static aiTurn() {
                const moves = this.getAllValidMoves(1);
                if (moves.length === 0) return;

                let selectedMove;
                switch (this.config.aiLevel) {
                    case 1:
                        selectedMove = moves[Math.floor(Math.random() * moves.length)];
                        break;
                    case 2:
                        selectedMove = moves.reduce((best, current) =>
                            this.evaluateMove(current) > this.evaluateMove(best) ? current : best
                        );
                        break;
                    case 3:
                        selectedMove = this.alphaBetaSearch(3)[1];
                        break;
                }

                this.selectPiece(selectedMove[0][0], selectedMove[0][1]);
                setTimeout(() => this.movePiece(selectedMove[1][0], selectedMove[1][1]), 500);
            }

            static getAllValidMoves(player) {
                const moves = [];
                for (let row = 0; row < this.config.boardHeight; row++) {
                    for (let col = 0; col < this.config.boardWidth; col++) {
                        if (this.config.board[row][col] === player) {
                            this.getValidMoves(row, col).forEach(target => {
                                moves.push([[row, col], target]);
                            });
                        }
                    }
                }
                return moves;
            }

            static evaluateMove(move) {
                const tempBoard = JSON.parse(JSON.stringify(this.config.board));
                const [start, end] = move;
                tempBoard[start[0]][start[1]] = null;
                tempBoard[end[0]][end[1]] = 1;
                return this.calculateConversion(tempBoard, end[0], end[1]).length;
            }

            static alphaBetaSearch(depth) {
                let alpha = -Infinity;
                let beta = Infinity;
                let bestMove = null;
                let bestValue = -Infinity;

                const moves = this.getAllValidMoves(1);
                for (const move of moves) {
                    const originalState = this.getCurrentState();
                    this.simulateMove(move);

                    const value = this.minValue(depth - 1, alpha, beta);
                    this.restoreState(originalState);

                    if (value > bestValue) {
                        bestValue = value;
                        bestMove = move;
                    }
                    alpha = Math.max(alpha, bestValue);
                }
                return [bestValue, bestMove];
            }

            static maxValue(depth, alpha, beta) {
                if (depth === 0 || this.checkGameOverSync()) {
                    return this.evaluateBoard();
                }

                let value = -Infinity;
                const moves = this.getAllValidMoves(1);
                for (const move of moves) {
                    const originalState = this.getCurrentState();
                    this.simulateMove(move);

                    value = Math.max(value, this.minValue(depth - 1, alpha, beta));
                    this.restoreState(originalState);

                    if (value >= beta) return value;
                    alpha = Math.max(alpha, value);
                }
                return value;
            }

            static minValue(depth, alpha, beta) {
                if (depth === 0 || this.checkGameOverSync()) {
                    return this.evaluateBoard();
                }

                let value = Infinity;
                const moves = this.getAllValidMoves(0);
                for (const move of moves) {
                    const originalState = this.getCurrentState();
                    this.simulateMove(move);

                    value = Math.min(value, this.maxValue(depth - 1, alpha, beta));
                    this.restoreState(originalState);

                    if (value <= alpha) return value;
                    beta = Math.min(beta, value);
                }
                return value;
            }

            static checkGameOverSync() {
                const blackCount = this.config.board.flat().filter(c => c === 0).length;
                const whiteCount = this.config.board.flat().filter(c => c === 1).length;
                return blackCount === 0 || whiteCount === 0 ||
                    (!this.canMove(0) && !this.canMove(1));
            }

            static simulateMove(move) {
                const [start, end] = move;
                const tempBoard = JSON.parse(JSON.stringify(this.config.board));
                tempBoard[start[0]][start[1]] = null;
                tempBoard[end[0]][end[1]] = 1;

                const flipped = this.calculateConversion(tempBoard, end[0], end[1]);
                flipped.forEach(([r, c]) => tempBoard[r][c] = 1);
                this.config.board = tempBoard;
            }

            static evaluateBoard() {
                let score = 0;
                const centerWeight = 3;
                const centerArea = this.getCenterArea();

                for (let i = 0; i < this.config.boardHeight; i++) {
                    for (let j = 0; j < this.config.boardWidth; j++) {
                        if (this.config.board[i][j] === 1) {
                            score += centerArea.some(([x, y]) => x === i && y === j) ? centerWeight : 1;
                        } else if (this.config.board[i][j] === 0) {
                            score -= centerArea.some(([x, y]) => x === i && y === j) ? centerWeight : 1;
                        }
                    }
                }
                return score;
            }

            static restoreState(state) {
                this.config.board = JSON.parse(JSON.stringify(state.board));
                this.config.scores = state.scores ? [...state.scores] : [0, 0];
                this.config.currentPlayer = state.currentPlayer;
            }

            static bindEvents() {
                document.getElementById('gameMode').addEventListener('change', (e) => {
                    this.config.mode = e.target.value;
                    document.getElementById('aiLevelGroup').style.display =
                        e.target.value === 'pve' ? 'flex' : 'none';
                    this.updateView();
                });

                document.getElementById('aiLevel').addEventListener('change', (e) => {
                    this.config.aiLevel = parseInt(e.target.value);
                });

                ['diagonalRule', 'diagonalMove', 'radiationMode'].forEach(id => {
                    document.getElementById(id).addEventListener('change', (e) => {
                        this.config[id] = e.target.checked;
                    });
                });

                const linkDimension = (sliderId, inputId, prop) => {
                    const slider = document.getElementById(sliderId);
                    const input = document.getElementById(inputId);

                    const update = (value) => {
                        value = parseInt(value) || 0;

                        // 行数有效性验证
                        if (prop === 'pieceRows') {
                            const maxRows = Math.floor((this.config.boardHeight - 1) / 2);
                            value = Math.min(value, maxRows);
                            value = Math.max(value, 1); // 至少1行
                            if (value !== parseInt(input.value)) {
                                alert(`行数已自动调整为${value}(最大允许值:${maxRows}`);
                            }
                        }

                        // 更新控件值
                        slider.value = input.value = value;
                        this.config[prop] = value;

                        // 清空棋盘逻辑
                        this.config.board = Array.from({ length: this.config.boardHeight }, () =>
                            Array(this.config.boardWidth).fill(null)
                        );

                        // 初始化基础布局
                        if (prop === 'pieceRows') {
                            this.resetBoard(true); // 带初始棋子布局
                        } else {
                            this.updateView(); // 仅更新空棋盘
                        }
                    };

                    slider.addEventListener('input', (e) => update(e.target.value));
                    input.addEventListener('change', (e) => update(e.target.value));
                };

                linkDimension('customWidthSlider', 'customWidth', 'boardWidth');
                linkDimension('customHeightSlider', 'customHeight', 'boardHeight');
                linkDimension('pieceRowsSlider', 'pieceRows', 'pieceRows');

                // 自定义棋盘拖动逻辑
                let isDragging = false;
                document.addEventListener('mousedown', (e) => {
                    if (!this.config.customizing) return;
                    const colorBtn = e.target.closest('.color-option');
                    if (!colorBtn) return;

                    isDragging = true;
                    this.config.customColor = colorBtn.classList.contains('black') ? 0 :
                        colorBtn.classList.contains('white') ? 1 : null;
                });

                document.addEventListener('mouseup', () => isDragging = false);

                document.querySelectorAll('#board td').forEach(cell => {
                    cell.addEventListener('mouseenter', (e) => {
                        if (isDragging && this.config.customizing) {
                            const row = parseInt(cell.dataset.row);
                            const col = parseInt(cell.dataset.col);
                            this.config.board[row][col] = this.config.customColor;
                            this.updateView();
                        }
                    });
                });
            }

            static showBoardSettings() {
                document.getElementById('board-settings-menu').style.display = 'flex';
                document.getElementById('board-settings-overlay').style.display = 'block';
            }

            static hideBoardSettings() {
                document.getElementById('board-settings-menu').style.display = 'none';
                document.getElementById('board-settings-overlay').style.display = 'none';
            }

            static startCustomBoard() {
                // 保存原始状态时包含完整棋盘数据
                this.config.originalState = {
                    ...this.getCurrentState(),
                    board: JSON.parse(JSON.stringify(this.config.board))  // 深度拷贝棋盘
                };

                document.getElementById('controls').style.display = 'none';
                document.getElementById('custom-controls').style.display = 'grid';
                this.hideBoardSettings();
                this.config.customizing = true;

                // 不再重置棋盘,保持当前状态
                this.updateView();  // 只刷新视图

                console.log('进入自定义模式,当前棋盘尺寸:',
                    `${this.config.boardWidth}x${this.config.boardHeight}`);
            }

            static selectColor(color) {
                this.config.customColor = color;
                document.querySelectorAll('.color-option').forEach(opt => {
                    opt.classList.toggle('selected',
                        (color === 0 && opt.classList.contains('black')) ||
                        (color === 1 && opt.classList.contains('white')) ||
                        (color === null && opt.classList.contains('clear-piece'))
                    );
                });
            }

            static clearBoard() {
                this.config.board = Array.from({ length: this.config.boardHeight },
                    () => Array(this.config.boardWidth).fill(null));
                this.updateView();
            }

            static restoreDefault() {
                this.config.boardWidth = 7;
                this.config.boardHeight = 7;
                this.config.pieceRows = 1;
                this.resetBoard(true);
                this.updateView();
            }

            static confirmCustom() {
                this.config.lastCustomState = {
                    boardWidth: this.config.boardWidth,
                    boardHeight: this.config.boardHeight,
                    pieceRows: this.config.pieceRows,
                    firstPlayer: parseInt(document.querySelector('input[name="firstPlayer"]:checked').value),
                    board: JSON.parse(JSON.stringify(this.config.board))
                };
                this.config.customizing = false;
                document.getElementById('custom-controls').style.display = 'none';
                document.getElementById('controls').style.display = 'grid';
                this.config.currentPlayer = this.config.lastCustomState.firstPlayer;
                this.updateView();

                // AI先手处理
                if (this.config.mode === 'pve' && this.config.currentPlayer === 1) {
                    setTimeout(() => this.aiTurn(), 500);
                }
            }

            static cancelCustom() {
                if (this.config.originalState) {
                    Object.assign(this.config, this.config.originalState);
                }
                document.getElementById('custom-controls').style.display = 'none';
                document.getElementById('controls').style.display = 'grid';
                this.updateView();
            }

            static handleRestart() {
                if (confirm('确定要重置游戏吗?')) {
                    if (this.config.lastCustomState) {
                        this.config.boardWidth = this.config.lastCustomState.boardWidth;
                        this.config.boardHeight = this.config.lastCustomState.boardHeight;
                        this.config.pieceRows = this.config.lastCustomState.pieceRows;
                        this.config.board = JSON.parse(JSON.stringify(this.config.lastCustomState.board));
                        this.config.currentPlayer = this.config.lastCustomState.firstPlayer;
                    } else {
                        this.resetBoard(true);
                    }
                    this.config.scores = [0, 0];
                    this.config.moveHistory = [];
                    this.updateView();
                }
            }

            static getRandomEmptyCell() {
                const emptyCells = [];
                for (let i = 0; i < this.config.boardHeight; i++) {
                    for (let j = 0; j < this.config.boardWidth; j++) {
                        if (this.config.board[i][j] === null) {
                            emptyCells.push([i, j]);
                        }
                    }
                }
                return emptyCells[Math.floor(Math.random() * emptyCells.length)] || [0, 0];
            }

            static calculateConversion(tempBoard, row, col) {
                const piecesToFlip = [];
                const directions = this.config.diagonalRule ?
                    [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]] :
                    [[-1, 0], [1, 0], [0, -1], [0, 1]];
                const current = 1;

                for (const [dx, dy] of directions) {
                    const front = [row + dx, col + dy];
                    const back = [row - dx, col - dy];
                    if (this.isValid(front) && this.isValid(back) &&
                        tempBoard[front[0]][front[1]] === 0 &&
                        tempBoard[back[0]][back[1]] === 0) {
                        piecesToFlip.push(front, back);
                    }

                    const far = [row + dx * 2, col + dy * 2];
                    if (this.isValid(far) &&
                        tempBoard[far[0]][far[1]] === current &&
                        tempBoard[row + dx][col + dy] === 0) {
                        piecesToFlip.push([row + dx, col + dy]);
                    }
                }
                return piecesToFlip;
            }

            static getCenterArea() {
                const centerX = Math.floor(this.config.boardWidth / 2);
                const centerY = Math.floor(this.config.boardHeight / 2);
                return [
                    [centerY, centerX],
                    [centerY - 1, centerX], [centerY + 1, centerX],
                    [centerY, centerX - 1], [centerY, centerX + 1]
                ];
            }
        }
        // 初始化游戏
        window.addEventListener('DOMContentLoaded', () => Game.init());
    </script>
</body>

</html>
Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐