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() {
        // 更新逻辑(如有需要)
    }
}
相关推荐
小小小小宇7 分钟前
前端监控用户停留时长
前端
小小小小宇16 分钟前
前端性能监控笔记
前端
烛阴1 小时前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小51 小时前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步
穗余1 小时前
NodeJS全栈开发面试题讲解——P6安全与鉴权
前端·sql·xss
穗余2 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js
航Hang*3 小时前
WEBSTORM前端 —— 第3章:移动 Web —— 第4节:移动适配-VM
前端·笔记·edge·less·css3·html5·webstorm
江城开朗的豌豆3 小时前
JavaScript篇:a==0 && a==1 居然能成立?揭秘JS中的"魔法"比较
前端·javascript·面试
江城开朗的豌豆3 小时前
JavaScript篇:setTimeout遇上for循环:为什么总是输出5?如何正确输出0-4?
前端·javascript·面试
橘子味的冰淇淋~3 小时前
npm run build 报错:Some chunks are larger than 500 KB after minification
前端·npm·node.js