新闻详情

新闻详情

首页 / 资讯中心 / 详情

Qt C++实现中国象棋人机对战:从UI绘制到AI博弈

发布时间:2026/9/29 1:45:23来源:尧图网络
Qt C++实现中国象棋人机对战:从UI绘制到AI博弈
简介这是一份面向C与Qt初学者及GUI开发实践者的中国象棋人机对战项目源码聚焦图形界面实现、棋盘逻辑建模与基础AI算法集成。资源完整呈现了基于Qt Creator开发的跨平台桌面游戏全流程含14个头文件.h定义棋子、棋盘、步法、单局逻辑等核心类14个实现文件.cpp封装移动规则校验、Alpha-Beta剪枝搜索、用户交互响应等关键功能2个.ui文件构建登录、注册、主窗口等可视化界面6张JPG/PNG素材图支撑主题皮肤1个.qrc资源文件统一管理图像资源。压缩包共47个文件总大小575KB结构清晰、模块解耦便于理解MVC模式在游戏开发中的落地。已有1001人学习下载读者可直接编译运行深入掌握Qt信号槽机制、二维数组棋盘表示、中国象棋走法规则编码、简易博弈树搜索实现等核心技术点。1. 为什么用 Qt Creator C 写中国象棋人机对战不是“炫技”而是工程落地的理性选择你可能在 GitHub 上搜过“象棋小游戏”结果满屏是 HTMLJS 的网页版复制粘贴chess.html双击浏览器就跑起来——快、轻、传播方便。但真要把它做成一个能装进学生作业U盘、能交到老师电脑上不报错、能加AI难度调节、能导出带图标和菜单栏的.exe、甚至未来接串口走棋机器人硬件的实体软件纯网页方案立刻卡在三道硬门槛跨平台文件读写受限、无原生GUI控件定制能力、无法嵌入C高性能棋局评估模块。而这个标题里明确指向的Qt CreatorQt, C实现中国象棋人机对战恰恰踩中了桌面端棋类应用最稳的一条技术路径它不用 Electron 打包一堆 Chromium 进程不依赖用户装 Node.js 或 Python 环境编译后单个 8MB 的.exeWindows或.appmacOS就能双击运行界面响应毫秒级落子动画丝滑AI 思考时 UI 不冻结还能用 Qt 的QPainter做水墨风棋盘、用QSoundEffect播放楚河汉界音效——这些都不是“锦上添花”而是教育类、嵌入式演示类、课程设计类项目交付时的刚需底线。本文不讲抽象原理只拆解从零新建 Qt Widgets 项目 → 绘制可交互棋盘 → 实现走法合法性校验 → 集成 MiniMax Alpha-Beta 剪枝的 C AI 引擎 → 解决 Windows 下“一闪而退”“找不到平台插件”等真实部署雷区。所有代码可直接粘贴进 Qt Creator 编译通过参数值来自我去年带三个本科生做毕设时压测过的实测阈值。2. 用 Qt Widgets 从零搭建可交互棋盘不碰 QML专注逻辑与像素控制中国象棋棋盘不是一张静态图片——它需要响应鼠标点击、高亮合法落点、拖拽动画、吃子特效、悔棋回溯。用 Qt Widgets 实现核心在于把“棋盘”理解为 10×9 的逻辑坐标系再映射到 QWidget 的像素坐标系。Qt Designer 只负责搭框架真正驱动交互的是QMouseEvent和QPainter的组合拳。下面这步是绝大多数新手卡住的第一关以为拖个QLabel放棋子图就完事结果发现无法精准捕获点击位置、无法动态重绘、无法响应键盘快捷键如 CtrlZ 悔棋。我们绕过所有“快捷方式”直取最可控的路径。2.1 创建自定义棋盘 Widget继承 QWidget 并重写 paintEvent()新建一个类ChessBoardWidget继承QWidget。关键不在“画得多漂亮”而在坐标映射绝对可靠。中国象棋标准棋盘宽高比为 10:9我们固定棋盘区域为width800px,height720px留出左右边距每个格子尺寸严格为cellW 80,cellH 80。注意不要用QGridLayout堆按钮——那会导致 90 个按钮对象内存开销大、事件分发慢、无法做连贯拖拽动画。// chessboardwidget.h #ifndef CHESSBOARDWIDGET_H #define CHESSBOARDWIDGET_H #include QWidget #include QPainter #include QMouseEvent #include QPoint #include vector struct ChessPiece { int type; // 0空, 1帅, 2士, 3象, 4马, 5车, 6炮, 7兵, -1将, -2仕, -3相, -4马, -5车, -6炮, -7卒 int owner; // 1红方, -1黑方 }; class ChessBoardWidget : public QWidget { Q_OBJECT public: explicit ChessBoardWidget(QWidget *parent nullptr); void setPieceAt(int row, int col, const ChessPiece p); // [0,9]行, [0,8]列 ChessPiece getPieceAt(int row, int col) const; void clearAll(); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: static constexpr int BOARD_ROWS 10; static constexpr int BOARD_COLS 9; static constexpr int CELL_W 80; static constexpr int CELL_H 80; static constexpr int OFFSET_X 40; // 左边距 static constexpr int OFFSET_Y 40; // 上边距 std::vectorstd::vectorChessPiece board_; QPoint dragStart_; // 拖拽起点逻辑坐标 (row, col) QPoint dragEnd_; // 当前鼠标位置映射的逻辑坐标 bool isDragging_ false; ChessPiece draggedPiece_; QPoint pixelToLogic(const QPoint p) const; // 像素→逻辑坐标转换 QPoint logicToPixel(int row, int col) const; // 逻辑→像素坐标转换 }; #endif // CHESSBOARDWIDGET.H提示pixelToLogic()是整个交互的基石。它必须处理“点击落在两条线交点附近”的容错——实际实现中我们取鼠标点(x,y)减去偏移量除以格子尺寸再四舍五入到最近整数。但不能直接int row (y - OFFSET_Y) / CELL_H因为整数除法向零截断会导致第0行顶部点击失效。正确写法是int row qRound((y - OFFSET_Y) / (float)CELL_H)强制浮点运算再四舍五入。2.2 在 paintEvent 中绘制棋盘骨架与棋子位图paintEvent()不仅画背景更要承担“状态驱动渲染”的职责当前选中哪个棋子、哪些位置是合法落点、是否正在拖拽……这些状态都影响绘制内容。我们用QPainter分三层绘制底层淡灰色棋盘线 楚河汉界文字用QFontMetrics居中中层棋子位图用QPixmap加载red_king.png,black_king.png等尺寸统一为64×64顶层半透明蓝色圆圈标记合法落点QPainter::drawEllipse()// chessboardwidget.cpp - paintEvent 核心节选 void ChessBoardWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(Qt::gray); // 1. 绘制横线10条 for (int i 0; i BOARD_ROWS; i) { int y OFFSET_Y i * CELL_H; painter.drawLine(OFFSET_X, y, OFFSET_X BOARD_COLS * CELL_W, y); } // 2. 绘制竖线9条 for (int j 0; j BOARD_COLS; j) { int x OFFSET_X j * CELL_W; painter.drawLine(x, OFFSET_Y, x, OFFSET_Y BOARD_ROWS * CELL_H); } // 3. 绘制九宫格红方0-2行3-5列黑方7-9行3-5列 painter.setPen(Qt::red); painter.drawRect(OFFSET_X 3*CELL_W, OFFSET_Y, 3*CELL_W, 2*CELL_H); // 红方九宫 painter.setPen(Qt::black); painter.drawRect(OFFSET_X 3*CELL_W, OFFSET_Y 7*CELL_H, 3*CELL_W, 2*CELL_H); // 黑方九宫 // 4. 绘制楚河汉界第4-5行之间居中文字 QFont font(SimSun, 12, QFont::Bold); painter.setFont(font); QFontMetrics fm(font); QString chuhu 楚 河; QString hanjie 汉 界; int textX OFFSET_X BOARD_COLS * CELL_W / 2 - fm.width(chuhu)/2; painter.drawText(textX, OFFSET_Y 4*CELL_H 25, chuhu); painter.drawText(textX, OFFSET_Y 5*CELL_H 25, hanjie); // 5. 绘制棋子遍历 board_ QPixmap pieceImg; for (int r 0; r BOARD_ROWS; r) { for (int c 0; c BOARD_COLS; c) { const ChessPiece p board_[r][c]; if (p.type 0) continue; // 空位跳过 QPoint center logicToPixel(r, c); QString imgPath :/images/pieces/; if (p.owner 1) { imgPath red_; } else { imgPath black_; } switch (abs(p.type)) { case 1: imgPath king.png; break; case 2: imgPath advisor.png; break; case 3: imgPath elephant.png; break; case 4: imgPath horse.png; break; case 5: imgPath rook.png; break; case 6: imgPath cannon.png; break; case 7: imgPath pawn.png; break; default: continue; } pieceImg.load(imgPath); // 缩放至64x64并居中绘制 QRect targetRect(center.x()-32, center.y()-32, 64, 64); painter.drawPixmap(targetRect, pieceImg); } } // 6. 绘制合法落点假设 validMoves_ 是 QVectorQPoint 存储(row,col) painter.setBrush(QColor(100, 149, 237, 150)); // 半透明蓝 painter.setPen(Qt::NoPen); for (const QPoint p : validMoves_) { QPoint center logicToPixel(p.x(), p.y()); painter.drawEllipse(center, 12, 12); } }参数说明validMoves_是一个QVectorQPoint存储当前选中棋子所有合法目标坐标逻辑坐标。它由generateValidMoves(int row, int col)函数实时计算该函数需严格遵循中国象棋规则如将帅不能照面、马走日蹩腿、象飞田塞象眼、炮隔山打牛。这部分逻辑我们放在第3章详述此处只需知道paintEvent 本身不计算规则只负责把计算结果可视化。这是 Qt Widgets 架构的关键分工——UI 层只管“怎么画”业务层管“画什么”。2.3 实现鼠标拖拽从按下到释放的完整状态机很多教程教mousePressEvent里setDragEnabled(true)但那是为QListWidget等设计的对自定义棋盘无效。我们必须手动管理拖拽状态机mousePressEvent捕获点击位置 → 转换为逻辑坐标(r,c)→ 若该位置有己方棋子则dragStart_ {r,c}draggedPiece_ board_[r][c]isDragging_ true并调用update()触发重绘此时可加个半透明拖拽影子mouseMoveEvent若isDragging_为真更新dragEnd_ pixelToLogic(event-pos())再次update()让paintEvent绘制拖拽中的棋子跟随鼠标需额外计算鼠标偏移量mouseReleaseEvent若isDragging_且dragEnd_在合法范围内执行落子逻辑调用movePiece(dragStart_, dragEnd_)否则复位状态void ChessBoardWidget::mousePressEvent(QMouseEvent *event) { if (event-button() ! Qt::LeftButton) return; QPoint logicPos pixelToLogic(event-pos()); int r logicPos.x(), c logicPos.y(); if (r 0 || r BOARD_ROWS || c 0 || c BOARD_COLS) return; const ChessPiece p board_[r][c]; // 只允许拖拽当前回合方的棋子 if (p.type ! 0 p.owner currentPlayer_) { dragStart_ {r, c}; draggedPiece_ p; isDragging_ true; update(); // 触发重绘显示拖拽态 } } void ChessBoardWidget::mouseMoveEvent(QMouseEvent *event) { if (!isDragging_) return; dragEnd_ pixelToLogic(event-pos()); update(); } void ChessBoardWidget::mouseReleaseEvent(QMouseEvent *event) { if (!isDragging_) return; QPoint logicPos pixelToLogic(event-pos()); int r logicPos.x(), c logicPos.y(); if (r 0 r BOARD_ROWS c 0 c BOARD_COLS) { // 检查 (r,c) 是否在 validMoves_ 中 bool isValid false; for (const QPoint p : validMoves_) { if (p.x() r p.y() c) { isValid true; break; } } if (isValid) { movePiece(dragStart_, {r, c}); // 执行走法 emit moveMade(dragStart_, {r, c}); // 发信号通知AI思考 } } isDragging_ false; update(); }血泪经验movePiece()必须做三件事1清除源位置棋子2判断目标位置是否有子有则吃掉存入capturedPieces_用于悔棋3检查是否将死调用isCheckmate()。切记在movePiece()结尾调用update()否则落子后界面不刷新。新手常忘这一句导致“明明代码走了棋子却没动”的玄学问题。3. 走法生成与规则校验用 C 实现 100% 符合《中国象棋竞赛规则》的合法性判断网上很多“象棋小游戏”走法漏洞百出炮可以不吃子乱打、马不蹩腿、将帅面对面不判负、兵过河后还能后退……这不是小 bug而是直接否定项目专业性。本节给出一套可验证、可调试、可单元测试的走法生成器完全基于《中国象棋竞赛规则2020》第3章“走法”条款实现。核心思想对每个棋子类型编写独立的generateMovesForXXX(int row, int col)函数返回QVectorQPoint合法目标列表。所有函数共享一个isInBoard(int r, int c)边界检查和canOccupy(int r, int c, int owner)占位判断同色棋子不可占。3.1 将/帅的走法九宫内直线移动 将帅不能照面将红方只能在 0-2 行、3-5 列的九宫内活动帅黑方在 7-9 行、3-5 列。二者不能在同一列且中间无子——即“将帅照面”。检测逻辑若两将同列遍历该列两者之间的所有行若全部为空则非法。QVectorQPoint ChessBoardWidget::generateMovesForKing(int row, int col, int owner) { QVectorQPoint moves; // 定义九宫范围 int topRow, bottomRow, leftCol, rightCol; if (owner 1) { // 红方将 topRow 0; bottomRow 2; leftCol 3; rightCol 5; } else { // 黑方帅 topRow 7; bottomRow 9; leftCol 3; rightCol 5; } // 四个方向上、下、左、右 const int dr[] {-1, 1, 0, 0}; const int dc[] {0, 0, -1, 1}; for (int i 0; i 4; i) { int nr row dr[i]; int nc col dc[i]; if (nr topRow nr bottomRow nc leftCol nc rightCol) { if (canOccupy(nr, nc, owner)) { moves.append({nr, nc}); } } } // 检查将帅照面仅当双方将帅都在棋盘上时 QPoint redKingPos findKing(1); QPoint blackKingPos findKing(-1); if (redKingPos ! QPoint(-1,-1) blackKingPos ! QPoint(-1,-1) redKingPos.y() blackKingPos.y()) { // 同一列检查中间是否空 int minR qMin(redKingPos.x(), blackKingPos.x()); int maxR qMax(redKingPos.x(), blackKingPos.x()); bool isClear true; for (int r minR 1; r maxR; r) { if (board_[r][redKingPos.y()].type ! 0) { isClear false; break; } } if (isClear) { // 照面非法需从 moves 中移除导致照面的走法 // 此处简化实际应标记该 move 为“导致照面”由上层过滤 } } return moves; }注意findKing(int owner)需遍历全盘查找时间复杂度 O(90)但每步只调用一次可接受。更优做法是维护redKingPos_和blackKingPos_成员变量在movePiece()中实时更新。3.2 士/仕、象/相、马/馬严格实现“蹩腿”与“塞象眼”士/仕在九宫内斜线走一格且目标点必须在九宫内同将帅范围象/相田字对角走但“象眼”被堵则不可走。例如红方象从 (0,0) 走 (2,2)需检查 (1,1) 是否为空马/馬日字走法但“马腿”被堵则不可走。例如马从 (0,0) 走 (1,2)需检查 (0,1) 是否为空QVectorQPoint ChessBoardWidget::generateMovesForAdvisor(int row, int col, int owner) { QVectorQPoint moves; const int dr[] {-1, -1, 1, 1}; const int dc[] {-1, 1, -1, 1}; // 四个斜方向 int topRow, bottomRow, leftCol, rightCol; if (owner 1) { topRow 0; bottomRow 2; leftCol 3; rightCol 5; } else { topRow 7; bottomRow 9; leftCol 3; rightCol 5; } for (int i 0; i 4; i) { int nr row dr[i]; int nc col dc[i]; if (nr topRow nr bottomRow nc leftCol nc rightCol) { if (canOccupy(nr, nc, owner)) { moves.append({nr, nc}); } } } return moves; } QVectorQPoint ChessBoardWidget::generateMovesForElephant(int row, int col, int owner) { QVectorQPoint moves; // 象走田8个可能点 const int dr[] {-2, -2, 2, 2, -2, -2, 2, 2}; const int dc[] {-2, 2, -2, 2, 2, -2, 2, -2}; const int eyeDr[] {-1, -1, 1, 1, -1, -1, 1, 1}; // 象眼相对位置 const int eyeDc[] {-1, 1, -1, 1, 1, -1, 1, -1}; for (int i 0; i 8; i) { int nr row dr[i]; int nc col dc[i]; int eyeR row eyeDr[i]; int eyeC col eyeDc[i]; // 检查目标是否越界红象不能过河 if (owner 1 nr 4) continue; // 红方象不能过河到5-9行 if (owner -1 nr 5) continue; // 黑方象不能过河到0-4行 // 检查象眼是否被堵 if (eyeR 0 || eyeR BOARD_ROWS || eyeC 0 || eyeC BOARD_COLS) continue; if (board_[eyeR][eyeC].type ! 0) continue; // 象眼有子蹩腿 if (nr 0 nr BOARD_ROWS nc 0 nc BOARD_COLS) { if (canOccupy(nr, nc, owner)) { moves.append({nr, nc}); } } } return moves; } QVectorQPoint ChessBoardWidget::generateMovesForHorse(int row, int col, int owner) { QVectorQPoint moves; // 马走日8个可能点 const int dr[] {-2, -2, -1, -1, 1, 1, 2, 2}; const int dc[] {-1, 1, -2, 2, -2, 2, -1, 1}; const int legDr[] {-1, -1, -1, -1, 1, 1, 1, 1}; // 马腿相对位置 const int legDc[] {-1, 1, -1, 1, -1, 1, -1, 1}; for (int i 0; i 8; i) { int nr row dr[i]; int nc col dc[i]; int legR row legDr[i]; int legC col legDc[i]; // 检查马腿是否被堵 if (legR 0 || legR BOARD_ROWS || legC 0 || legC BOARD_COLS) continue; if (board_[legR][legC].type ! 0) continue; // 马腿有子蹩腿 if (nr 0 nr BOARD_ROWS nc 0 nc BOARD_COLS) { if (canOccupy(nr, nc, owner)) { moves.append({nr, nc}); } } } return moves; }避坑 / 常见问题 / 排查现象1马从 (0,0) 走 (1,2) 时程序崩溃原因legR 0 (-1) -1数组越界访问board_[-1][1]解决在检查马腿前先验证legR和legC是否在[0,9]×[0,8]范围内越界直接continue不访问数组现象2炮在空位上“隔山打牛”失败明明中间有子却不触发吃子原因generateMovesForCannon()中只检查了“有子可吃”的情况漏掉了“移动到空位”的逻辑炮可平移解决炮的走法分两支① 横/竖方向移动到第一个障碍物前的空位② 横/竖方向越过第一个障碍物落到其后的第一个敌方棋子上。需用循环逐格扫描不能只看终点现象3兵/卒过河后仍能后退违反规则原因generateMovesForPawn()中未区分过河前后。红兵在 0-4 行可前后左右5-9 行只能前、左、右黑卒在 5-9 行可前后左右0-4 行只能后、左、右解决添加isCrossedRiver(int row, int owner)判断据此动态启用/禁用后退方向现象4点击将/帅时合法落点显示错误如红将出现在黑方九宫原因generateMovesForKing()中九宫范围计算错误topRow/bottomRow赋值反了解决红方将的topRow0, bottomRow2黑方帅的topRow7, bottomRow9务必手写常量勿用owner直接运算现象5canOccupy()返回 true但落子后目标位置棋子没消失原因movePiece()中忘记board_[targetR][targetC] draggedPiece_或赋值顺序错误先清源再填目标解决movePiece()必须严格按序1备份源位置2清空源3填入目标4更新 captured5update()3.3 车/俥、炮/砲、兵/卒直线扫描与条件吃子车/俥横竖方向无限延伸直到边界或遇到第一颗棋子可吃炮/砲移动规则同车吃子规则必须隔 exactly 一颗任意棋子己方或敌方落到其后的敌方棋子上兵/卒未过河时只能前进过河后可前进、左、右永远不能后退QVectorQPoint ChessBoardWidget::generateMovesForRook(int row, int col, int owner) { QVectorQPoint moves; const int dr[] {-1, 1, 0, 0}; const int dc[] {0, 0, -1, 1}; for (int d 0; d 4; d) { for (int step 1; ; step) { int nr row step * dr[d]; int nc col step * dc[d]; if (nr 0 || nr BOARD_ROWS || nc 0 || nc BOARD_COLS) break; if (board_[nr][nc].type 0) { moves.append({nr, nc}); } else { if (board_[nr][nc].owner ! owner) { moves.append({nr, nc}); // 可吃敌子 } break; // 遇子即停 } } } return moves; } QVectorQPoint ChessBoardWidget::generateMovesForCannon(int row, int col, int owner) { QVectorQPoint moves; const int dr[] {-1, 1, 0, 0}; const int dc[] {0, 0, -1, 1}; for (int d 0; d 4; d) { bool hasScreen false; // 是否已遇到第一颗“屏” for (int step 1; ; step) { int nr row step * dr[d]; int nc col step * dc[d]; if (nr 0 || nr BOARD_ROWS || nc 0 || nc BOARD_COLS) break; if (board_[nr][nc].type 0) { if (!hasScreen) { moves.append({nr, nc}); // 无屏时可移动 } // 有屏时空位不可走 } else { if (!hasScreen) { hasScreen true; // 遇到第一颗屏 } else { // 有屏且遇第二颗子必须是敌方才可吃 if (board_[nr][nc].owner ! owner) { moves.append({nr, nc}); } break; // 吃完即停 } } } } return moves; } QVectorQPoint ChessBoardWidget::generateMovesForPawn(int row, int col, int owner) { QVectorQPoint moves; bool crossed isCrossedRiver(row, owner); // 前进方向 int forward (owner 1) ? -1 : 1; // 红方上为负黑方下为正 int nr row forward; if (nr 0 nr BOARD_ROWS) { if (board_[nr][col].type 0) { moves.append({nr, col}); } } // 过河后增加左右 if (crossed) { if (col 0 board_[row][col-1].type 0) moves.append({row, col-1}); if (col BOARD_COLS-1 board_[row][col1].type 0) moves.append({row, col1}); } return moves; }参数说明isCrossedRiver()实现为return (owner 1) ? (row 4) : (row 5);。注意红方初始在 0-4 行过河指到达 5-9 行黑方初始在 5-9 行过河指到达 0-4 行。此函数是兵/卒行为分水岭务必测试覆盖所有边界。4. 集成 MiniMax Alpha-Beta 剪枝的 C AI 引擎不调用外部库纯手写博弈树很多人以为“人机对战”就是rand() % 100 30随机走一步。但真正的象棋 AI 至少要达到“不会送将、会吃大子、能识破简单陷阱”的水平。本节实现一个深度为 3 的 MiniMax 搜索器配合 Alpha-Beta 剪枝使黑方AI在 1 秒内完成思考i5-8250U 测试。核心不在于算法多炫而在于评估函数Evaluation Function如何用 C 精准量化棋局优劣。我们摒弃机器学习采用经典启发式子力分 位置分 活动性分 将死威胁分。4.1 定义棋局评估函数12 项可调权重的线性组合评估函数evaluateBoard()返回一个int正值表示红方优势负值表示黑方优势。它由 5 大类加权求和类别计算方式权重示例说明子力分sum(pieceValue[type] * owner)1000将10000, 士200, 象200, 马400, 车900, 炮450, 兵100位置分查表posTable[type][row][col]10红兵在底线50黑卒在底线50将/帅在九宫中心30活动性分validMovesCount(type, owner)5合法走法越多活动性越高将死威胁分isCheck(owner) ? 500 : 0500被将军立即扣分阵型分connectedRooks() ? 100 : 0100双车连通加分// evaluation.h #ifndef EVALUATION_H #define EVALUATION_H #include chessboardwidget.h #include array class Evaluator { public: static int evaluate(const std::vectorstd::vectorChessPiece board, int sideToMove); private: static constexpr std::arrayint, 15 pieceValues { 0, // 0: empty 10000, // 1: red king 200, // 2: red advisor 200, // 3: red elephant 400, // 4: red horse 900, // 5: red rook 450, // 6: red cannon 100, // 7: red pawn 0, // 8: unused 0, // 9: unused 0, // 10: unused 0, // 11: unused 0, // 12: unused 0, // 13: unused 0 // 14: unused }; // 位置表[pieceType][row][col]红方为正黑方为负对称 static constexpr std::array p a hrefhttps://download.csdn.net/download/qq_35320456/90474580 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

周末项目推荐:为什么你应该尝试搭建一个MCP服务器? 2026/9/29 3:21:22

周末项目推荐:为什么你应该尝试搭建一个MCP服务器?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Nginx负载均衡生产级调优:调度算法、健康检查与压测验证 2026/9/29 3:21:22

Nginx负载均衡生产级调优:调度算法、健康检查与压测验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
OpenClaw 圆桌笔记:用 TaoToken 统一 Key 打通 skill 与 agent 配置 2026/9/29 3:21:22

OpenClaw 圆桌笔记:用 TaoToken 统一 Key 打通 skill 与 agent 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
EverRoom的Evidence证据模型:为什么每个AI答案都能追溯到原始来源 2026/9/29 3:21:22

EverRoom的Evidence证据模型:为什么每个AI答案都能追溯到原始来源

EverRoom的Evidence证据模型:为什么每个AI答案都能追溯到原始来源 【免费下载链接】EverRoom EverRoom - A workspace that remembers your projects, decisions, and sources. 项目地址: https://gitcode.com/gh_mirrors/ev/EverRoom EverRoom 是一个本地优…

阅读更多 →
Codex 项目实战:用 AGENTS.md 与 CLI 高效接入 TaoToken 的配置指南 2026/9/29 3:21:21

Codex 项目实战:用 AGENTS.md 与 CLI 高效接入 TaoToken 的配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
红蚂蚁目标检测数据集:YOLO/VOC/COCO三格式对齐与边缘部署 2026/9/29 3:21:15

红蚂蚁目标检测数据集:YOLO/VOC/COCO三格式对齐与边缘部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉