基于QT和C++实现的中国象棋

一,源码

board.h

#ifndef BOARD_H
#define BOARD_H#include <QWidget>
#include "Stone.h"class Board : public QWidget
{Q_OBJECT
public:explicit Board(QWidget *parent = 0);bool _bRedTurn; // 红方先走int _currentPlayer; // 当前玩家,1为红方,-1为黑方Stone _s[32];int _r; /* 棋子的半径 */int _selectid;/* 返回象棋棋盘行列对应的像素坐标 */QPoint center(int row, int col);QPoint center(int id);bool getRowCol(QPoint pt, int& row, int& col);void drawStone(QPainter& painter, int id);void paintEvent(QPaintEvent *);void mouseReleaseEvent(QMouseEvent *);bool canMove(int moveid, int row, int col, int killid);bool canMoveJIANG(int moveid, int row, int col, int killid);bool canMoveSHI(int moveid, int row, int col, int killid);bool canMoveXIANG(int moveid, int row, int col, int killid);bool canMoveCHE(int moveid, int row, int col, int killid);bool canMoveMA(int moveid, int row, int col, int killid);bool canMovePAO(int moveid, int row, int col, int killid);bool canMoveBING(int moveid, int row, int col, int killid);bool isStoneAt(int row, int col);void saveGameState();
signals:public slots:};#endif // BOARD_H

Stone.h

#ifndef STONE_H
#define STONE_H#include <QString>class Stone
{
public:Stone();~Stone();int getRow()const{return _row;}int getCol()const{return _col;}enum TYPE{JIANG, CHE, PAO, MA, BING, SHI, XIANG};int _row;int _col;TYPE _type;int _id;bool _dead;bool _red;void init(int id){struct {int row, col;Stone::TYPE type;} pos[16] = {{0, 0, Stone::CHE},{0, 1, Stone::MA},{0, 2, Stone::XIANG},{0, 3, Stone::SHI},{0, 4, Stone::JIANG},{0, 5, Stone::SHI},{0, 6, Stone::XIANG},{0, 7, Stone::MA},{0, 8, Stone::CHE},{2, 1, Stone::PAO},{2, 7, Stone::PAO},{3, 0, Stone::BING},{3, 2, Stone::BING},{3, 4, Stone::BING},{3, 6, Stone::BING},{3, 8, Stone::BING},};_id = id;_dead = false;_red = id<16;if(id < 16){_row = pos[id].row;_col = pos[id].col;_type = pos[id].type;}else{_row = 9-pos[id-16].row;_col = 8-pos[id-16].col;_type = pos[id-16].type;}}QString getText(){switch(this->_type){case CHE:return "车";case MA:return "马";case PAO:return "炮";case BING:return "兵";case JIANG:return "将";case SHI:return "士";case XIANG:return "相";}return "错误";}
};#endif // STONE_H

board.cpp

#include "Board.h"
#include <QPainter>
#include <QMouseEvent>
#include <QMessageBox>
#include<QTextStream>
Board::Board(QWidget *parent) :QWidget(parent){for(int i=0; i<32; ++i){_s[i].init(i);}_selectid = -1;_bRedTurn = true;
}void Board::paintEvent(QPaintEvent*)
{QPainter painter(this);int d = 40;_r = d / 2;// 画10横线for (int i = 1; i <= 10; ++i){painter.drawLine(QPoint(d, i * d), QPoint(9 * d, i * d));}// 画9竖线for (int i = 1; i <= 9; ++i){if (i == 1 || i == 9)painter.drawLine(QPoint(i * d, d), QPoint(i * d, 10 * d));else{painter.drawLine(QPoint(i * d, d), QPoint(i * d, 5 * d));painter.drawLine(QPoint(i * d, 6 * d), QPoint(i * d, 10 * d));}}// 九宫格painter.drawLine(QPoint(4 * d, 1 * d), QPoint(6 * d, 3 * d));painter.drawLine(QPoint(6 * d, 1 * d), QPoint(4 * d, 3 * d));painter.drawLine(QPoint(4 * d, 8 * d), QPoint(6 * d, 10 * d));painter.drawLine(QPoint(6 * d, 8 * d), QPoint(4 * d, 10 * d));// 绘制32个棋子for (int i = 0; i < 32; ++i){drawStone(painter, i);}
}QPoint Board::center(int row, int col)
{QPoint ret;ret.rx() = (col + 1) * _r * 2;ret.ry() = (row + 1) * _r * 2;return ret;
}QPoint Board::center(int id)
{return center(_s[id]._row, _s[id]._col);
}void Board::drawStone(QPainter& painter, int id)
{if (_s[id]._dead)return;QPoint c = center(id);QRect rect = QRect(c.x() - _r, c.y() - _r, _r * 2, _r * 2);if (id == _selectid)painter.setBrush(QBrush(Qt::gray));elsepainter.setBrush(QBrush(Qt::yellow));painter.setPen(Qt::black);painter.drawEllipse(center(id), _r, _r);if (_s[id]._red)painter.setPen(Qt::red);painter.setFont(QFont("system", _r, 700));painter.drawText(rect, _s[id].getText(), QTextOption(Qt::AlignCenter));
}bool Board::getRowCol(QPoint pt, int& row, int& col)
{for (row = 0; row <= 9; row++){for (col = 0; col <= 8; col++){QPoint c = center(row, col);int dx = c.x() - pt.x();int dy = c.y() - pt.y();int dist = dx * dx + dy * dy;if (dist < _r * _r)return true;}}return false;
}bool Board::isStoneAt(int row, int col)
{for (int i = 0; i < 32; ++i){if (_s[i]._row == row && _s[i]._col == col && !_s[i]._dead){return true;}}return false;
}bool Board::canMoveXIANG(int moveid, int row, int col, int killid)
{int fromRow = _s[moveid]._row;int fromCol = _s[moveid]._col;int toRow = row;int toCol = col;if (_s[moveid]._red && toRow > 4) return false; // 红方象不能过河if (!_s[moveid]._red && toRow < 5) return false; // 黑方象不能过河int rowDir = toRow - fromRow;int colDir = toCol - fromCol;if (abs(rowDir) != 2 || abs(colDir) != 2) return false;int checkRow = fromRow + rowDir / 2;int checkCol = fromCol + colDir / 2;if (isStoneAt(checkRow, checkCol)) return false; // 路径上有棋子if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;return true;
}bool Board::canMoveCHE(int moveid, int row, int col, int killid)
{int fromRow = _s[moveid]._row;int fromCol = _s[moveid]._col;int toRow = row;int toCol = col;if (fromRow != toRow && fromCol != toCol) return false;// 计算移动的方向int rowDir = fromRow < toRow ? 1 : (fromRow > toRow ? -1 : 0);int colDir = fromCol < toCol ? 1 : (fromCol > toCol ? -1 : 0);int checkRow = fromRow + rowDir;int checkCol = fromCol + colDir;while (checkRow != toRow || checkCol != toCol){if (isStoneAt(checkRow, checkCol)) return false; // 路径上有棋子checkRow += rowDir;checkCol += colDir;}// 如果有棋子被吃,检查是否是对方的棋子if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;return true;
}
bool Board::canMoveMA(int moveid, int row, int col, int killid)
{int fromRow = _s[moveid]._row;int fromCol = _s[moveid]._col;int toRow = row;int toCol = col;int rowDiff = toRow - fromRow;int colDiff = toCol - fromCol;// 检查移动是否是“日”字形状if ((abs(rowDiff) == 2 && abs(colDiff) == 1) || (abs(rowDiff) == 1 && abs(colDiff) == 2)){// 检查是否有棋子蹩马腿int legRow = fromRow + (rowDiff > 0 ? (rowDiff / 2) : -(abs(rowDiff) / 2));int legCol = fromCol + (colDiff > 0 ? (colDiff / 2) : -(abs(colDiff) / 2));if (!isStoneAt(legRow, legCol)) // 如果没有棋子蹩马腿{// 如果有棋子被吃,检查是否是对方的棋子if (killid != -1 && _s[killid]._red != _s[moveid]._red){return true; // 可以吃掉对方的棋子}else if (killid == -1){return true;}}}return false; // 不满足移动条件
}bool Board::canMovePAO(int moveid, int row, int col, int killid)
{int fromRow = _s[moveid]._row;int fromCol = _s[moveid]._col;int toRow = row;int toCol = col;if (fromRow != toRow && fromCol != toCol) return false;int rowDir = fromRow < toRow ? 1 : (fromRow > toRow ? -1 : 0);int colDir = fromCol < toCol ? 1 : (fromCol > toCol ? -1 : 0);// 检查移动路径上是否有其他棋子int checkRow = fromRow + rowDir;int checkCol = fromCol + colDir;int jumpCount = 0;while (checkRow != toRow || checkCol != toCol){if (isStoneAt(checkRow, checkCol)) jumpCount++;checkRow += rowDir;checkCol += colDir;}if (killid != -1){if (_s[killid]._red == _s[moveid]._red) return false;if (jumpCount != 1) return false; // 必须跳过一个棋子}else{if (jumpCount != 0) return false; // 如果没有吃子,不能跳过棋子}return true;
}bool Board::canMoveBING(int moveid, int row, int col, int killid)
{int fromRow = _s[moveid]._row;int fromCol = _s[moveid]._col;int toRow = row;int toCol = col;if (fromRow != toRow && fromCol != toCol) return false;int rowDiff = toRow - fromRow;int colDiff = toCol - fromCol;// 检查是否过河if (_s[moveid]._red && toRow >= 5) // 红方兵过河{if (abs(rowDiff) != 1) return false; // 过河后只能直走一格}else if (!_s[moveid]._red && toRow <= 4) // 黑方兵过河{if (abs(rowDiff) != 1) return false; // 过河后只能直走一格}else // 未过河{if (abs(rowDiff) != 1) return false; // 未过河只能直走一格}if (killid != -1 && _s[killid]._red == _s[moveid]._red) return false;}bool Board::canMoveJIANG(int moveid, int row, int col, int killid)
{if (_s[moveid]._red){if (row > 2)return false;}else{if (row < 7)return false;}if (col < 3) return false;if (col > 5) return false;int rowDir = _s[moveid]._row - row;int colDir = _s[moveid]._col - col;int d = abs(rowDir) * 10 + abs(colDir);if (d == 1 || d == 10)return true;return false;
}bool Board::canMoveSHI(int moveid, int row, int col, int killid)
{if (_s[moveid]._red){if (row > 2)return false;}else{if (row < 7)return false;}if (col < 3) return false;if (col > 5) return false;int rowDir = _s[moveid]._row - row;int colDir = _s[moveid]._col - col;int d = abs(rowDir) * 10 + abs(colDir);if (d == 11)return true;return false;}bool Board::canMove(int moveid, int row, int col, int killid)
{// 如果移动的棋子和被吃的棋子颜色相同,则切换选择if (_s[moveid]._red == _s[killid]._red && killid != -1){_selectid = killid;update();return false;}// 判断是否是主将被吃if (_s[killid]._type == Stone::JIANG && _s[killid]._red != _s[moveid]._red){QString winner = _s[moveid]._red ? "红方" : "黑方";QMessageBox::information(this, "胜利", "恭喜," + winner + "赢了!");return false; // 停止游戏}// 根据棋子类型调用相应的移动规则函数switch (_s[moveid]._type){case Stone::JIANG:return canMoveJIANG(moveid, row, col, killid);break;case Stone::SHI:return canMoveSHI(moveid, row, col, killid);break;case Stone::XIANG:return canMoveXIANG(moveid, row, col, killid);break;case Stone::CHE:return canMoveCHE(moveid, row, col, killid);break;case Stone::MA:return canMoveMA(moveid, row, col, killid);break;case Stone::PAO:return canMovePAO(moveid, row, col, killid);break;case Stone::BING:return canMoveBING(moveid, row, col, killid);break;default:return false;}return true;
}void Board::saveGameState() {// 假设你有一个数组来存储棋盘的状态,比如棋子的类型和位置QString fileName = "game_state.txt";QFile file("game.txt");if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {QMessageBox::critical(this, "保存棋局状态", "无法打开文件进行保存!");return;}QTextStream out(&file);for (int i = 0; i < 32; ++i) {out << _s[i].getText() << "," << _s[i].getRow() << "," << _s[i].getCol() << "\n";}file.close();QMessageBox::information(this, "保存棋局状态", "棋局状态已成功保存!");
}void Board::mouseReleaseEvent(QMouseEvent *ev)
{QPoint pt = ev->pos();// 将pt转化成象棋的行列值// 判断这个行列值上面有没有棋子int row, col;bool bRet = getRowCol(pt, row, col);if(bRet == false) // 点到棋盘外return;int i;int clickid = -1;for(i=0;i<32;++i){if(_s[i]._row == row && _s[i]._col == col && _s[i]._dead== false){clickid = i;break;}}if(_selectid == -1){if(clickid != -1){if(_bRedTurn == _s[clickid]._red){_selectid = clickid;update();}}}else{if(canMove(_selectid, row, col, clickid)){/*走棋*/_s[_selectid]._row = row;_s[_selectid]._col = col;if(clickid != -1){_s[clickid]._dead = true;}_selectid = -1;_bRedTurn = !_bRedTurn;update();}else{// 不能移动棋子,给出提示QMessageBox::information(this, "提示", "该棋子不能移动到指定的位置!");// 如果点击的是另一个相同颜色的棋子,则切换选择if(clickid != -1 && _s[clickid]._red == _s[_selectid]._red){_selectid = clickid;update();}else{_selectid = -1; // 取消选择update();}}}}

main.cpp


#include <QApplication>
#include "Board.h"int main(int argc, char* argv[])
{QApplication app(argc, argv);Board board;board.show();return app.exec();
}

Stone.cpp

#include "Stone.h"Stone::Stone()
{}Stone::~Stone()
{}

二,具体分析

棋盘的绘制,棋子的绘制,以及选中棋子进行一系列操作都使用QT的库函数,所以在创建

board类时我们需要选择基于Qwidge作为父类

board.cpp是实现绘制棋盘,棋子,以及棋子运行逻辑的文件

Stone.h包含了棋子类

三,运行展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/353045.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年计算机毕业设计题目参考

今年最新计算机毕业设计题目参考 以下可以参考 springboot洗衣店订单管理系统 springboot美发门店管理系统 springboot课程答疑系统 springboot师生共评的作业管理系统 springboot平台的医疗病历交互系统 springboot购物推荐网站的设计与实现 springboot知识管理系统 springbo…

5G工业路由器在智慧交通车路协同应用的深度解析

随着科技的飞速发展&#xff0c;智慧交通已成为现代城市发展的重要方向。在智慧交通的众多技术中&#xff0c;5G工业路由器凭借其高速、稳定、安全等特性&#xff0c;成为车路协同应用中不可或缺的一环。本文将在本文中深度解析5G工业路由器在智慧交通车路协同应用中的重要作用…

Interlinks Manager评测:WordPress 的最佳内部链接插件?

在这篇Interlinks Manager评测中&#xff0c;我们将彻底查看它是否真的是 WordPress 最佳内部链接插件&#xff01;我们还将检查它是否值得您尝试。现在就开始吧 快速浏览一下插件&#xff0c;你就会对其提供的功能有一个基本的了解。 Interlinks Manager是一款 SEO 插件&…

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表 本期纯整活儿好吧&#xff01;&#xff01;&#xff01;&#xff01; 初衷 有一天我突然开始好奇虚函数表是否真的存在&#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。&#xff0c;于是有了下面的操作。 操作过程 起初我并没有思路…

一文学会消息中间件的基础知识

什么是消息队列 队列数据结构 我们都学习过数据结构与算法相关的内容,消息队列从数据结构来看,就是一个由链表或是数组构成的一个先进先出的数据容器。由链表实现还是数组实现都没关系,它只要满足数据项是先进先出的特点,那么就可以认为它是一个队列结构。队列是只允许在…

已解决:geecg Column ‘id‘ in order clause is ambiguous

报错&#xff1a;Column id in order clause is ambiguous&#xff1b; MyBatis关联查询&#xff0c;相同字段名冲突&#xff0c;sql语句已经使用别名但仍然报错。 分析&#xff1a;写mapper映射文件时&#xff0c;在写到一对一关联&#xff0c;一对多关联时&#xff0c;由于两…

MATLAB画图时添加标注显示有效数字的位数,可以编辑此函数

本来系统有个函数&#xff0c;在图像窗口选择标注工具&#xff0c;再在图像窗口右击鼠标&#xff0c;选择"编辑文本更新函数..."&#xff0c;即打开系统的设置函数&#xff0c;可以修改最后一行&#xff1a; formattedValue [valueFormat num2str(value,4) removeV…

论文阅读笔记:Towards Higher Ranks via Adversarial Weight Pruning

论文阅读笔记&#xff1a;Towards Higher Ranks via Adversarial Weight Pruning 1 背景2 创新点3 方法4 模块4.1 问题表述4.2 分析高稀疏度下的权重剪枝4.3 通过SVD进行低秩逼近4.4 保持秩的对抗优化4.5 渐进式剪枝框架 5 效果5.1 和SOTA方法对比5.2 消融实验5.3 开销分析 6 结…

4418 android4.4 + 6818 android5.1 移植gps

网上已经有的移植的资料 目前 3399 的板子上 对已有的可运行的GPS 进行测试。 我这里的 rk3399 的板卡 , Android7 Android8 都是 可以运行 GPS 应用程序的。 使用的是 ttyS4 的节点。 问题: 我发现, 开机就删掉 ttyS4 节点,也是可以 打开应用的。并且可以正常运行。不…

归并排序与计数排序

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:MySQL数据库 JavaEE专栏:JavaEE 关注博主带你了解更多数据结构知识 1.归并排序 1.递归实现归并排序 归并排序 //归并排序public static void mergeSort(int[] array) {mergeSortFun(array,0,array.length-1); r…

现在市面上哪个大大数据信用查询平台比较好用?

在当今信息化和数字化的时代&#xff0c;信用查询平台的重要性愈发突出&#xff0c;特别是在个人贷款、信用卡申请和金融服务领域。选择一个优秀的大数据信用查询平台&#xff0c;不仅可以帮助用户全面了解自己的信用状况&#xff0c;还能提供针对性的解读和建议&#xff0c;帮…

文件操作(2)(C语言版)

文件的随机读写&#xff1a; fseek函数&#xff1a; 前面讲解了顺序读写的相关函数&#xff0c;这里介绍一些可以“指哪写哪的函数” 有三个参数&#xff1a; 1、文件的地址 2、相对于第三个参数origin偏移的位置 3、起始位置&#xff08;有三种&#xff09; 第一种&#xff…

重生奇迹MU召唤术师简介

出生地&#xff1a;幻术园 性 别&#xff1a;女 擅 长&#xff1a;召唤幻兽、辅助魔法&攻击魔法 转 职&#xff1a;召唤巫师&#xff08;3转&#xff09; 介 绍&#xff1a;从古代开始流传下来的高贵的血缘&#xff0c;为了种族纯正血缘的延续及特殊使用咒术的天赋&…

基于YOLOv10深度学习的高密度人脸智能检测与统计系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

【MySQL】索引(上)

https://www.wolai.com/curry00/fzTPy3kSsMDEgEcdvo4G5w https://www.bilibili.com/video/BV1Kr4y1i7ru/?p69 https://jimhackking.github.io/%E8%BF%90%E7%BB%B4/MySQL%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/#%E7%B4%A2%E5%BC%95 索引是一种用于快速查询和检索数据的数据结构…

VMware虚拟机卡顿(虚拟机卡死)(调整所有虚拟机内存使其适应预留的主机 RAM (F)、默认进程优先级、不允许使用内存页面修整功能(M))

文章目录 设置编辑——首选项——内存——额外内存——调整所有虚拟机内存使其适应预留的主机 RAM (F)&#xff08;我把这个勾上了&#xff09;编辑——首选项——优先级——默认进程优先级虚拟机——设置——选项——高级——不允许使用内存页面修整功能(M) 参考文章&#xff…

Tuple 元组

文章目录 一、什么是元组 &#xff1f;二、元组的具体操作2.1 创建元组2.1.1 tuple() 创建元组函数和 list() 创建列表函数总结 2.2 元组的元素访问操作2.3 元组的元素计数操作2.4 zip 对象 一、什么是元组 &#xff1f; 列表属于可变序列,可以任意修改列表中的元素。 元组的…

(1)图像识别yolov5—安装教程

目录 1、安装YOLOv5: 2、下载预训练模型: 3、识别示例图片: 1、安装YOLOv5: 首先,你需要在你的计算机上下载 YOLOv5 的文件包,下载链接:https://github.com/ultralytics/yolov5。下载后对压缩文件进行解压。 (Linux命令行安装方式:git clone https://github.com/u…

Chromium源码阅读:深入理解Mojo框架的设计思想,并掌握其基本用法(1)

Mojo简介 Mojo 是一个运行时库的集合&#xff0c;提供与平台无关的通用 IPC 原语抽象、消息 IDL 格式以及具有针对多种目标语言的代码生成的绑定库&#xff0c;以便于跨任意进程间和进程内边界传递消息。 Mojo 分为清晰分离的层&#xff0c;子组件的基本层次结构如下&#xff…

全氟己酮自动灭火材料表现亮眼!手把手教你自动灭火毯的使用方法

灭火毯的使用方法是什么&#xff1f;很多朋友在购买灭火毯之前&#xff0c;都比较关心这个问题。在这里&#xff0c;我们可以把灭火毯分为两种。一种是传统灭火毯&#xff0c;还有一种是近年来兴起的高科技产品—全氟己酮自动灭火毯。这两种灭火毯的使用方法大有不同&#xff0…