• 5233阅读
  • 7回复

《Qt实战小项目》3,中国象棋 [复制链接]

上一主题 下一主题
离线逆风微光
 

图酷模式  只看楼主 倒序阅读 楼主  发表于: 2018-05-20
这是我的CSDN博客专栏《Qt实战小项目》的系列文章第3篇。
专栏地址:https://blog.csdn.net/dpsying/category_9267660.html


目前专栏内有这些文章:
QT项目一:俄罗斯方块游戏
QT项目二:五子棋游戏
QT项目三:中国象棋
QT项目四:扫雷游戏
QT项目五--计算器
QT项目六:简易客户信息管理系统

1,简介

QT实现中国象棋单机版,本来以为会花费不少时间的。由于之前2个游戏项目的练手,这个主要只在走棋算法部分花了些时间研究,最后一共2个晚上完成。
由于只是QT学习,没有考虑更多复杂功能,比如人机对战等,那个可能算法就得花很长时间。

2,效果





3,设计思路

准备2个棋盘棋子图片:


棋子类:


[cpp] view plain copy


  1. #pragma once  
  2. #include "qpainter.h"  
  3.   
  4.   
  5. enum ITEM_TYPE{  
  6.     ITEM_SHUAI = 0, //帅  
  7.     ITEM_SHI,       //士  
  8.     ITEM_XIANG,     //象  
  9.     ITEM_JU,            //车  
  10.     ITEM_MA,            //马  
  11.     ITEM_PAO,       //炮  
  12.     ITEM_BING,      //兵  
  13.     ITEM_MAX,  
  14. };  
  15.   
  16. enum ITEM_COLOR{  
  17.     COLOR_RED = 0,  //红方  
  18.     COLOR_BLACK,        //黑方  
  19.     COLOR_MAX,  
  20. };  
  21.   
  22. class Item  
  23. {  
  24. public:  
  25.     Item(){}  
  26.     Item(ITEM_TYPE t,ITEM_COLOR c,QPoint pt);    
  27.     ~Item(void);  
  28.   
  29. public:  
  30.     ITEM_TYPE m_type;       //棋子类型  
  31.     ITEM_COLOR m_color; //红方黑方  
  32.   
  33.     QPoint m_pt;            //位置  
  34.     bool m_bShow;           //是否显示,实现选中时闪烁效果  
  35. };  


可以看到,棋子类型的定义和图片中棋子顺序一致,是为了直接以此枚举值计算棋子在图片中的位置。


MainWindow.h:


[cpp] view plain copy


  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.   
  4. #include <QMainWindow>  
  5. #include "Item.h"  
  6. #include "qmap.h"  
  7.   
  8. namespace Ui {  
  9. class MainWindow;  
  10. }  
  11.   
  12. class MainWindow : public QMainWindow  
  13. {  
  14.     Q_OBJECT  
  15.   
  16. public:  
  17.     explicit MainWindow(QWidget *parent = 0);  
  18.     ~MainWindow();  
  19.   
  20.     void InitItems();           <span style="white-space:pre;">                 </span>//初始化所有棋子位置  
  21.     void NewGame();         <span style="white-space:pre;">                     </span>//新游戏  
  22.   
  23.     bool FindItemAtPoint(QPoint pt,Item& item);                 //给定一个棋盘上的点,查找是否存在棋子  
  24.     bool DeleteItemAtPoint(QPoint pt,bool& bDeleteSHUAI);       <span style="white-space:pre;">     </span>//bDeleteSHUAI:是否打掉的是“帅”或“将”  
  25.     void SetItemShow(Item item,bool bShow);                     //设置棋子显隐  
  26.     bool MoveItem(Item item,QPoint ptMoveTo);                   //走棋逻辑  
  27.     void ChangeItemPoint(QPoint ptItem,QPoint pt);              <span style="white-space:pre;"> </span>//改变棋子位置属性  
  28.   
  29.     void DrawItem(QPainter& painter,Item item);                 //绘制棋子  
  30.   
  31.     //获取棋子能移动的区域  
  32.     void GetMoveArea(Item item,QVector<QPoint>& area);  
  33.     void GetMoveArea_JU(Item item,QVector<QPoint>& area);  
  34.     void GetMoveArea_MA(Item item,QVector<QPoint>& area);  
  35.     void GetMoveArea_XIANG(Item item,QVector<QPoint>& area);  
  36.     void GetMoveArea_SHI(Item item,QVector<QPoint>& area);  
  37.     void GetMoveArea_SHUAI(Item item,QVector<QPoint>& area);  
  38.     void GetMoveArea_PAO(Item item,QVector<QPoint>& area);  
  39.     void GetMoveArea_BING(Item item,QVector<QPoint>& area);  
  40. protected:  
  41.     void paintEvent(QPaintEvent *);  
  42.     void mousePressEvent(QMouseEvent *);  
  43.     void timerEvent(QTimerEvent *);  
  44.   
  45. private:  
  46.     Ui::MainWindow *ui;  
  47.   
  48.     int m_nItemWidth;  
  49.     int m_nItemHeight;  
  50.     QPixmap m_ItemsImage;       //棋子图片  
  51.   
  52.     QVector<Item> m_items;    <span style="white-space:pre;"> </span>//所有棋子  
  53.     Item m_SelectedItem;  
  54.     bool m_bExistSelectedItem;  //是否已存在选中的棋子  
  55.     bool m_bIsRedTurn;      //当前该红方下  
  56. };  
  57.   
  58. #endif // MAINWINDOW_H  


其中主要逻辑都在mousePressEvent 鼠标响应:


[cpp] view plain copy


  1. //鼠标点击,走棋、吃棋等逻辑  
  2. void MainWindow::mousePressEvent(QMouseEvent * e)  
  3. {  
  4.     //获得鼠标点所对应的棋盘点pt  
  5.     QPoint pt;  
  6.     pt.setX( (e->pos().x() - START_X ) / RECT_WIDTH);  
  7.     pt.setY( (e->pos().y() - START_Y ) / RECT_HEIGHT);  
  8.   
  9.     //是否有选中的棋子  
  10.     if(m_bExistSelectedItem)  
  11.     {  
  12.         //已存在棋子,判断鼠标点击的是否是选中棋子  
  13.         if (pt == m_SelectedItem.m_pt)  
  14.         {  
  15.             //再次点击已经选择的棋子,什么也不做  
  16.             return;  
  17.         }  
  18.         //点击其它棋子  
  19.         Item ClickedItem;  
  20.         if (FindItemAtPoint(pt,ClickedItem))  
  21.         {  
  22.             //点击的同色的另外一个棋子,改选  
  23.             if ( (m_bIsRedTurn && ClickedItem.m_color == COLOR_RED) ||  
  24.                  (!m_bIsRedTurn && ClickedItem.m_color != COLOR_RED))  
  25.             {  
  26.                 SetItemShow(m_SelectedItem,true);  
  27.                 m_SelectedItem = ClickedItem;  
  28.                 return;  
  29.             }  
  30.         }  
  31.         //点击的异色棋子,判断是否能走能吃  
  32.         QVector<QPoint> moveArea;  
  33.         GetMoveArea(m_SelectedItem,moveArea);       //获取已选择棋子的可移动区域  
  34.         if (moveArea.contains(pt))                    
  35.         {  
  36.             //包含当前鼠标点中的棋子,则能吃  
  37.             bool bDeleteSHUAI = false;  
  38.             DeleteItemAtPoint(pt,bDeleteSHUAI);  
  39.             ChangeItemPoint(m_SelectedItem.m_pt,pt);  
  40.             if (bDeleteSHUAI)  
  41.             {  
  42.                 QString str = m_bIsRedTurn?QStringLiteral("红方胜利!"):QStringLiteral("黑方胜利!");  
  43.                 QMessageBox::information(NULL,  "GAME OVER  ",str, QMessageBox::Yes , QMessageBox::Yes);  
  44.                 NewGame();  
  45.                 return ;  
  46.             }  
  47.             m_bExistSelectedItem = false;  
  48.             m_bIsRedTurn = !m_bIsRedTurn;  
  49.             update();  
  50.             return ;  
  51.         }  
  52.         else  
  53.         {  
  54.             //不能走到该位置  
  55.             return ;  
  56.         }  
  57.     }  
  58.     else  
  59.     {  
  60.         //当前没有选中棋子  
  61.         Item ClickedItem;  
  62.         if (FindItemAtPoint(pt,ClickedItem))  
  63.         {  
  64.             //如果点中一个棋子,是当前走棋方的颜色,就选中了  
  65.             if ( (m_bIsRedTurn && ClickedItem.m_color == COLOR_RED) ||  
  66.                 (!m_bIsRedTurn && ClickedItem.m_color == COLOR_BLACK))  
  67.             {  
  68.                 m_SelectedItem = ClickedItem;  
  69.                 m_bExistSelectedItem = true;  
  70.                 return;  
  71.             }  
  72.         }  
  73.     }  
  74. }  


里面最重要的就是这个GetMoveArea 函数,用来获取每个棋子的可走动的区域:


[cpp] view plain copy


  1. void MainWindow::GetMoveArea(Item item,QVector<QPoint>& moveArea)  
  2. {  
  3.     switch (item.m_type)  
  4.     {  
  5.     case ITEM_JU:  
  6.         {  
  7.             GetMoveArea_JU(item,moveArea);  
  8.             break;  
  9.         }  
  10.     case ITEM_MA:  
  11.         {  
  12.             GetMoveArea_MA(item,moveArea);  
  13.             break;  
  14.         }  
  15.     case ITEM_XIANG:  
  16.         {  
  17.             GetMoveArea_XIANG(item,moveArea);  
  18.             break;  
  19.         }  
  20.     case ITEM_SHI:  
  21.         {  
  22.             GetMoveArea_SHI(item,moveArea);  
  23.             break;  
  24.         }  
  25.     case ITEM_SHUAI:  
  26.         {  
  27.             GetMoveArea_SHUAI(item,moveArea);  
  28.             break;  
  29.         }  
  30.     case ITEM_PAO:  
  31.         {  
  32.             GetMoveArea_PAO(item,moveArea);  
  33.             break;  
  34.         }  
  35.     case ITEM_BING:  
  36.         {  
  37.             GetMoveArea_BING(item,moveArea);  
  38.             break;  
  39.         }  
  40.     }  
  41. }  




比如对于棋子“马”,求可以走到的位置:

[cpp] view plain copy


  1. void MainWindow::GetMoveArea_MA( Item item,QVector<QPoint>& moveArea )  
  2. {  
  3.     //棋子“马”的计算可移动区域算法简介:  
  4.     //1,求出8个待选位置,8个位置的偏移是(-2,-1)(-2,1)(2,-1)(2,1)(1,-2)(1,2)(-1,-2)(-1,2)存在关系:|x|+|y|=3  
  5.     //2,判断待选位置是否在棋盘内  
  6.     //3,判断中间是否有卡位的棋子  
  7.     //4,位置上是否已存在同色棋子  
  8.     Item item2;  
  9.     for (int i = -2; i<=2; i++)  
  10.     {  
  11.         for(int j = -2; j<=2; j++)  
  12.         {  
  13.             if (qAbs(i) + qAbs(j) == 3)  
  14.             {  
  15.                 QPoint ptNew = item.m_pt + QPoint(i,j);  
  16.                 if (ptNew.x() >= 0 && ptNew.x() <= 8 && ptNew.y()>=0 && ptNew.y() <= 9)  
  17.                 {  
  18.   
  19.                 }  
  20.                 else  
  21.                 {  
  22.                     continue;  
  23.                 }  
  24.   
  25.                 //求该方向行走路线的 卡位元素位置  
  26.                 QPoint ptDirect(0,0);    
  27.                 if (qAbs(i) > qAbs(j))  
  28.                 {  
  29.                     if (i>0)  
  30.                     {  
  31.                         ptDirect = QPoint(1,0);  
  32.                     }  
  33.                     else  
  34.                     {  
  35.                         ptDirect = QPoint(-1,0);  
  36.                     }  
  37.                 }  
  38.                 else  
  39.                 {  
  40.                     if (j>0)  
  41.                     {  
  42.                         ptDirect = QPoint(0,1);  
  43.                     }  
  44.                     else  
  45.                     {  
  46.                         ptDirect = QPoint(0,-1);  
  47.                     }  
  48.                 }  
  49.                 QPoint ptHit = item.m_pt + ptDirect;    //马的卡位元素位置  
  50.                 if (FindItemAtPoint(ptHit,item2))  
  51.                 {  
  52.                     //卡位  
  53.                     continue;  
  54.                 }  
  55.                 if (FindItemAtPoint(ptNew ,item2) && item.m_color == item2.m_color)  
  56.                 {  
  57.                     //有本组item  
  58.                     continue;  
  59.                 }  
  60.                 moveArea.append(ptNew);  
  61.             }  
  62.         }  
  63.     }  
  64. }  


其它棋子类似,就是仔细处理下就OK了。




4,源码


源码可在学习群免费下载
群号码:1149411109
群名称:Qt实战派学习群







离线liuchangyin

只看该作者 1楼 发表于: 2018-05-21
离线逆风微光

只看该作者 2楼 发表于: 2018-05-25
回 liuchangyin 的帖子
liuchangyin:[表情]  (2018-05-21 09:41) 

谢谢支持!
离线往事纯白

只看该作者 3楼 发表于: 2018-06-04
厉害了
离线逆风微光

只看该作者 4楼 发表于: 2018-06-09
回 往事纯白 的帖子
往事纯白:厉害了[表情]  (2018-06-04 17:25) 

谢谢!
离线wbcg00

只看该作者 5楼 发表于: 2018-06-09
谢谢 这么好的练习题目
交作业了

下面是我的代码, ,大部分是抄楼主的 走棋一部分是自己写的, 不过没有复制,纯手敲的,敲了一遍, 理解了一部分
myChineseCheese.rar (201 K) 下载次数:11

离线逆风微光

只看该作者 6楼 发表于: 2018-06-09
回 wbcg00 的帖子
wbcg00:谢谢 这么好的练习题目
交作业了
[图片]
下面是我的代码,[表情] [表情] ,大部分是抄楼主的 走棋一部分是自己写的, 不过没有复制,纯手敲的,敲了一遍, 理解了一部分 [表情] [表情] [表情] [表情]
[图片]
....... (2018-06-09 20:00) 

厉害厉害呀!
离线wbcg00

只看该作者 7楼 发表于: 2018-06-09
  来qtcn 向大家学习!
快速回复
限100 字节
 
上一个 下一个