用Trae自动生成一个围棋小程序

先尝试用iFlow生成的,但是在棋盘布局这里走了一些弯路,很多时候棋盘都不规整,也就是棋子无法保证放在交叉点。很长时间才调试好,结果一改动,又全乱了。于是转而用Trae来处理。

使用的prompt

学习wgo的知识,写一个围棋程序。要求能选择5-19大小的棋盘。wgo的手册参考: https://wgo.waltheri.net/documentation

一轮就出来基本实现了,后面又微调了一下,基本就ok了。

网页主文件index.html

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <title>围棋对弈程序</title>
    <link rel="stylesheet" href="styles.css?v=1.1">
</head>
<body>
    <div class="container">
        <h1>围棋对弈程序</h1>
        <div class="game-info">
            <div class="status">当前回合:<span id="current-player">黑棋</span></div>
            <div class="captured">
                <span class="black-captured">黑棋提子:<span id="black-captured-count">0</span></span>
                <span class="white-captured">白棋提子:<span id="white-captured-count">0</span></span>
            </div>
        </div>
        <div class="board-controls">
            <label for="board-size">棋盘大小:</label>
            <select id="board-size">
                <option value="5">5×5</option>
                <option value="9">9×9</option>
                <option value="13">13×13</option>
                <option value="19" selected>19×19</option>
            </select>
            <button id="apply-size">应用</button>
        </div>
        <div id="board"></div>
        <div class="controls">
            <button id="pass-btn">虚手</button>
            <button id="undo-btn">悔棋</button>
            <button id="reset-btn">重新开始</button>
            <button id="hint-btn">智能提示</button>
        </div>
    </div>
    <script src="game.js?v=1.1"></script>
</body>
</html>

围棋实现主文件game.js

javascript 复制代码
class GoGame {
    constructor() {
        // WGO标准:支持5-19的棋盘大小,默认19x19
        this.boardSize = 19;
        // 0 表示空位,1 表示黑棋,-1 表示白棋
        this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
        this.currentPlayer = 1; // 1 代表黑棋,-1 代表白棋
        this.gameHistory = [];
        this.capturedStones = { black: 0, white: 0 };
        this.komi = 6.5; // WGO标准贴目(黑棋贴白棋6.5目)
        this.passCount = 0; // 连续虚手次数
        this.gameOver = false;
        this.initializeBoard();
        this.bindEvents();
    }

    initializeBoard() {
        const boardElement = document.getElementById('board');
        boardElement.innerHTML = '';
        
        // WGO标准:根据棋盘大小自适应设置尺寸
        let boardSizePx, cellSize, offset;
        
        // 根据棋盘大小优化显示效果
        if (this.boardSize <= 9) {
            boardSizePx = this.boardSize * 50 + 40; // 小棋盘使用更大的格子
            cellSize = 50;
            offset = 20;
        } else if (this.boardSize <= 13) {
            boardSizePx = this.boardSize * 40 + 40;
            cellSize = 40;
            offset = 20;
        } else if (this.boardSize <= 17) {
            boardSizePx = this.boardSize * 35 + 35;
            cellSize = 35;
            offset = 17;
        } else {
            boardSizePx = this.boardSize * 30 + 30;
            cellSize = 30;
            offset = 15;
        }
        
        boardElement.style.width = `${boardSizePx}px`;
        boardElement.style.height = `${boardSizePx}px`;
        
        // 绘制棋盘网格
        for (let i = 0; i < this.boardSize; i++) {
            // 横线
            const horizontalLine = document.createElement('div');
            horizontalLine.className = 'line horizontal';
            horizontalLine.style.top = `${i * cellSize + offset}px`;
            horizontalLine.style.left = `${offset}px`;
            horizontalLine.style.width = `${(this.boardSize - 1) * cellSize}px`;
            boardElement.appendChild(horizontalLine);
            
            // 竖线
            const verticalLine = document.createElement('div');
            verticalLine.className = 'line vertical';
            verticalLine.style.left = `${i * cellSize + offset}px`;
            verticalLine.style.top = `${offset}px`;
            verticalLine.style.height = `${(this.boardSize - 1) * cellSize}px`;
            boardElement.appendChild(verticalLine);
        }
        
        // 绘制星位点(根据WGO标准)
        this.drawStarPoints(cellSize, offset);
        
        this.updateStatus();
    }

    // 绘制星位点(根据WGO标准)
    drawStarPoints(cellSize, offset) {
        const boardElement = document.getElementById('board');
        const starPoints = this.getStarPoints();
        
        // 创建星位点标记
        for (const [row, col] of starPoints) {
            // 确保星位点在棋盘范围内
            if (row < this.boardSize && col < this.boardSize) {
                const starPoint = document.createElement('div');
                starPoint.className = 'star-point';
                starPoint.style.left = `${col * cellSize + offset}px`;
                starPoint.style.top = `${row * cellSize + offset}px`;
                boardElement.appendChild(starPoint);
            }
        }
    }

    // 获取星位点位置(根据不同棋盘大小调整,符合WGO标准)
    getStarPoints() {
        if (this.boardSize === 5) {
            // 5x5棋盘只有中心点
            return [[2, 2]];
        } else if (this.boardSize === 9) {
            // 9x9棋盘星位点:四角和中心
            const hoshi = [[2, 2], [2, 6], [6, 2], [6, 6], [4, 4]];
            return hoshi;
        } else if (this.boardSize === 13) {
            // 13x13棋盘星位点:四角、边中心和中心
            const hoshi = [
                [3, 3], [3, 9], [9, 3], [9, 9],
                [6, 6]
            ];
            return hoshi;
        } else if (this.boardSize === 19) {
            // 19x19棋盘星位点:标准9个星位点
            const hoshi = [
                [3, 3], [3, 9], [3, 15],
                [9, 3], [9, 9], [9, 15],
                [15, 3], [15, 9], [15, 15]
            ];
            return hoshi;
        } else {
            // 其他大小的棋盘,根据WGO标准显示星位点
            const center = Math.floor(this.boardSize / 2);
            const quarter = Math.floor(this.boardSize / 4);
            
            if (this.boardSize >= 15) {
                // 大棋盘显示9个星位点
                return [
                    [quarter, quarter], [quarter, center], [quarter, this.boardSize - quarter - 1],
                    [center, quarter], [center, center], [center, this.boardSize - quarter - 1],
                    [this.boardSize - quarter - 1, quarter], [this.boardSize - quarter - 1, center], [this.boardSize - quarter - 1, this.boardSize - quarter - 1]
                ];
            } else {
                // 小棋盘显示中心点
                return [[center, center]];
            }
        }
    }

    bindEvents() {
        const boardElement = document.getElementById('board');
        const passButton = document.getElementById('pass-btn');
        const undoButton = document.getElementById('undo-btn');
        const resetButton = document.getElementById('reset-btn');
        const applySizeButton = document.getElementById('apply-size');

        boardElement.addEventListener('click', (e) => {
            // 获取点击位置相对于棋盘的坐标
            const rect = boardElement.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            
            // 根据当前棋盘大小计算格子大小
            let cellSize, offset;
            
            if (this.boardSize <= 9) {
                cellSize = 50;
                offset = 20;
            } else if (this.boardSize <= 13) {
                cellSize = 40;
                offset = 20;
            } else if (this.boardSize <= 17) {
                cellSize = 35;
                offset = 17;
            } else {
                cellSize = 30;
                offset = 15;
            }
            
            // 计算点击的行列
            const col = Math.round((x - offset) / cellSize);
            const row = Math.round((y - offset) / cellSize);
            
            // 检查是否在有效范围内
            if (row >= 0 && row < this.boardSize && col >= 0 && col < this.boardSize) {
                this.makeMove(row, col);
            }
        });

        passButton.addEventListener('click', () => {
            this.pass();
        });

        undoButton.addEventListener('click', () => {
            this.undo();
        });

        resetButton.addEventListener('click', () => {
            this.resetGame();
        });

        applySizeButton.addEventListener('click', () => {
            this.changeBoardSize();
        });

        const hintButton = document.getElementById('hint-btn');
        hintButton.addEventListener('click', () => {
            this.showHint();
        });
    }

    makeMove(row, col) {
        // 检查位置是否为空
        if (this.board[row][col] !== 0) {
            return false;
        }

        // 保存当前状态用于悔棋
        this.saveState();

        // 在内部棋盘数组中放置棋子
        this.board[row][col] = this.currentPlayer;

        // 检查并处理吃子
        const captured = this.checkAndCapture(row, col);

        // 检查是否自杀(没有气且没有提子)
        if (this.getLiberties(row, col) === 0 && captured === 0) {
            // 自杀是非法的,恢复棋盘状态
            this.restoreState();
            return false;
        }

        // 在UI上显示棋子
        this.renderStone(row, col);

        // 更新提子计数
        if (this.currentPlayer === 1) {
            this.capturedStones.white += captured;
        } else {
            this.capturedStones.black += captured;
        }

        // 切换玩家
        this.currentPlayer *= -1;
        this.updateStatus();

        return true;
    }

    renderStone(row, col) {
        const board = document.getElementById('board');
        
        // 移除已存在的同位置棋子
        const existingStone = board.querySelector(`.stone[data-row="${row}"][data-col="${col}"]`);
        if (existingStone) {
            board.removeChild(existingStone);
        }
        
        const stone = document.createElement('div');
        stone.className = `stone ${this.board[row][col] === 1 ? 'black' : 'white'}`;
        stone.dataset.row = row;
        stone.dataset.col = col;
        
        // 根据当前棋盘大小计算棋子位置(与initializeBoard保持一致)
        let cellSize, offset;
        
        if (this.boardSize <= 9) {
            cellSize = 50;
            offset = 20;
        } else if (this.boardSize <= 13) {
            cellSize = 40;
            offset = 20;
        } else if (this.boardSize <= 17) {
            cellSize = 35;
            offset = 17;
        } else {
            cellSize = 30;
            offset = 15;
        }
        
        const x = col * cellSize + offset;
        const y = row * cellSize + offset;
        
        stone.style.left = `${x}px`;
        stone.style.top = `${y}px`;
        
        // 设置棋子大小(WGO标准:棋子大小约为格子大小的80%)
        const stoneSize = cellSize * 0.8;
        stone.style.width = `${stoneSize}px`;
        stone.style.height = `${stoneSize}px`;
        
        board.appendChild(stone);
    }

    updateStatus() {
        const statusElement = document.getElementById('current-player');
        
        if (this.gameOver) {
            statusElement.textContent = '游戏结束';
            statusElement.style.color = '#f00';
        } else {
            statusElement.textContent = this.currentPlayer === 1 ? '黑棋' : '白棋';
            statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
        }

        document.getElementById('black-captured-count').textContent = this.capturedStones.black;
        document.getElementById('white-captured-count').textContent = this.capturedStones.white;
        
        // 显示贴目信息
        const komiInfo = document.getElementById('komi-info');
        if (!komiInfo) {
            const gameInfo = document.querySelector('.game-info');
            const komiElement = document.createElement('div');
            komiElement.id = 'komi-info';
            komiElement.className = 'komi';
            komiElement.textContent = `贴目:${this.komi}`;
            gameInfo.appendChild(komiElement);
        }
    }

    // 获取棋块(相连的同色棋子)
    getGroup(row, col) {
        const color = this.board[row][col];
        if (color === 0) return [];

        const group = [];
        const visited = new Set();
        const queue = [{r: row, c: col}];
        visited.add(`${row},${col}`);
        
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        while (queue.length > 0) {
            const {r, c} = queue.shift();
            group.push({r, c});
            
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                
                // 检查边界
                if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                    // 检查是否是同色棋子且未访问过
                    if (this.board[nr][nc] === color && !visited.has(`${nr},${nc}`)) {
                        visited.add(`${nr},${nc}`);
                        queue.push({r: nr, c: nc});
                    }
                }
            }
        }
        
        return group;
    }

    // 获取棋子或棋块的气
    getLiberties(row, col) {
        const color = this.board[row][col];
        if (color === 0) return 0;

        // 找到整个棋块
        const group = this.getGroup(row, col);
        
        // 计算气的数量
        const liberties = new Set();
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        for (const {r, c} of group) {
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                
                // 检查边界
                if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                    // 如果是空点,增加气
                    if (this.board[nr][nc] === 0) {
                        liberties.add(`${nr},${nc}`);
                    }
                }
            }
        }
        
        return liberties.size;
    }

    // 检查并处理吃子
    checkAndCapture(row, col) {
        let capturedCount = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        const opponent = -this.currentPlayer;
        
        // 检查周围的敌方棋子
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            // 检查边界
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                // 如果是敌方棋子
                if (this.board[nr][nc] === opponent) {
                    // 检查该棋子或棋块是否没气
                    if (this.getLiberties(nr, nc) === 0) {
                        // 提子
                        const captured = this.captureGroup(nr, nc);
                        capturedCount += captured;
                    }
                }
            }
        }
        
        return capturedCount;
    }

    // 提子(移除棋块)
    captureGroup(row, col) {
        // 提子(移除棋块)
        const group = this.getGroup(row, col);
        const color = this.board[row][col];
        
        // 从棋盘上移除棋子
        for (const {r, c} of group) {
            this.board[r][c] = 0;
        }
        
        // 从UI上移除棋子
        this.removeStones(group);
        
        return group.length; // 返回被提子的数量
    }

    // 从UI上移除棋子
    removeStones(group) {
        const board = document.getElementById('board');
        // 从UI上移除棋子
        for (const {r, c} of group) {
            const stone = board.querySelector(`.stone[data-row="${r}"][data-col="${c}"]`);
            if (stone) {
                board.removeChild(stone);
            }
        }
    }

    // 保存当前状态用于悔棋
    saveState() {
        this.gameHistory.push({
            board: JSON.parse(JSON.stringify(this.board)),
            currentPlayer: this.currentPlayer,
            capturedStones: {...this.capturedStones}
        });
    }

    // 恢复上一个状态
    restoreState() {
        if (this.gameHistory.length > 0) {
            const prevState = this.gameHistory.pop();
            this.board = prevState.board;
            this.currentPlayer = prevState.currentPlayer;
            this.capturedStones = prevState.capturedStones;
            this.redrawBoard();
            this.updateStatus();
        }
    }

    // 重新绘制棋盘
    redrawBoard() {
        const board = document.getElementById('board');
        
        // 移除所有棋子
        const stones = board.querySelectorAll('.stone');
        stones.forEach(stone => board.removeChild(stone));
        
        // 重新绘制所有棋子
        for (let row = 0; row < this.boardSize; row++) {
            for (let col = 0; col < this.boardSize; col++) {
                if (this.board[row][col] !== 0) {
                    this.renderStone(row, col);
                }
            }
        }
    }

    // 虚手(根据WGO标准)
    pass() {
        this.saveState();
        this.passCount++;
        
        // 检查游戏是否结束(连续两次虚手)
        if (this.passCount >= 2) {
            this.endGame();
        } else {
            this.currentPlayer *= -1;
            this.updateStatus();
        }
    }

    // 悔棋
    undo() {
        this.restoreState();
    }

    // 显示智能提示
    showHint() {
        // 如果游戏已经结束,不显示提示
        if (this.gameOver) {
            alert('游戏已经结束,无法提供提示');
            return;
        }

        // 清除之前的提示
        this.clearHint();

        // 使用MCTS算法获取更智能的建议
        const bestMove = this.getSmartHintMove ? this.getSmartHintMove() : null;
        
        if (bestMove) {
            // 显示提示标记
            this.displayHint(bestMove.row, bestMove.col);
            
            // 显示提示信息
            const statusElement = document.getElementById('current-player');
            const originalText = statusElement.textContent;
            statusElement.textContent = `智能提示:建议在 ${String.fromCharCode(65 + bestMove.col)}${bestMove.row + 1} 落子`;
            statusElement.style.color = '#007bff';
            
            // 3秒后恢复原状态
            setTimeout(() => {
                statusElement.textContent = originalText;
                statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
            }, 3000);
        } else {
            // 如果MCTS没有找到合适位置,使用启发式算法作为备选
            const fallbackMove = this.getBestHintMove();
            if (fallbackMove) {
                this.displayHint(fallbackMove.row, fallbackMove.col);
                const statusElement = document.getElementById('current-player');
                const originalText = statusElement.textContent;
                statusElement.textContent = `智能提示:建议在 ${String.fromCharCode(65 + fallbackMove.col)}${fallbackMove.row + 1} 落子`;
                statusElement.style.color = '#007bff';
                
                setTimeout(() => {
                    statusElement.textContent = originalText;
                    statusElement.style.color = this.currentPlayer === 1 ? '#000' : '#666';
                }, 3000);
            } else {
                alert('没有有效的落子位置');
            }
        }
    }

    // 清除提示标记
    clearHint() {
        const board = document.getElementById('board');
        const existingHint = board.querySelector('.hint-marker');
        if (existingHint) {
            board.removeChild(existingHint);
        }
    }

    // 显示提示标记
    displayHint(row, col) {
        const board = document.getElementById('board');
        
        // 根据当前棋盘大小计算位置
        let cellSize, offset;
        
        if (this.boardSize <= 9) {
            cellSize = 50;
            offset = 20;
        } else if (this.boardSize <= 13) {
            cellSize = 40;
            offset = 20;
        } else if (this.boardSize <= 17) {
            cellSize = 35;
            offset = 17;
        } else {
            cellSize = 30;
            offset = 15;
        }
        
        const x = col * cellSize + offset;
        const y = row * cellSize + offset;
        
        const hintMarker = document.createElement('div');
        hintMarker.className = 'hint-marker';
        hintMarker.style.left = `${x}px`;
        hintMarker.style.top = `${y}px`;
        
        // 设置提示标记样式
        const markerSize = cellSize * 0.3;
        hintMarker.style.width = `${markerSize}px`;
        hintMarker.style.height = `${markerSize}px`;
        
        board.appendChild(hintMarker);
    }

    // 获取最佳提示落子位置(简单启发式算法)
    getBestHintMove() {
        const moves = [];
        
        // 收集所有合法落子位置
        for (let row = 0; row < this.boardSize; row++) {
            for (let col = 0; col < this.boardSize; col++) {
                if (this.board[row][col] === 0) {
                    // 检查是否为合法落子
                    if (this.isValidMove(row, col)) {
                        const score = this.evaluateMove(row, col);
                        moves.push({ row, col, score });
                    }
                }
            }
        }
        
        // 如果没有合法落子,返回null
        if (moves.length === 0) {
            return null;
        }
        
        // 按得分排序,选择得分最高的位置
        moves.sort((a, b) => b.score - a.score);
        return moves[0];
    }
    
    // 获取智能落子位置 - 集成MCTS算法
    getSmartHintMove() {
        try {
            // 创建当前游戏状态的副本
            const gameState = {
                board: JSON.parse(JSON.stringify(this.board)),
                currentPlayer: this.currentPlayer,
                boardSize: this.boardSize
            };
            
            // 根据棋盘大小调整MCTS参数
            let iterations = 200;
            if (this.boardSize <= 9) {
                // 小棋盘:减少迭代次数,增加速度
                iterations = 100;
            } else if (this.boardSize >= 13) {
                // 大棋盘:增加迭代次数,提高准确性
                iterations = 300;
            }
            
            // 使用MCTS算法进行深度分析
            const mcts = new MCTS(gameState, iterations);
            const bestMove = mcts.getBestMove();
            
            if (bestMove && this.isValidMove(bestMove.row, bestMove.col)) {
                return bestMove;
            }
        } catch (error) {
            console.warn('MCTS算法执行失败,使用启发式算法:', error);
        }
        
        return null;
    }

    // 评估落子位置的得分
    evaluateMove(row, col) {
        let score = 0;
        
        // 1. 检查是否能吃子(最高优先级)
        const captureScore = this.simulateCapture(row, col);
        score += captureScore * 100;
        
        // 2. 检查是否能连接己方棋子
        const connectionScore = this.checkConnection(row, col);
        score += connectionScore * 50;
        
        // 3. 检查是否靠近星位点
        const starScore = this.checkStarPointProximity(row, col);
        score += starScore * 30;
        
        // 4. 检查是否靠近边角
        const cornerScore = this.checkCornerProximity(row, col);
        score += cornerScore * 20;
        
        // 5. 检查气数
        const libertyScore = this.checkLiberties(row, col);
        score += libertyScore * 10;
        
        // 6. 小棋盘专用策略
        if (this.boardSize <= 9) {
            score += this.evaluateSmallBoardStrategy(row, col);
        }
        
        return score;
    }
    
    // 检查落子是否合法
    isValidMove(row, col) {
        if (row < 0 || row >= this.boardSize || col < 0 || col >= this.boardSize) {
            return false;
        }
        
        if (this.board[row][col] !== 0) {
            return false;
        }
        
        // 检查自杀规则
        const tempBoard = JSON.parse(JSON.stringify(this.board));
        tempBoard[row][col] = this.currentPlayer;
        
        // 检查是否有气
        const hasLiberty = this.checkLiberty(tempBoard, row, col);
        
        // 检查是否提子
        const captures = this.checkCaptures(tempBoard, row, col);
        
        // 如果有气或者能提子,就是合法走法
        return hasLiberty || captures > 0;
    }
    
    // 检查棋子是否有气
    checkLiberty(board, row, col) {
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (board[nr][nc] === 0) {
                    return true; // 有空点,有气
                }
            }
        }
        
        return false;
    }
    
    // 检查是否能提子
    checkCaptures(board, row, col) {
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        const opponent = -this.currentPlayer;
        let captures = 0;
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (board[nr][nc] === opponent) {
                    // 检查该棋子是否没气
                    if (!this.checkGroupLiberty(board, nr, nc)) {
                        captures++;
                    }
                }
            }
        }
        
        return captures;
    }
    
    // 检查棋块是否有气
    checkGroupLiberty(board, row, col) {
        const visited = new Set();
        const queue = [{r: row, c: col}];
        visited.add(`${row},${col}`);
        
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        while (queue.length > 0) {
            const {r, c} = queue.shift();
            
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                
                if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                    if (board[nr][nc] === 0) {
                        return true; // 找到气
                    }
                    
                    if (board[nr][nc] === board[row][col] && !visited.has(`${nr},${nc}`)) {
                        visited.add(`${nr},${nc}`);
                        queue.push({r: nr, c: nc});
                    }
                }
            }
        }
        
        return false;
    }
    
    // 小棋盘专用策略评估
    evaluateSmallBoardStrategy(row, col) {
        let score = 0;
        const center = Math.floor(this.boardSize / 2);
        
        // 1. 中心控制(小棋盘中心更重要)
        const distanceToCenter = Math.abs(row - center) + Math.abs(col - center);
        score += (this.boardSize - distanceToCenter) * 15;
        
        // 2. 快速扩张(小棋盘需要快速占领空间)
        const expansionScore = this.evaluateExpansion(row, col);
        score += expansionScore * 20;
        
        // 3. 分割对手(小棋盘分割对手更有效)
        const splitScore = this.evaluateOpponentSplit(row, col);
        score += splitScore * 25;
        
        // 4. 眼位形成(小棋盘眼位更重要)
        const eyeScore = this.evaluateEyeFormation(row, col);
        score += eyeScore * 30;
        
        return score;
    }
    
    // 评估扩张潜力
    evaluateExpansion(row, col) {
        let expansion = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]];
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (this.board[nr][nc] === 0) {
                    expansion++;
                }
            }
        }
        
        return expansion;
    }
    
    // 评估分割对手的效果
    evaluateOpponentSplit(row, col) {
        let splitScore = 0;
        const opponent = -this.currentPlayer;
        
        // 检查是否能够分割对手的棋块
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        let opponentGroups = 0;
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (this.board[nr][nc] === opponent) {
                    opponentGroups++;
                }
            }
        }
        
        // 如果能够分割对手的多个棋块,得分更高
        if (opponentGroups >= 2) {
            splitScore = opponentGroups * 10;
        }
        
        return splitScore;
    }
    
    // 评估眼位形成潜力
    evaluateEyeFormation(row, col) {
        let eyeScore = 0;
        
        // 检查是否能够形成眼位
        const tempBoard = JSON.parse(JSON.stringify(this.board));
        tempBoard[row][col] = this.currentPlayer;
        
        // 检查周围是否能够形成眼位
        const eyePatterns = this.getEyePatterns(tempBoard, row, col);
        eyeScore += eyePatterns * 15;
        
        return eyeScore;
    }
    
    // 获取眼位模式
    getEyePatterns(board, row, col) {
        let patterns = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        // 检查是否能够形成基本眼位
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (board[nr][nc] === this.currentPlayer) {
                    // 检查这个棋子周围是否有形成眼位的潜力
                    const surrounding = this.getSurroundingEmptyPoints(board, nr, nc);
                    if (surrounding >= 2) {
                        patterns++;
                    }
                }
            }
        }
        
        return patterns;
    }
    
    // 获取周围空点数
    getSurroundingEmptyPoints(board, row, col) {
        let emptyPoints = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (board[nr][nc] === 0) {
                    emptyPoints++;
                }
            }
        }
        
        return emptyPoints;
    }

    // 模拟吃子
    simulateCapture(row, col) {
        // 临时放置棋子
        this.board[row][col] = this.currentPlayer;
        
        let captured = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        const opponent = -this.currentPlayer;
        
        // 检查周围的敌方棋子
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (this.board[nr][nc] === opponent) {
                    // 检查敌方棋子是否被提
                    if (this.getLiberties(nr, nc) === 0) {
                        captured++;
                    }
                }
            }
        }
        
        // 恢复棋盘状态
        this.board[row][col] = 0;
        
        return captured;
    }

    // 检查连接己方棋子
    checkConnection(row, col) {
        let connections = 0;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (this.board[nr][nc] === this.currentPlayer) {
                    connections++;
                }
            }
        }
        
        return connections;
    }

    // 检查是否靠近星位点
    checkStarPointProximity(row, col) {
        const starPoints = this.getStarPoints();
        let proximity = 0;
        
        for (const [sr, sc] of starPoints) {
            const distance = Math.abs(row - sr) + Math.abs(col - sc);
            if (distance <= 2) {
                proximity += (3 - distance);
            }
        }
        
        return proximity;
    }

    // 检查是否靠近边角
    checkCornerProximity(row, col) {
        const center = Math.floor(this.boardSize / 2);
        const distanceFromCenter = Math.abs(row - center) + Math.abs(col - center);
        
        // 距离中心越远,得分越低(鼓励开局时靠近边角)
        return Math.max(0, 5 - distanceFromCenter / 2);
    }

    // 检查气数
    checkLiberties(row, col) {
        // 临时放置棋子
        this.board[row][col] = this.currentPlayer;
        const liberties = this.getLiberties(row, col);
        
        // 恢复棋盘状态
        this.board[row][col] = 0;
        
        return Math.min(liberties, 4); // 最大4分
    }

    // 检查是否为合法落子
    isValidMove(row, col) {
        // 检查位置是否为空
        if (this.board[row][col] !== 0) {
            return false;
        }
        
        // 临时放置棋子
        this.board[row][col] = this.currentPlayer;
        
        // 检查是否有气
        const hasLiberties = this.getLiberties(row, col) > 0;
        
        // 检查是否提子
        let captures = false;
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        const opponent = -this.currentPlayer;
        
        for (const [dr, dc] of directions) {
            const nr = row + dr;
            const nc = col + dc;
            
            if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                if (this.board[nr][nc] === opponent && this.getLiberties(nr, nc) === 0) {
                    captures = true;
                    break;
                }
            }
        }
        
        // 恢复棋盘状态
        this.board[row][col] = 0;
        
        // 合法落子条件:有气或者能提子
        return hasLiberties || captures;
    }

    // 游戏结束(根据WGO标准计算胜负)
    endGame() {
        this.gameOver = true;
        
        // 计算领地(空点和己方棋子)
        const territory = this.calculateTerritory();
        
        // 计算最终得分
        const blackScore = territory.black + this.capturedStones.white;
        const whiteScore = territory.white + this.capturedStones.black + this.komi;
        
        // 显示结果
        let resultMessage;
        if (blackScore > whiteScore) {
            resultMessage = `黑棋胜!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}\n黑棋领先:${blackScore - whiteScore}目`;
        } else if (whiteScore > blackScore) {
            resultMessage = `白棋胜!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}\n白棋领先:${whiteScore - blackScore}目`;
        } else {
            resultMessage = `平局!\n黑棋得分:${blackScore}\n白棋得分:${whiteScore}`;
        }
        
        alert(resultMessage);
        
        // 更新状态显示
        this.updateStatus();
    }

    // 计算领地(WGO标准)
    calculateTerritory() {
        const territory = { black: 0, white: 0 };
        const visited = new Set();
        
        for (let row = 0; row < this.boardSize; row++) {
            for (let col = 0; col < this.boardSize; col++) {
                const key = `${row},${col}`;
                if (!visited.has(key) && this.board[row][col] === 0) {
                    // 找到空点区域
                    const area = this.getEmptyArea(row, col);
                    const owner = this.determineAreaOwner(area);
                    
                    if (owner === 1) {
                        territory.black += area.length;
                    } else if (owner === -1) {
                        territory.white += area.length;
                    }
                    
                    // 标记为已访问
                    area.forEach(({r, c}) => visited.add(`${r},${c}`));
                }
            }
        }
        
        return territory;
    }

    // 获取空点区域
    getEmptyArea(row, col) {
        const area = [];
        const queue = [{r: row, c: col}];
        const visited = new Set();
        visited.add(`${row},${col}`);
        
        const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
        
        while (queue.length > 0) {
            const {r, c} = queue.shift();
            area.push({r, c});
            
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                
                if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                    const key = `${nr},${nc}`;
                    if (!visited.has(key) && this.board[nr][nc] === 0) {
                        visited.add(key);
                        queue.push({r: nr, c: nc});
                    }
                }
            }
        }
        
        return area;
    }

    // 确定区域归属
    determineAreaOwner(area) {
        let blackBorder = false;
        let whiteBorder = false;
        
        for (const {r, c} of area) {
            const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
            
            for (const [dr, dc] of directions) {
                const nr = r + dr;
                const nc = c + dc;
                
                if (nr >= 0 && nr < this.boardSize && nc >= 0 && nc < this.boardSize) {
                    const stone = this.board[nr][nc];
                    if (stone === 1) blackBorder = true;
                    if (stone === -1) whiteBorder = true;
                }
            }
        }
        
        if (blackBorder && !whiteBorder) return 1; // 黑棋领地
        if (whiteBorder && !blackBorder) return -1; // 白棋领地
        return 0; // 中立或争议区域
    }

    // 重新开始游戏
    resetGame() {
        if (confirm('确定要重新开始游戏吗?')) {
            this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
            this.currentPlayer = 1;
            this.gameHistory = [];
            this.capturedStones = { black: 0, white: 0 };
            this.passCount = 0;
            this.gameOver = false;
            this.initializeBoard();
            this.redrawBoard();
        }
    }

    // 改变棋盘大小
    changeBoardSize() {
        const sizeSelect = document.getElementById('board-size');
        const newSize = parseInt(sizeSelect.value);
        
        if (newSize >= 5 && newSize <= 19) {
            if (confirm(`确定要将棋盘大小改为 ${newSize}×${newSize} 吗?这将重新开始游戏。`)) {
                this.boardSize = newSize;
                this.board = Array(this.boardSize).fill().map(() => Array(this.boardSize).fill(0));
                this.currentPlayer = 1;
                this.gameHistory = [];
                this.capturedStones = { black: 0, white: 0 };
                this.initializeBoard();
                this.redrawBoard();
            }
        }
    }
}

// 初始化游戏
document.addEventListener('DOMContentLoaded', () => {
    new GoGame();
});

效果如图

当前实现的功能

5*5 19*19等棋盘的选择,吃子判断以及提子,悔棋以及智能提示等。

当前的问题:

智能提示还不太智能,机器AI棋力低。

打劫的设定还不对。也就是打劫吃了子,当前对方可以直接吃回去,这与规则不符合。

相关推荐
nju_spy3 小时前
牛客网 AI题(一)机器学习 + 深度学习
人工智能·深度学习·机器学习·lstm·笔试·损失函数·自注意力机制
千桐科技3 小时前
qKnow 知识平台【开源版】安装与部署全指南
人工智能·后端
youcans_4 小时前
【DeepSeek论文精读】13. DeepSeek-OCR:上下文光学压缩
论文阅读·人工智能·计算机视觉·ocr·deepseek
m0_650108244 小时前
【论文精读】Latent-Shift:基于时间偏移模块的高效文本生成视频技术
人工智能·论文精读·文本生成视频·潜在扩散模型·时间偏移模块·高效生成式人工智能
岁月的眸4 小时前
【循环神经网络基础】
人工智能·rnn·深度学习
文火冰糖的硅基工坊4 小时前
[人工智能-大模型-35]:模型层技术 - 大模型的能力与应用场景
人工智能·神经网络·架构·transformer
GIS数据转换器4 小时前
2025无人机在农业生态中的应用实践
大数据·网络·人工智能·安全·无人机
從南走到北5 小时前
西陆房产系统小程序
微信·微信小程序·小程序
syso_稻草人5 小时前
基于 ComfyUI + Wan2.2 animate实现 AI 视频人物换衣:完整工作流解析与资源整合(附一键包)
人工智能·音视频