AI 五子棋网站(Grok-3思考版 2 轮对话生成)


AI 五子棋网站

Grok-3思考版二轮对话生成


对话展示

第一轮对话

第二轮对话


源码文件

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI五子棋</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>五子棋</h1>
        <div id="board"></div>
        <div class="controls">
            <label for="difficulty">难度:</label>
            <select id="difficulty">
                <option value="easy">简单</option>
                <option value="medium" selected>普通</option>
                <option value="hard">困难</option>
            </select>
            <button id="newGame">新游戏</button>
            <button id="undo">悔棋</button>
            <div id="status"></div>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

style.css

body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f0f0f0;
}

.container {
    text-align: center;
}

h1 {
    color: #333;
}

#board {
    display: grid;
    grid-template-columns: repeat(15, 30px);
    grid-template-rows: repeat(15, 30px);
    gap: 1px;
    background-color: #ccc;
    margin: 20px auto;
    border: 2px solid #333;
    padding: 5px;
}

.cell {
    width: 30px;
    height: 30px;
    background-color: #fff;
    position: relative;
    cursor: pointer;
    transition: background-color 0.2s;
}

.cell:hover {
    background-color: #e0e0e0;
}

.cell.black::after {
    content: '';
    position: absolute;
    top: 5px;
    left: 5px;
    width: 20px;
    height: 20px;
    background-color: black;
    border-radius: 50%;
}

.cell.white::after {
    content: '';
    position: absolute;
    top: 5px;
    left: 5px;
    width: 20px;
    height: 20px;
    background-color: white;
    border-radius: 50%;
    border: 1px solid #333;
}

.controls {
    margin-top: 20px;
}

label {
    margin-right: 10px;
    font-size: 18px;
}

select {
    padding: 5px;
    font-size: 16px;
    margin-right: 10px;
}

button {
    padding: 10px 20px;
    margin: 0 10px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.2s;
}

button:hover {
    background-color: #45a049;
}

#status {
    margin-top: 10px;
    font-size: 18px;
    color: #333;
}

script.js

const boardSize = 15;
const board = Array(boardSize).fill().map(() => Array(boardSize).fill(0));
let currentPlayer = 1; // 1为黑棋(玩家),2为白棋(AI)
let gameOver = false;
let history = [];

const boardElement = document.getElementById('board');
const statusElement = document.getElementById('status');
const newGameButton = document.getElementById('newGame');
const undoButton = document.getElementById('undo');
const difficultySelect = document.getElementById('difficulty');

// 初始化棋盘
for (let i = 0; i < boardSize; i++) {
    for (let j = 0; j < boardSize; j++) {
        const cell = document.createElement('div');
        cell.classList.add('cell');
        cell.dataset.row = i;
        cell.dataset.col = j;
        cell.addEventListener('click', () => handleClick(i, j));
        boardElement.appendChild(cell);
    }
}

// 处理玩家点击
function handleClick(row, col) {
    if (gameOver || board[row][col] !== 0) return;
    board[row][col] = currentPlayer;
    history.push({ row, col, player: currentPlayer });
    updateBoard();
    if (checkWin(row, col, currentPlayer)) {
        gameOver = true;
        statusElement.textContent = currentPlayer === 1 ? '黑棋获胜!' : '白棋获胜!';
        return;
    }
    currentPlayer = 3 - currentPlayer;
    statusElement.textContent = currentPlayer === 1 ? '轮到黑棋' : '轮到白棋';
    if (currentPlayer === 2) {
        setTimeout(aiMove, 500); // AI延迟0.5秒落子
    }
}

// 更新棋盘显示
function updateBoard() {
    const cells = document.querySelectorAll('.cell');
    cells.forEach(cell => {
        const row = parseInt(cell.dataset.row);
        const col = parseInt(cell.dataset.col);
        cell.classList.remove('black', 'white');
        if (board[row][col] === 1) cell.classList.add('black');
        else if (board[row][col] === 2) cell.classList.add('white');
    });
}

// 检查胜利
function checkWin(row, col, player) {
    const directions = [
        { dr: 0, dc: 1 }, // 水平
        { dr: 1, dc: 0 }, // 垂直
        { dr: 1, dc: 1 }, // 对角线
        { dr: 1, dc: -1 } // 反对角线
    ];
    for (const { dr, dc } of directions) {
        let count = 1;
        for (let i = 1; i < 5; i++) {
            const r = row + i * dr;
            const c = col + i * dc;
            if (r < 0 || r >= boardSize || c < 0 || c >= boardSize || board[r][c] !== player) break;
            count++;
        }
        for (let i = 1; i < 5; i++) {
            const r = row - i * dr;
            const c = col - i * dc;
            if (r < 0 || r >= boardSize || c < 0 || c >= boardSize || board[r][c] !== player) break;
            count++;
        }
        if (count >= 5) return true;
    }
    return false;
}

// 获取候选位置
function getCandidates() {
    const candidates = [];
    const directions = [
        { dr: -1, dc: 0 }, { dr: 1, dc: 0 }, { dr: 0, dc: -1 }, { dr: 0, dc: 1 },
        { dr: -1, dc: -1 }, { dr: -1, dc: 1 }, { dr: 1, dc: -1 }, { dr: 1, dc: 1 }
    ];
    const visited = Array(boardSize).fill().map(() => Array(boardSize).fill(false));
    for (let i = 0; i < boardSize; i++) {
        for (let j = 0; j < boardSize; j++) {
            if (board[i][j] !== 0) {
                for (const { dr, dc } of directions) {
                    const ni = i + dr;
                    const nj = j + dc;
                    if (ni >= 0 && ni < boardSize && nj >= 0 && nj < boardSize && board[ni][nj] === 0 && !visited[ni][nj]) {
                        candidates.push({ row: ni, col: nj });
                        visited[ni][nj] = true;
                    }
                }
            }
        }
    }
    return candidates.length > 0 ? candidates : getAllEmptyCells();
}

// 获取所有空位(备用)
function getAllEmptyCells() {
    const emptyCells = [];
    for (let i = 0; i < boardSize; i++) {
        for (let j = 0; j < boardSize; j++) {
            if (board[i][j] === 0) emptyCells.push({ row: i, col: j });
        }
    }
    return emptyCells;
}

// 评估函数评分表
const scoreTable = [
    [],              // 长度0
    [0, 0, 0],       // 长度1
    [0, 1, 10],      // 长度2:[0开放端,1开放端,2开放端]
    [1, 10, 100],    // 长度3
    [10, 100, 1000], // 长度4
    [10000, 10000, 10000] // 长度5及以上
];

// 评估单条线的得分
function scoreLine(line, player) {
    let score = 0;
    let j = 0;
    while (j < line.length) {
        if (line[j] === player) {
            let k = j;
            while (k < line.length && line[k] === player) k++;
            const length = k - j;
            if (length >= 2) {
                let openEnds = 0;
                if (j > 0 && line[j - 1] === 0) openEnds++;
                if (k < line.length && line[k] === 0) openEnds++;
                const effectiveLength = Math.min(length, 5);
                score += scoreTable[effectiveLength][openEnds];
            }
            j = k;
        } else {
            j++;
        }
    }
    return score;
}

// 获取玩家总得分
function getScore(player) {
    let score = 0;
    // 水平
    for (let i = 0; i < boardSize; i++) {
        score += scoreLine(board[i], player);
    }
    // 垂直
    for (let j = 0; j < boardSize; j++) {
        const column = board.map(row => row[j]);
        score += scoreLine(column, player);
    }
    // 对角线(左上到右下)
    for (let k = 0; k < 2 * boardSize - 1; k++) {
        const line = [];
        for (let i = 0; i < boardSize; i++) {
            const j = k - i;
            if (j >= 0 && j < boardSize) line.push(board[i][j]);
        }
        if (line.length >= 5) score += scoreLine(line, player);
    }
    // 反对角线(右上到左下)
    for (let k = -boardSize + 1; k < boardSize; k++) {
        const line = [];
        for (let i = 0; i < boardSize; i++) {
            const j = i - k;
            if (j >= 0 && j < boardSize) line.push(board[i][j]);
        }
        if (line.length >= 5) score += scoreLine(line, player);
    }
    return score;
}

// 局面评估
function evaluate() {
    return getScore(2) - getScore(1); // AI(白棋)得分 - 玩家(黑棋)得分
}

// Minimax算法
function minimax(depth, alpha, beta, isMaximizing) {
    if (depth === 0) return evaluate();
    const candidates = getCandidates();
    if (isMaximizing) { // AI回合
        let maxEval = -Infinity;
        for (const { row, col } of candidates) {
            board[row][col] = 2;
            if (checkWin(row, col, 2)) {
                board[row][col] = 0;
                return 100000 - depth; // 优先更快获胜
            }
            const eval = minimax(depth - 1, alpha, beta, false);
            board[row][col] = 0;
            maxEval = Math.max(maxEval, eval);
            alpha = Math.max(alpha, eval);
            if (beta <= alpha) break;
        }
        return maxEval;
    } else { // 玩家回合
        let minEval = Infinity;
        for (const { row, col } of candidates) {
            board[row][col] = 1;
            if (checkWin(row, col, 1)) {
                board[row][col] = 0;
                return -100000 + depth; // 延缓失败
            }
            const eval = minimax(depth - 1, alpha, beta, true);
            board[row][col] = 0;
            minEval = Math.min(minEval, eval);
            beta = Math.min(beta, eval);
            if (beta <= alpha) break;
        }
        return minEval;
    }
}

// AI落子
function aiMove() {
    const difficulty = difficultySelect.value;
    if (difficulty === 'easy') {
        const emptyCells = getAllEmptyCells();
        if (emptyCells.length > 0) {
            const randomIndex = Math.floor(Math.random() * emptyCells.length);
            const { row, col } = emptyCells[randomIndex];
            handleClick(row, col);
        }
    } else {
        let depth = difficulty === 'medium' ? 3 : 4;
        const candidates = getCandidates();
        let bestMove = null;
        let bestValue = -Infinity;
        for (const { row, col } of candidates) {
            board[row][col] = 2;
            const moveValue = minimax(depth - 1, -Infinity, Infinity, false);
            board[row][col] = 0;
            if (moveValue > bestValue) {
                bestValue = moveValue;
                bestMove = { row, col };
            }
        }
        if (bestMove) handleClick(bestMove.row, bestMove.col);
    }
}

// 新游戏
newGameButton.addEventListener('click', () => {
    board.forEach(row => row.fill(0));
    history = [];
    currentPlayer = 1;
    gameOver = false;
    updateBoard();
    statusElement.textContent = '轮到黑棋';
});

// 悔棋
undoButton.addEventListener('click', () => {
    if (history.length < 2 || gameOver) return; // 回退玩家和AI一步
    history.pop(); // 移除AI一步
    const lastMove = history.pop(); // 移除玩家一步
    board[lastMove.row][lastMove.col] = 0;
    currentPlayer = lastMove.player;
    gameOver = false;
    updateBoard();
    statusElement.textContent = '轮到黑棋';
});

// 初始化状态
statusElement.textContent = '轮到黑棋';

效果图


38 Likes

AFF AICNN - aicnn | 领先的AI技术平台 | AI绘画 | AI插件 | AI语音生成 | GPTS

@hashnode
建议加大投入,今天有点网络繁忙

14 Likes

好的,感谢佬友,刚上线还有点小bug,在线上修复中

14 Likes

强啊,我之前用cline写个笨死了,都不知道封堵

14 Likes

我就是写给那些说Grok 代码能力不行的人看的,哈哈哈

12 Likes

14 Likes

@F-droid 代码很强的

11 Likes

水平还是很强的,分享一把离谱的

11 Likes

看来还要优化,我下的几局都是正常的

2 Likes

特殊情况,不按这个套路下水平还是可以的

3 Likes

2轮搞出来的很不错了

3 Likes

懒得改了,这个帖子就是展示Grok快速写前端的能力

1 Like

官方api都还没放出来啊,这个grok3哪里来的?

1 Like

逆向的。。

2 Likes

你们速度也太快了。。。

2 Likes

刚刚战胜了困难模式的白棋,感觉有点问题,grok可能不太清楚五子棋规则,三三禁手、四四禁手都不知道。不过页面做得很精美,很好玩

2 Likes

算好的了,别的模型要很多轮都不一定达到这个效果

2 Likes

也许这就是下一个前端之神

2 Likes

这前端堪比claude了,不清楚claude能不能做出来这样的。估计能是能,但是要修改好几轮

2 Likes

现在xAI估值已经超过Anthropic了