AI中国象棋双人游戏开发尝试:AI_Grok

前言

  • 让AI Grok做双人象棋小游戏,但是通过一次提示词的话,效果并不理想,只能说能玩而已,还需要多次提示词去优化游戏才行
  • 废话不多说,上代码

环境说明

  • Grok 我是直接在网页上使用的 Grok3
    • 先预告一点结果小内容:Grok 网页上输出结果非常快,Grok都生成好了,Trea上的 Gemini 2.5 还在一步一步的思考,非常慢,这可能更多是Trea的问题,过程中经常出现修复修改失败这种情况,循环尝试后,就最终失败告终

提示词

js 复制代码
这是份微信小程序中的小游戏代码的主场景游代码,我现在要你基于现在的代码,帮我实现一个双人中国象棋游戏,象棋棋盘位于整个屏幕的居中位置,棋盘以及棋盘中的格子和棋盘中的棋子尺寸都基于屏幕的宽度进行计算,屏幕宽度越宽尺寸就相对增加,整个棋盘的宽度是屏幕宽度的90%,棋盘中的棋子分为红方和黑方,即棋子中的文字颜色分为红色和黑色,棋子的背景色都是近似木头的浅色,棋子都是圆形的;红方和黑方棋谱分布方式为上下分布,中间楚河汉界,上方为红方,下方为黑方;象棋玩法你可以去网上查找,就是以往传统的中国象棋游戏,最终如果有一方胜利了,就用console.log打印出胜利的一方,如:红方胜利;且有一方胜利后,棋局就不能再继续了;而且要通过实现drawWinResult并调用drawWinResult函数在屏幕居中显示一个展示棋局结果的矩形信息区域,这个矩形区域边框是黄色,背景是咖啡色,里面文字水平和垂直都居中展示,文字颜色是白色,字体尺寸是屏幕宽度的30%,字体颜色是白色,这个矩形信息区域的宽度与屏幕宽度相同,高度则比其中的文字高5%,其中文字内容则展示胜利结果,如"红方胜"或"黑方胜"

请根据我上面的需求帮我实现这个双人中国象棋游戏吧

下面是我这份已有的主场景代码 /* 棋盘 */ import { SCREEN_WIDTH, SCREEN_HEIGHT } from '../../render';

/** * 获取点击屏幕的坐标 * @param {*} event * @returns */ function getTouchPosition(event) { const touches = event.changedTouches; if (!touches || touches.length === 0) return null;

const touch = touches[0]; const touchX = touch.clientX; const touchY = touch.clientY;

return { x: touchX, y: touchY }; }

const CAN_BACK_STEP_COUNT = 1; // 可悔棋的步数

export default class Chessboard { constructor(musicCtrl) { this.musicCtrl = musicCtrl; this.isPlayedMusic = false; // --

this.initBoard(); this.bindTouchEvent(); }

bindTouchEvent() { wx.onTouchEnd(this.handleClick.bind(this)); }

initBoard() { }

/** * 悔棋 */ backstep() { }

handleClick(event) { const position = getTouchPosition(event); if (!position) return;

const { x: canvasX, y: canvasY } = position; }

drawWinResult(ctx, text) { }

update() { }

render(ctx) { if (!this.ctx) { this.ctx = ctx; }

this.update(); } }

Grok 结果

  • 效果截图
    • 体验:只能说能玩,但是AI几乎不懂得优化,比如中间的楚河汉界就是直接覆盖掉了格子,还有整个棋盘画的也不理想,点击棋子后,倒是有选中棋子的状态,但是,画面的其他棋子边框也会发生一点影响,总之效果还要多次通过提示词来进行优化,现在只能说能玩而已
js 复制代码
/* 棋盘
*/
import { SCREEN_WIDTH, SCREEN_HEIGHT } from '../../render';


/**
 * 获取点击屏幕的坐标
 * @param {*} event 
 * @returns 
 */
function getTouchPosition(event) {
    const touches = event.changedTouches;
    if (!touches || touches.length === 0) return null;

    const touch = touches[0];
    const touchX = touch.clientX;
    const touchY = touch.clientY;

    return { x: touchX, y: touchY };
}

const CAN_BACK_STEP_COUNT = 1; // 可悔棋的步数

export default class Chessboard {
    constructor(musicCtrl) {
        this.musicCtrl = musicCtrl;
        this.isPlayedMusic = false;

        this.initBoard();
        this.bindTouchEvent();
    }

    initBoard() {
        // 棋盘参数
        this.boardWidth = SCREEN_WIDTH * 0.9; // 棋盘宽度为屏幕宽度的90%
        this.cellSize = this.boardWidth / 9; // 每格尺寸
        this.pieceRadius = this.cellSize * 0.4; // 棋子半径
        this.boardLeft = (SCREEN_WIDTH - this.boardWidth) / 2; // 棋盘左边界
        this.boardTop = (SCREEN_HEIGHT - this.boardWidth * 10 / 9) / 2; // 棋盘上边界,纵横比10:9
        this.boardHeight = this.boardWidth * 10 / 9; // 棋盘高度
        this.history = []; // 记录走棋历史
        this.selectedPiece = null; // 当前选中的棋子
        this.currentPlayer = 'red'; // 当前玩家:red 或 black
        this.gameOver = false; // 游戏是否结束

        // 初始化棋盘(10行9列,0表示无棋子,1表示红方,2表示黑方)
        this.board = Array(10).fill().map(() => Array(9).fill(0));

        // 棋子初始位置(以棋盘坐标表示,[row, col])
        this.pieces = [
            // 红方棋子
            { type: '車', player: 'red', pos: [9, 0], alive: true },
            { type: '馬', player: 'red', pos: [9, 1], alive: true },
            { type: '象', player: 'red', pos: [9, 2], alive: true },
            { type: '士', player: 'red', pos: [9, 3], alive: true },
            { type: '将', player: 'red', pos: [9, 4], alive: true },
            { type: '士', player: 'red', pos: [9, 5], alive: true },
            { type: '象', player: 'red', pos: [9, 6], alive: true },
            { type: '馬', player: 'red', pos: [9, 7], alive: true },
            { type: '車', player: 'red', pos: [9, 8], alive: true },
            { type: '炮', player: 'red', pos: [7, 1], alive: true },
            { type: '炮', player: 'red', pos: [7, 7], alive: true },
            { type: '兵', player: 'red', pos: [6, 0], alive: true },
            { type: '兵', player: 'red', pos: [6, 2], alive: true },
            { type: '兵', player: 'red', pos: [6, 4], alive: true },
            { type: '兵', player: 'red', pos: [6, 6], alive: true },
            { type: '兵', player: 'red', pos: [6, 8], alive: true },
            // 黑方棋子
            { type: '車', player: 'black', pos: [0, 0], alive: true },
            { type: '馬', player: 'black', pos: [0, 1], alive: true },
            { type: '象', player: 'black', pos: [0, 2], alive: true },
            { type: '士', player: 'black', pos: [0, 3], alive: true },
            { type: '将', player: 'black', pos: [0, 4], alive: true },
            { type: '士', player: 'black', pos: [0, 5], alive: true },
            { type: '象', player: 'black', pos: [0, 6], alive: true },
            { type: '馬', player: 'black', pos: [0, 7], alive: true },
            { type: '車', player: 'black', pos: [0, 8], alive: true },
            { type: '炮', player: 'black', pos: [2, 1], alive: true },
            { type: '炮', player: 'black', pos: [2, 7], alive: true },
            { type: '卒', player: 'black', pos: [3, 0], alive: true },
            { type: '卒', player: 'black', pos: [3, 2], alive: true },
            { type: '卒', player: 'black', pos: [3, 4], alive: true },
            { type: '卒', player: 'black', pos: [3, 6], alive: true },
            { type: '卒', player: 'black', pos: [3, 8], alive: true },
        ];

        // 更新棋盘状态
        this.updateBoard();
    }

    bindTouchEvent() {
        wx.onTouchEnd(this.handleClick.bind(this));
    }

    updateBoard() {
        // 清空棋盘
        this.board = Array(10).fill().map(() => Array(9).fill(0));
        // 更新棋子位置
        for (const piece of this.pieces) {
            if (piece.alive) {
                const [row, col] = piece.pos;
                this.board[row][col] = piece.player === 'red' ? 1 : 2;
            }
        }
    }

    // 将屏幕坐标转换为棋盘坐标
    screenToBoard(x, y) {
        const col = Math.round((x - this.boardLeft) / this.cellSize);
        const row = Math.round((y - this.boardTop) / this.cellSize);
        return { row, col };
    }

    // 将棋盘坐标转换为屏幕坐标
    boardToScreen(row, col) {
        const x = this.boardLeft + col * this.cellSize;
        const y = this.boardTop + row * this.cellSize;
        return { x, y };
    }

    // 检查移动是否合法
    isValidMove(piece, newRow, newCol) {
        if (this.gameOver) return false;
        if (newRow < 0 || newRow > 9 || newCol < 0 || newCol > 8) return false;
        const [oldRow, oldCol] = piece.pos;
        const targetPiece = this.pieces.find(p => p.alive && p.pos[0] === newRow && p.pos[1] === newCol);

        // 不能吃自己方的棋子
        if (targetPiece && targetPiece.player === piece.player) return false;

        // 棋子移动规则
        switch (piece.type) {
            case '車':
                return this.isValidRookMove(oldRow, oldCol, newRow, newCol);
            case '馬':
                return this.isValidKnightMove(oldRow, oldCol, newRow, newCol);
            case '象':
                return this.isValidElephantMove(oldRow, oldCol, newRow, newCol, piece.player);
            case '士':
                return this.isValidAdvisorMove(oldRow, oldCol, newRow, newCol, piece.player);
            case '将':
                return this.isValidKingMove(oldRow, oldCol, newRow, newCol, piece.player);
            case '炮':
                return this.isValidCannonMove(oldRow, oldCol, newRow, newCol, targetPiece);
            case '兵':
            case '卒':
                return this.isValidPawnMove(oldRow, oldCol, newRow, newCol, piece.player);
            default:
                return false;
        }
    }

    isValidRookMove(oldRow, oldCol, newRow, newCol) {
        if (oldRow !== newRow && oldCol !== newCol) return false;
        if (oldRow === newRow) {
            const step = oldCol < newCol ? 1 : -1;
            for (let col = oldCol + step; col !== newCol; col += step) {
                if (this.board[oldRow][col] !== 0) return false;
            }
        } else {
            const step = oldRow < newRow ? 1 : -1;
            for (let row = oldRow + step; row !== newRow; row += step) {
                if (this.board[row][oldCol] !== 0) return false;
            }
        }
        return true;
    }

    isValidKnightMove(oldRow, oldCol, newRow, newCol) {
        const rowDiff = Math.abs(newRow - oldRow);
        const colDiff = Math.abs(newCol - oldCol);
        if (!((rowDiff === 2 && colDiff === 1) || (rowDiff === 1 && colDiff === 2))) return false;
        if (rowDiff === 2) {
            const midRow = (oldRow + newRow) / 2;
            if (this.board[midRow][oldCol] !== 0) return false;
        } else {
            const midCol = (oldCol + newCol) / 2;
            if (this.board[oldRow][midCol] !== 0) return false;
        }
        return true;
    }

    isValidElephantMove(oldRow, oldCol, newRow, newCol, player) {
        if (player === 'red' && newRow < 5 || player === 'black' && newRow > 4) return false;
        if (Math.abs(newRow - oldRow) !== 2 || Math.abs(newCol - oldCol) !== 2) return false;
        const midRow = (oldRow + newRow) / 2;
        const midCol = (oldCol + newCol) / 2;
        return this.board[midRow][midCol] === 0;
    }

    isValidAdvisorMove(oldRow, oldCol, newRow, newCol, player) {
        if (player === 'red' && (newRow < 7 || newCol < 3 || newCol > 5)) return false;
        if (player === 'black' && (newRow > 2 || newCol < 3 || newCol > 5)) return false;
        return Math.abs(newRow - oldRow) === 1 && Math.abs(newCol - oldCol) === 1;
    }

    isValidKingMove(oldRow, oldCol, newRow, newCol, player) {
        if (player === 'red' && (newRow < 7 || newCol < 3 || newCol > 5)) return false;
        if (player === 'black' && (newRow > 2 || newCol < 3 || newCol > 5)) return false;
        if (Math.abs(newRow - oldRow) + Math.abs(newCol - oldCol) !== 1) return false;
        // 将帅对面
        if (oldCol === newCol && this.board[newRow][newCol] === (player === 'red' ? 2 : 1)) {
            const step = player === 'red' ? -1 : 1;
            for (let row = oldRow + step; row !== newRow; row += step) {
                if (this.board[row][newCol] !== 0) return false;
            }
            return true;
        }
        return true;
    }

    isValidCannonMove(oldRow, oldCol, newRow, newCol, targetPiece) {
        if (oldRow !== newRow && oldCol !== newCol) return false;
        let pieceCount = 0;
        if (oldRow === newRow) {
            const step = oldCol < newCol ? 1 : -1;
            for (let col = oldCol + step; col !== newCol; col += step) {
                if (this.board[oldRow][col] !== 0) pieceCount++;
            }
        } else {
            const step = oldRow < newRow ? 1 : -1;
            for (let row = oldRow + step; row !== newRow; row += step) {
                if (this.board[row][oldCol] !== 0) pieceCount++;
            }
        }
        return (targetPiece && pieceCount === 1) || (!targetPiece && pieceCount === 0);
    }

    isValidPawnMove(oldRow, oldCol, newRow, newCol, player) {
        const rowDiff = newRow - oldRow;
        const colDiff = Math.abs(newCol - oldCol);
        if (colDiff > 1 || Math.abs(rowDiff) > 1) return false;
        if (player === 'red') {
            if (oldRow <= 4 && colDiff === 1) return false; // 过河前只能向前
            if (rowDiff > 0) return false; // 只能向前
            return colDiff === 0 || (colDiff === 1 && oldRow <= 4);
        } else {
            if (oldRow >= 5 && colDiff === 1) return false; // 过河前只能向前
            if (rowDiff < 0) return false; // 只能向前
            return colDiff === 0 || (colDiff === 1 && oldRow >= 5);
        }
    }

    // 检查是否胜利
    checkWin() {
        const redKing = this.pieces.find(p => p.type === '将' && p.player === 'red' && p.alive);
        const blackKing = this.pieces.find(p => p.type === '将' && p.player === 'black' && p.alive);
        if (!redKing) {
            console.log('黑方胜利');
            this.gameOver = true;
            return 'black';
        }
        if (!blackKing) {
            console.log('红方胜利');
            this.gameOver = true;
            return 'red';
        }
        return null;
    }

    handleClick(event) {
        if (this.gameOver) return;
        const position = getTouchPosition(event);
        if (!position) return;

        const { x, y } = position;
        const { row, col } = this.screenToBoard(x, y);

        if (row < 0 || row > 9 || col < 0 || col > 8) return;

        const clickedPiece = this.pieces.find(p => p.alive && p.pos[0] === row && p.pos[1] === col);

        if (this.selectedPiece) {
            if (this.isValidMove(this.selectedPiece, row, col)) {
                const targetPiece = this.pieces.find(p => p.alive && p.pos[0] === row && p.pos[1] === col);
                if (targetPiece) {
                    targetPiece.alive = false;
                }
                this.history.push({
                    piece: { ...this.selectedPiece },
                    from: [...this.selectedPiece.pos],
                    to: [row, col],
                    eaten: targetPiece ? { ...targetPiece } : null
                });
                this.selectedPiece.pos = [row, col];
                this.updateBoard();
                const winner = this.checkWin();
                if (winner) {
                    this.drawWinResult(this.ctx, winner === 'red' ? '红方胜' : '黑方胜');
                } else {
                    this.currentPlayer = this.currentPlayer === 'red' ? 'black' : 'red';
                }
                this.selectedPiece = null;
            } else {
                this.selectedPiece = null;
            }
        } else if (clickedPiece && clickedPiece.player === this.currentPlayer) {
            this.selectedPiece = clickedPiece;
        }
    }

    backstep() {
        if (this.gameOver || this.history.length === 0 || this.history.length > CAN_BACK_STEP_COUNT) return;
        const lastMove = this.history.pop();
        lastMove.piece.pos = lastMove.from;
        if (lastMove.eaten) {
            lastMove.eaten.alive = true;
        }
        this.updateBoard();
        this.currentPlayer = this.currentPlayer === 'red' ? 'black' : 'red';
        this.selectedPiece = null;
    }

    drawWinResult(ctx, text) {
        const fontSize = SCREEN_WIDTH * 0.3;
        const rectWidth = SCREEN_WIDTH;
        const rectHeight = fontSize * 1.05;
        const rectX = 0;
        const rectY = (SCREEN_HEIGHT - rectHeight) / 2;

        ctx.fillStyle = '#8B4513'; // 咖啡色背景
        ctx.strokeStyle = '#FFFF00'; // 黄色边框
        ctx.lineWidth = 2;
        ctx.fillRect(rectX, rectY, rectWidth, rectHeight);
        ctx.strokeRect(rectX, rectY, rectWidth, rectHeight);

        ctx.fillStyle = '#FFFFFF'; // 白色文字
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(text, SCREEN_WIDTH / 2, rectY + rectHeight / 2);
    }

    render(ctx) {
        if (!this.ctx) {
            this.ctx = ctx;
        }

        // 绘制棋盘背景
        ctx.fillStyle = '#F4A460'; // 木质颜色
        ctx.fillRect(this.boardLeft, this.boardTop, this.boardWidth, this.boardHeight);

        // 绘制格子线
        ctx.strokeStyle = '#000000';
        ctx.lineWidth = 2;
        for (let i = 0; i <= 9; i++) {
            const y = this.boardTop + i * this.cellSize;
            ctx.beginPath();
            ctx.moveTo(this.boardLeft, y);
            ctx.lineTo(this.boardLeft + this.boardWidth, y);
            ctx.stroke();
        }
        for (let i = 0; i <= 8; i++) {
            const x = this.boardLeft + i * this.cellSize;
            ctx.beginPath();
            ctx.moveTo(x, this.boardTop);
            ctx.lineTo(x, this.boardTop + this.boardHeight);
            ctx.stroke();
        }

        // 绘制楚河汉界
        ctx.fillStyle = '#FFFFFF';
        ctx.fillRect(this.boardLeft, this.boardTop + 4.5 * this.cellSize, this.boardWidth, this.cellSize);
        ctx.fillStyle = '#000000';
        ctx.font = `${this.cellSize * 0.5}px Arial`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText('楚河            汉界', this.boardLeft + this.boardWidth / 2, this.boardTop + 5 * this.cellSize);

        // 绘制九宫格
        const drawPalace = (rowStart, colStart) => {
            ctx.beginPath();
            ctx.moveTo(this.boardLeft + colStart * this.cellSize, this.boardTop + rowStart * this.cellSize);
            ctx.lineTo(this.boardLeft + (colStart + 2) * this.cellSize, this.boardTop + (rowStart + 2) * this.cellSize);
            ctx.moveTo(this.boardLeft + (colStart + 2) * this.cellSize, this.boardTop + rowStart * this.cellSize);
            ctx.lineTo(this.boardLeft + colStart * this.cellSize, this.boardTop + (rowStart + 2) * this.cellSize);
            ctx.stroke();
        };
        drawPalace(0, 3); // 黑方九宫格
        drawPalace(7, 3); // 红方九宫格

        // 绘制棋子
        for (const piece of this.pieces) {
            if (piece.alive) {
                const { x, y } = this.boardToScreen(piece.pos[0], piece.pos[1]);
                ctx.fillStyle = '#DEB887'; // 木质浅色
                ctx.beginPath();
                ctx.arc(x, y, this.pieceRadius, 0, Math.PI * 2);
                ctx.fill();
                ctx.strokeStyle = '#000000';
                ctx.stroke();
                ctx.fillStyle = piece.player === 'red' ? '#FF0000' : '#000000';
                ctx.font = `${this.pieceRadius * 0.8}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(piece.type, x, y);
                if (piece === this.selectedPiece) {
                    ctx.strokeStyle = '#00FF00';
                    ctx.lineWidth = 3;
                    ctx.beginPath();
                    ctx.arc(x, y, this.pieceRadius, 0, Math.PI * 2);
                    ctx.stroke();
                }
            }
        }

        // 如果游戏结束,绘制胜利结果
        if (this.gameOver) {
            const winner = this.checkWin();
            if (winner) {
                this.drawWinResult(ctx, winner === 'red' ? '红方胜' : '黑方胜');
            }
        }

        this.update();
    }

    update() {
        // 更新逻辑(如有需要)
    }
}
相关推荐
whysqwhw1 小时前
js之Promise
前端
恋猫de小郭4 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
chinahcp20085 小时前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
gnip6 小时前
浏览器跨标签页通信方案详解
前端·javascript
gnip7 小时前
运行时模块批量导入
前端·javascript
hyy27952276847 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
逆风优雅7 小时前
vue实现模拟 ai 对话功能
前端·javascript·html
若梦plus8 小时前
http基于websocket协议通信分析
前端·网络协议
不羁。。8 小时前
【web站点安全开发】任务3:网页开发的骨架HTML与美容术CSS
前端·css·html
这是个栗子8 小时前
【问题解决】Vue调试工具Vue Devtools插件安装后不显示
前端·javascript·vue.js