项目实战:Qt5/C++:QT象棋【初版】

项目实战:Qt5/C++:QT象棋【初版】


编辑环境:win10_x64 /Qt5.4.1

项目:Qt 象棋

 [2]双人模式(单PC端)的功能,但是后面的功能是在这些基础上面,直接进行一些人工智能,电脑根据玩家的下法,通过指定的规则,来进行可能得算法,从而下一些可能是的的算法步骤。而最后的socket的双人PC端进行游戏,在现在现在基础上面加上Socket编程的类就可以了。

其他:后续还是会继续更新的,当有空的时候是会继续完善后面的功能的。

==================================================================

项目运行效果

==================================================================


==================================================================

项目思路分析:

==================================================================

步骤:

1.绘画棋盘

2.绘画棋子

3.棋盘行列值和屏幕之间的像素值之间进行切换

4.象棋轮流下

5.制定象棋的具体规则

6.屏幕重绘

==================================================================

项目主要源码部分:

==================================================================

  1. /棋盘和象棋走法类
  2. #ifndef BOARD_H
  3. #define BOARD_H
  4. #include <QWidget>
  5. #include "Stone.h"
  6. namespace Ui {
  7. class Board;
  8. }
  9. class Board : public QWidget
  10. {
  11. Q_OBJECT
  12. public:
  13. explicit Board(QWidget *parent = 0);
  14. ~Board();
  15. private:
  16. bool isDead(int id);
  17. int getStoneId(int row, int col);
  18. //车 炮 的功能辅助函数 判断两个点是否在一个直线上面,且返回直线之间的棋子个数
  19. int getStoneCountAtLine(int row1, int col1, int row2, int col2);
  20. public:
  21. //绘画棋盘
  22. virtual void paintEvent(QPaintEvent *);
  23. //象棋的棋盘的坐标转换成界面坐标
  24. QPoint center(int row, int col);
  25. QPoint center(int id);
  26. //绘画单个具体的棋子
  27. void drawStone(QPainter& painter, int id);
  28. //界面坐标转换成棋盘的行列值[获取鼠标点击的像素坐标,是位于棋盘的哪一个行列值]
  29. bool getRowCol(QPoint pt, int& row, int& col);
  30. //鼠标点击事件
  31. virtual void mousePressEvent(QMouseEvent *);
  32. //象棋移动的规则[将 士 象 马 车 炮 兵]
  33. bool canMove(int moveId, int killId, int row, int col);
  34. bool canMoveJIANG(int moveId, int killId, int row, int col);
  35. bool canMoveSHI(int moveId, int killId, int row, int col);
  36. bool canMoveXIANG(int moveId, int killId, int row, int col);
  37. bool canMoveMA(int moveId, int killId, int row, int col);
  38. bool canMoveCHE(int moveId, int killId, int row, int col);
  39. bool canMovePAO(int moveId, int killId, int row, int col);
  40. bool canMoveBING(int moveId, int killId, int row, int col);
  41. public:
  42. Stone _stone[32];
  43. int _r; //棋子半径
  44. int _offset; //距离界面的边距
  45. int _d; //间距为50px
  46. int _selectId; //IS? 选中棋子[-1:选棋子 || 非-1:走棋子]
  47. int _clickId; //点击鼠标选中棋子的ID
  48. bool _bRedTrue; //红棋先下标志
  49. private:
  50. Ui::Board *ui;
  51. };
  52. #endif // BOARD_H

  1. #include "Board.h"
  2. #include "ui_Board.h"
  3. #include <QPainter>
  4. #include <QMouseEvent>
  5. Board::Board(QWidget *parent) :
  6. QWidget(parent),
  7. ui(new Ui::Board)
  8. {
  9. //初始化32个象棋
  10. for(int i = 0; i <= 31; i++)
  11. {
  12. _stone[i].initialize(i);
  13. }
  14. _selectId = -1; //IS? 选中棋子[-1:选棋子 || 非-1:走棋子]
  15. _bRedTrue = true;
  16. ui->setupUi(this);
  17. }
  18. Board::~Board()
  19. {
  20. delete ui;
  21. }
  22. int Board::getStoneId(int row, int col)
  23. {
  24. for(int i=0; i <32; ++i)
  25. {
  26. if(_stone[i]._row == row && _stone[i]._col == col && !isDead(i))
  27. return i;
  28. }
  29. return -1;
  30. }
  31. bool Board::isDead(int id)
  32. {
  33. if(id == -1)return true;
  34. return _stone[id]._deal;
  35. }
  36. int Board::getStoneCountAtLine(int row1, int col1, int row2, int col2)
  37. {
  38. int ret = 0;
  39. if(row1 != row2 && col1 != col2)
  40. return -1;
  41. if(row1 == row2 && col1 == col2)
  42. return -1;
  43. if(row1 == row2)
  44. {
  45. int min = col1 < col2 ? col1 : col2;
  46. int max = col1 < col2 ? col2 : col1;
  47. for(int col = min+1; col<max; ++col)
  48. {
  49. if(getStoneId(row1, col) != -1) ++ret;
  50. }
  51. }
  52. else
  53. {
  54. int min = row1 < row2 ? row1 : row2;
  55. int max = row1 < row2 ? row2 : row1;
  56. for(int row = min+1; row<max; ++row)
  57. {
  58. if(getStoneId(row, col1) != -1) ++ret;
  59. }
  60. }
  61. return ret;
  62. }
  63. //绘画棋盘
  64. void Board::paintEvent(QPaintEvent *)
  65. {
  66. QPainter painter(this);
  67. _offset = 60; //距离界面的边距
  68. _d = 90; //间距为50px
  69. _r = _d/2; //棋子半径为d/2
  70. //*******************绘画棋盘*******************
  71. //绘画10条横线
  72. for(int i = 0; i <= 9; i++)
  73. {
  74. painter.drawLine(QPoint(_offset, _offset+i*_d), QPoint(_offset+8*_d, _offset+i*_d));
  75. }
  76. //绘画9条竖线
  77. for(int i = 0; i <= 8; i++)
  78. {
  79. if(i==0 || i==8)
  80. {
  81. painter.drawLine(QPoint(_offset+i*_d, _offset), QPoint(_offset+i*_d, _offset+9*_d));
  82. }
  83. else
  84. {
  85. painter.drawLine(QPoint(_offset+i*_d, _offset), QPoint(_offset+i*_d, _offset+4*_d));
  86. painter.drawLine(QPoint(_offset+i*_d, _offset+5*_d), QPoint(_offset+i*_d, _offset+9*_d));
  87. }
  88. }
  89. //绘画4条斜线
  90. painter.drawLine(QPoint(_offset+3*_d, _offset), QPoint(_offset+5*_d, _offset+2*_d));
  91. painter.drawLine(QPoint(_offset+3*_d, _offset+2*_d), QPoint(_offset+5*_d, _offset));
  92. painter.drawLine(QPoint(_offset+3*_d, _offset+7*_d), QPoint(_offset+5*_d, _offset+9*_d));
  93. painter.drawLine(QPoint(_offset+3*_d, _offset+9*_d), QPoint(_offset+5*_d, _offset+7*_d));
  94. QRect rect1(_offset+_d, _offset+4*_d, _d, _d);
  95. QRect rect2(_offset+2*_d, _offset+4*_d, _d, _d);
  96. QRect rect3(_offset+5*_d, _offset+4*_d, _d, _d);
  97. QRect rect4(_offset+6*_d, _offset+4*_d, _d, _d);
  98. painter.setFont(QFont("隶书", _r, 800));
  99. painter.drawText(rect1, "楚", QTextOption(Qt::AlignCenter));
  100. painter.drawText(rect2, "河", QTextOption(Qt::AlignCenter));
  101. painter.drawText(rect3, "汉", QTextOption(Qt::AlignCenter));
  102. painter.drawText(rect4, "界", QTextOption(Qt::AlignCenter));
  103. //*******************绘画棋子*******************
  104. for(int i = 0; i <= 31; i++)
  105. {
  106. drawStone(painter, i);
  107. }
  108. }
  109. //象棋的棋盘的坐标转换成界面坐标
  110. QPoint Board::center(int row, int col)
  111. {
  112. QPoint rePoint;
  113. //这里注意坐标的转换
  114. rePoint.rx() = col*_d+_offset;
  115. rePoint.ry() = row*_d+_offset;
  116. return rePoint;
  117. }
  118. //重载:坐标转换
  119. QPoint Board::center(int id)
  120. {
  121. return center(_stone[id]._row, _stone[id]._col);
  122. }
  123. //绘画单个具体的棋子
  124. void Board::drawStone(QPainter &painter, int id)
  125. {
  126. if(_stone[id]._deal)
  127. return;
  128. QPoint temp = center(id);
  129. QRect rect(temp.x()-_r, temp.y()-_r, _d, _d);
  130. if(_selectId == id)
  131. painter.setBrush(QBrush(QColor(64,64,196, 80)));
  132. else
  133. painter.setBrush(QBrush(QColor(64,64,196, 10)));
  134. painter.setPen(QColor(0, 0, 0));
  135. painter.drawEllipse(center(id), _r, _r); //绘画圆形
  136. painter.setFont(QFont("华文行楷", _r, 700));
  137. if(id < 16)
  138. {
  139. painter.setPen(QColor(255, 0, 0));
  140. }
  141. else
  142. {
  143. painter.setPen(QColor(0, 0, 0));
  144. }
  145. painter.drawText(rect, _stone[id].getText(), QTextOption(Qt::AlignCenter)); //绘画圆形里面的汉字
  146. }
  147. //界面坐标转换成棋盘的行列值[获取鼠标点击的像素坐标,是位于棋盘的哪一个行列值]
  148. bool Board::getRowCol(QPoint pt, int &row, int &col)
  149. {
  150. for(row = 0; row <= 9; row++)
  151. {
  152. for(col = 0; col <= 8; col++)
  153. {
  154. QPoint temp = center(row, col);
  155. int x = temp.x()-pt.x();
  156. int y = temp.y()-pt.y();
  157. if(x*x+y*y < _r*_r)
  158. return true;
  159. }
  160. }
  161. }
  162. //鼠标点击事件
  163. void Board::mousePressEvent(QMouseEvent *ev)
  164. {
  165. QPoint pt = ev->pos();
  166. //将pt转化成棋盘的像行列值
  167. //判断这个行列值上面有没有棋子
  168. int row, col;
  169. //点击棋盘外面就不做处理
  170. if(!getRowCol(pt, row, col))
  171. return;
  172. _clickId = -1;
  173. int i;
  174. //判断是哪一个棋子被选中,根据ID(这里的局部i)来记录下来
  175. for(i = 0; i <= 31; i++)
  176. {
  177. if(_stone[i]._row == row && _stone[i]._col == col && _stone[i]._deal == false)
  178. break;
  179. }
  180. if(i < 32)
  181. _clickId = i; //选中的棋子的ID
  182. if(_selectId == -1)//选中棋子
  183. {
  184. if(_clickId != -1)
  185. {
  186. if(_bRedTrue == _stone[_clickId]._red)
  187. {
  188. _selectId = _clickId;
  189. }
  190. }
  191. }
  192. else//走棋子
  193. {
  194. if(canMove(_selectId, _clickId, row, col ))
  195. {
  196. //_selectId为第一次点击选中的棋子,
  197. //_clickId为第二次点击||被杀的棋子ID,准备选中棋子下子的地方
  198. _stone[_selectId]._row = row;
  199. _stone[_selectId]._col = col;
  200. if(_clickId != -1)
  201. _stone[_clickId]._deal = true;
  202. _selectId = -1;
  203. _bRedTrue = !_bRedTrue;
  204. }
  205. }
  206. update();
  207. }
  208. //总的移动规则,选中准备下的棋子,被杀的棋子, 准备移动到的目的行列值
  209. bool Board::canMove(int moveId, int killId, int row, int col)
  210. {
  211. //1.确定是选择其它棋子还是走棋
  212. //2.是否需要使用到canMoveXXX()来做限制
  213. //3.罗列出所有情况,和需要的得到的结果值 ==> 然后进行中间的逻辑层判断※不要受到别人的代码框架的束缚※
  214. if(_stone[moveId]._red == _stone[killId]._red) //选择其它棋子,返回false
  215. {
  216. if(killId == -1) //其中有一个特殊情况,黑+_stone[-1]._red ==> 也需要判断能否
  217. {
  218. switch (_stone[moveId]._type)
  219. {
  220. case Stone::JIANG:
  221. return canMoveJIANG(moveId, killId, row, col);
  222. case Stone::SHI:
  223. return canMoveSHI(moveId, killId, row, col);
  224. case Stone::XIANG:
  225. return canMoveXIANG(moveId, killId, row, col);
  226. case Stone::MA:
  227. return canMoveMA(moveId, killId, row, col);
  228. case Stone::CHE:
  229. return canMoveCHE(moveId, killId, row, col);
  230. case Stone::PAO:
  231. return canMovePAO(moveId, killId, row, col);
  232. case Stone::BING:
  233. return canMoveBING(moveId, killId, row, col);
  234. }
  235. }
  236. _selectId = killId;
  237. update();
  238. return false;
  239. }
  240. else //选择其走棋,返回true
  241. {
  242. switch (_stone[moveId]._type)
  243. {
  244. case Stone::JIANG:
  245. return canMoveJIANG(moveId, killId, row, col);
  246. case Stone::SHI:
  247. return canMoveSHI(moveId, killId, row, col);
  248. case Stone::XIANG:
  249. return canMoveXIANG(moveId, killId, row, col);
  250. case Stone::MA:
  251. return canMoveMA(moveId, killId, row, col);
  252. case Stone::CHE:
  253. return canMoveCHE(moveId, killId, row, col);
  254. case Stone::PAO:
  255. return canMovePAO(moveId, killId, row, col);
  256. case Stone::BING:
  257. return canMoveBING(moveId, killId, row, col);
  258. }
  259. return true;
  260. }
  261. }
  262. bool Board::canMoveJIANG(int moveId, int killId, int row, int col)
  263. {
  264. if(_stone[moveId]._red) //红 将
  265. {
  266. if(row > 2 || col < 3 || col > 5) return false;
  267. }
  268. else //黑 将
  269. {
  270. if(row < 7 || col < 3 || col > 5) return false;
  271. }
  272. int dr = _stone[moveId]._row - row;
  273. int dc = _stone[moveId]._col - col;
  274. int d = abs(dr)*10 + abs(dc);
  275. if(d == 1 || d == 10)
  276. return true;
  277. return false;
  278. }
  279. bool Board::canMoveSHI(int moveId, int killId, int row, int col)
  280. {
  281. if(_stone[moveId]._red) //红 士
  282. {
  283. if(row > 2 || col < 3 || col > 5) return false;
  284. }
  285. else //黑 士
  286. {
  287. if(row < 7 || col < 3 || col > 5) return false;
  288. }
  289. int dr = _stone[moveId]._row - row;
  290. int dc = _stone[moveId]._col - col;
  291. int d = abs(dr)*10 + abs(dc);
  292. if(d == 11)
  293. return true;
  294. return false;
  295. }
  296. bool Board::canMoveXIANG(int moveId, int killId, int row, int col)
  297. {
  298. if(_stone[moveId]._red) //红
  299. {
  300. if(row > 4) return false;
  301. }
  302. else //黑
  303. {
  304. if(row < 5) return false;
  305. }
  306. int dr = _stone[moveId]._row - row;
  307. int dc = _stone[moveId]._col - col;
  308. int d = abs(dr)*10 + abs(dc);
  309. int dr2 = (_stone[moveId]._row + row)/2;
  310. int dc2 = (_stone[moveId]._col + col)/2;
  311. //象眼被堵,就不能够调,就会有i属于0~31,返回false
  312. int i = 0;
  313. for(i = 0; i <= 31; i++)
  314. {
  315. if(_stone[i]._row == dr2 && _stone[i]._col == dc2 && _stone[i]._deal == false)
  316. break;
  317. }
  318. if(0 <= i && i <= 31)
  319. return false;
  320. if(d == 22)
  321. return true;
  322. return false;
  323. }
  324. bool Board::canMoveMA(int moveId, int killId, int row, int col)
  325. {
  326. int dr = _stone[moveId]._row - row;
  327. int dc = _stone[moveId]._col - col;
  328. int d = abs(dr)*10 + abs(dc);
  329. int dr2 = (_stone[moveId]._row + row)/2;
  330. int dc2 = (_stone[moveId]._col + col)/2;
  331. //蹩脚马
  332. if(abs(dr) == 2 && abs(dc)==1)
  333. {
  334. int i = 0;
  335. if(row < _stone[moveId]._row )
  336. {
  337. for(i = 0; i <= 31; i++)
  338. {
  339. if(_stone[i]._row == (_stone[moveId]._row-1) && _stone[i]._col == _stone[moveId]._col && _stone[i]._deal == false)
  340. break;
  341. }
  342. }
  343. else
  344. {
  345. for(i = 0; i <= 31; i++)
  346. {
  347. if(_stone[i]._row == (_stone[moveId]._row+1) && _stone[i]._col == _stone[moveId]._col && _stone[i]._deal == false)
  348. break;
  349. }
  350. }
  351. if(0 <= i && i <= 31)
  352. return false;
  353. }
  354. if(abs(dr) == 1 && abs(dc)==2)
  355. {
  356. int i = 0;
  357. if(col < _stone[moveId]._col)
  358. {
  359. for(i = 0; i <= 31; i++)
  360. {
  361. if(_stone[i]._row == _stone[moveId]._row && _stone[i]._col == (_stone[moveId]._col-1) && _stone[i]._deal == false)
  362. break;
  363. }
  364. }
  365. else
  366. {
  367. for(i = 0; i <= 31; i++)
  368. {
  369. if(_stone[i]._row == _stone[moveId]._row && _stone[i]._col == (_stone[moveId]._col+1) && _stone[i]._deal == false)
  370. break;
  371. }
  372. }
  373. if(0 <= i && i <= 31)
  374. return false;
  375. }
  376. if(d == 12 || d == 21)
  377. return true;
  378. return false;
  379. }
  380. bool Board::canMoveCHE(int moveId, int killId, int row, int col)
  381. {
  382. int ret = getStoneCountAtLine(_stone[moveId]._row, _stone[moveId]._col, row, col);
  383. if(ret == 0)
  384. return true;
  385. return false;
  386. }
  387. bool Board::canMovePAO(int moveId, int killId, int row, int col)
  388. {
  389. int ret = getStoneCountAtLine(row, col, _stone[moveId]._row, _stone[moveId]._col);
  390. if(killId != -1)
  391. {
  392. if(ret == 1) return true;
  393. }
  394. else
  395. {
  396. if(ret == 0) return true;
  397. }
  398. return false;
  399. }
  400. bool Board::canMoveBING(int moveId, int killId, int row, int col)
  401. {
  402. int dr = _stone[moveId]._row - row;
  403. int dc = _stone[moveId]._col - col;
  404. int d = abs(dr)*10 + abs(dc);
  405. if(d != 1 && d != 10) return false;
  406. if(_stone[moveId]._red) //红 兵
  407. {
  408. if(row < _stone[moveId]._row) return false;
  409. if(_stone[moveId]._row == 3 || _stone[moveId]._row == 4)
  410. {
  411. if(col == _stone[moveId]._col && row == (_stone[moveId]._row+1))
  412. return true;
  413. }
  414. else
  415. {
  416. if((col == _stone[moveId]._col && row >= 5) || (row == _stone[moveId]._row && abs(col-_stone[moveId]._col)==1))
  417. return true;
  418. }
  419. return false;
  420. }
  421. else //黑 兵
  422. {
  423. if(row > _stone[moveId]._row) return false;
  424. if(_stone[moveId]._row == 5 || _stone[moveId]._row == 6)
  425. {
  426. if(col == _stone[moveId]._col && row == (_stone[moveId]._row-1))
  427. return true;
  428. }
  429. else
  430. {
  431. if((col == _stone[moveId]._col && row <= 4) || (row == _stone[moveId]._row && abs(col-_stone[moveId]._col)==1))
  432. return true;
  433. }
  434. return false;
  435. }
  436. return true;
  437. }
  1. //棋子类
  2. class Stone
  3. {
  4. public:
  5. Stone();
  6. ~Stone();
  7. public:
  8. QString getText(); //根据enum TYPE得类型决定棋子上面的汉字
  9. void initialize(int id); //32个棋子的初始化
  10. public:
  11. enum TYPE{JIANG, SHI, XIANG, MA, CHE, PAO, BING };
  12. int _row; //棋子在棋盘的行(不是界面的坐标)
  13. int _col; //棋子在棋盘的列(不是界面的坐标)
  14. int _id; //棋子的ID
  15. bool _deal; //棋子是否死亡
  16. bool _red; //棋子的颜色
  17. TYPE _type; //棋子的类型
  18. };



==================================================================

项目成品:

==================================================================

链接:https://pan.baidu.com/s/1Usuk6nEy4OxhMfEnERM6Wg 密码:0o4l

你可能感兴趣的:(QT)