【C++课程设计】期末大作业 - 基于Qt开发的中国象棋软件

1 题目

基于Qt的中国象棋软件开发

本博客只是简略的描述论文的大致结构、框架,以及软件开发的大致过程。有兴趣的参考原文档资料。

2总体设计与主要功能

2.1概述

中国象棋是起源于中国的一种棋,属于二人对抗性游戏的一种,在中国有着悠久的历史。由于用具简单,趣味性强,成为流行极为广泛的棋艺活动。中国象棋是中国棋文化,也是中华民族的文化瑰宝,它源远流长,趣味浓厚,基本规则简明易懂。中国象棋在中国的群众中基础远远超过围棋,是普及最广的棋类项目,中国象棋已流传到十几个国家和地区。本项目主要目的为实现中国传统双人对弈游戏—中国象棋,实现了在单机或联网状态下,无论是单人还是多人,无论使用系统是否相同,均可以实现象棋游戏功能。开发全程使用 Qt Creator 4.3.0 (Community) 的 C/C++功能,基本不使用控件拖拽,并且尽量手写代码来实现相应的函数功能,以此来熟悉面向对象编程,提高手写代码能力。通过此项目,不仅自己将取得编程技术上的进步,更是对中国传统文化的一种宣扬和推广。
最终程序运行效果如图1、图2所示:

图1 主菜单界面图 图2 游戏界面效果图
项目重难点分析:

  1. 棋子和棋盘的绘画
  2. 棋子ID的确定
  3. 每一类型棋子的规则制定
  4. 多界面之间的切换
  5. 对战计时模块的实现
  6. 悔棋模块的实现
  7. 人机对战中AI的算法
    程序总体流程如图3所示。

图3 游戏流程图

2.2模块划分

对于这个基于QT的中国象棋游戏的项目,因QT本身提供了良好的封装机制,使得它的模块化非常高,可重用性良好。
此项目分为登录界面、用户选择界面、人机对战、人人对战、关于作者五个模块。如图4所示。

图4模块层次图

2.3各模块功能介绍

2.3.1 登录界面和用户选择界面

登录界面作为程序初始界面,用户需要输入正确的用户名和密码才能进入程序下一界面,用户选择界面为用户提供了人人对战、人机对战、关于作者三个选项,用户点击一个按钮进入对应的窗口。

2.3.2人人对战

作为最主要、最核心的基类,也是最开始就单独保持运行的玩家和自己对战的游戏模块。包含有棋子类,在创建和绘画棋盘的时候,要在构造函数里里面对32颗棋子进行初始化赋值。ChessBoard类实现了棋子的创建、棋子的初始化、棋盘的创建、棋盘的初始化、游戏规则指定等任务,同时三大模块之一的人人对战也将在这个类中实现。

2.3.3人机对战

这个模块实现了用户和电脑下棋的功能,由MachineGame类实现。该类继承于ChessBoard类,在已经实现了人人对战的基础上,只需实现几个AI算法函数,这样大大减少了不必要的编程工作量。

2.3.4关于作者

此模块显示作者信息:为本人的学号及姓名。

3 详细设计与实现

3.1登录界面的设计实现

添加一个设计师界面类,选择界面模板Dialog without Buttons,而后在其ui界面添加相关控件,如图5所示。

图5 UI界面控件添加
在on_loginBtn_clicked()函数中设置默认用户名为:jjxq,密码为1234,这通过检测用户的输入与设定的数据是否相等来实现,即以下if语句:

if(ui->usrLineEdit->text().trimmed()==tr("jjxq")
            &&ui->pwdLineEdit->text().trimmed()==tr("1234"))

如果用户输入错误,则弹出提示信息:

QMessageBox::warning(this,tr("警告!"),tr("用户名或密码错误!"),QMessageBox::Yes);

3.2棋子的设计实现

棋子共有三十二个,分为红、黑两组,每组共十六个,各分七种,其名称和数目如下:
红棋子:帅一个,车、马、炮、相、仕各两个,兵五个。
黑棋子:将一个,车、马、炮、象、士各两个,卒五个。
一个棋子包括了它的名称(ID号)、状态(是否死亡)、是红棋还是黑棋、它在棋盘中的位置(几行几列)等信息。棋子由ChessPieces类实现,其包含以下私有成员变量:

enum m_emTYPE{JIANG, SHI, XIANG, MA, CHE, PAO, BING};
int  m_nRow; 
int  m_nCol;  
int  m_nID;  
bool m_bDead;  
bool m_bRed;  
m_emTYPE m_emType; 

其中,棋子的位置由结构体pos实现,包含了棋子所在行、所在列、以及棋子类型的信息:

struct POS
{
    int t_nRow;
    int t_nCol;
    ChessPieces::m_emTYPE t_emType;
};

3.3移动棋子的实现

中国象棋是由两边轮流走子,在战法上遵循古代孙子兵法中的“不战而屈人之兵,善之善者也”的作战思想,以“将死”或“困毙”对方将(帅)为胜的一种二人对抗性游戏。对局时,由执红棋的一方先走,双方轮流各走一招,直至分出胜、负、和,对局即终了。

3.4棋盘及人人对战的实现

类ChessBoard实现了棋盘的制作、棋子的绘制、各种象棋的规则函数实现、判断胜负等功能,用户开始游戏后,程序流程如图7所示。

3.4.1绘画棋盘

通过使用系统自带的API函数接口,调用画家Qpainter、画笔drawLine,以及相关的设备函数等,来绘画初步的函数图像。通过定义一个画家对象painter,设置距离边界值m_nOffSet为60,每个小方格正方形的边长m_nD为90,设置棋子半径m_nR为其一半,即45;通过一个for循环绘制10条横线,

3.4.2绘画棋子

对棋子进行初始化赋值,使得他们分别固定在对应的棋盘位置。绘画单个棋子,首先判断棋子是否死亡,若是死亡状态则不在绘制,设置画笔的颜色为黑色,画一个圆形代表一个棋子:

3.4.3象棋轮流下

设置BOOL变量值,当属于红方或者黑方下了一步棋之后,对方才能移动棋子,继续进行游戏,若是相同的颜色则不能继续进行游戏。

if(sameColor(moveId,killId))
{
    m_nSelectID=killId;
        update();
        return false;
}

3.4.4制定象棋的具体规则

要设置每一种棋子对应的走法规则。具体规则如下:
帅(将):帅(将)是棋中的首脑,是双方竭力争夺的目标。它只能在九宫之内活动,可上可下,可左可右,每次走动只能按竖线或横线走动一格。帅与将不能在同一直线上直接对面,否则走方判负。

3.4.5坐标转换

象棋的棋盘的坐标转换成界面坐标,即将象棋在棋盘中的相对位置加上棋盘的边界值:

3.4.6判断谁胜谁负

专门写一个功能函数,看看红方或者黑方的所有棋子均无路可走或者己方的将被击杀,则会判定对方胜利,自己失败,从而结束游戏。

3.4.7判断特殊情况

鸿门宴,即红方的帅和黑方的将出现在同一col上并且之间没有其他棋子阻挡,colBlack获取了黑方将的列号,colRed获取了红方帅的列号,若二者相等并且在这一列之间没有其他棋子则判断为true:

3.4.8对战计时模块

利用Qt自带控件QLCDNumber来充当计时的显示屏,QpushButton按钮来作为启动开始和结束的开关。点击开始按钮,计时器后台开始工作。然后每隔一秒钟就刷新一下显示界面。使得看起来计时器是在工作。再点击一下暂停来停止,点击一下重置就会清零计时器的显示时间为00:00,可以开始下一轮的计时。
步骤:
1.拖拉控件制作:拖拽设计界面的控件,做好布局的显示准备
2.定义槽函数:定义各个按钮需要的对应功能,设计成为槽函数
3.连接信号与槽:使用connnect()连接控件发射的信号,和实施对应的槽函数,已完成相应的功能。

3.4.9重置游戏模块

若游戏结束,reset()函数实现了计时停止和计时控制按钮不再可用,直到用户重新游戏:

m_bIsOver = true;
if(m_bIsStart)
{
m_timer->stop();
m_bIsStart = false;
}
ui->pushButton_start->setEnabled(false);

3.4.10设置窗口图标和背景

使用绘图设备QPixmap将准备好的素材的像素读取到pixChessBoard,再使用调色板类QPalette将pixChessBoard写入背景颜色:

QPixmap pixChessBoard=QPixmap(":/images/sky.jpg");
    QPalette palette(this->palette());
    palette.setBrush( QPalette::Window,QBrush(pixChessBoard));
this->setPalette(palette);

3.4.11悔棋模块

分析:对于我们来说,可能在下棋时突然后悔上一步走的棋,在现实中我们只需要记住上一步我们的棋子起始位置,以及上一步的目标位置,或者有吃子的情况,这时候

3.5人机对战的设计与实现

以上的步骤完成了棋盘类的封装、棋子类的封装以及各种类型的棋子的走棋算法的实现。有了前面的铺垫,这就可以开始着手实现人机对战的人工智能(Artificial Intelligence,以下简称AI)算法。

该算法流程如下:
(1)getAllPossibleMoveStep()函数在当前局面下获取A的所有可能走棋路径,模拟着走一下,其参数为一个记录步骤的向量。

(2)在上一步(A已经试着走了一下)的基础上获取B的所有走棋路径,并以B的视角试着走一下,有可击杀的红棋子就走击杀红棋子最优的一步,没有可击杀的红棋子就走最后的一步,然后评估局面分(因为A是电脑,所以局面分是以A的角度计算的,即A的总分-B的总分)
(3)在上一步返回的局面分的最小值中,找到最大值,并锁定与该最大值对应的走棋路径作为返回值返回。

至此,AI算法的核心部分已实现,这里使用machineChooseAndMovePieces()函数来实现AI选择棋子和移动棋子。首先,它通过调用getBestMove()函数来获取最佳路径,然后判断黑棋有没有可以击杀的红色方棋子,如果有则击杀红方棋子,否则只能走能走的路径中最后一步棋。实现代码如下:

void MachineGame::machineChooseAndMovePieces()
{
    ChessStep* step = getBestMove();
    if(step->m_nKillID == -1) 
    {
        m_ChessPieces[step->m_nMoveID].m_nRow = step->m_nRowTo;
        m_ChessPieces[step->m_nMoveID].m_nCol = step->m_nnColTo;
    }
    else  
    {
        m_ChessPieces[step->m_nKillID].m_bDead = true;
        m_ChessPieces[step->m_nMoveID].m_nRow = m_ChessPieces[step->m_nKillID].m_nRow;
        m_ChessPieces[step->m_nMoveID].m_nCol = m_ChessPieces[step->m_nKillID].m_nCol;
        m_nSelectID = -1;
    }
    m_bIsRed = !m_bIsRed;
}

3.6 程序多界面的设计与实现

3.6.1类设计

这个模块由ChooseMainWindow类来实现,这是程序运行的初始窗口,使用QPushButton定义一个指针数组m_buttons[3],初始化人人对战类指针m_pAgainstYourself,人机对战类指针m_pMachineGame。用户通过选择按钮来跳转到相应的功能模块,它继承于对话框类QDialog,私有成员为一个按钮类指针数组和三个分别指向不同功能模块的指针:
QPushButton* m_buttons[3];
ChessBoard* m_pAgainstYourself;
MachineGame* m_pMachineGame;
QWidget* m_pabout;

3.6.2配置布局

通过成员函数setWindowTitle()来设置窗口名称,使用setFixedSize()设置窗口固定大小为350*220,而后使用setWindowIcon()函数来设置窗口图标,其参数为QIcon(path);

3.6.3消息处理

使用connect()函数连接信号和槽函数,比如,用户点击人人对战按钮后,主窗口隐藏,初始化m_pAgainstYourself为ChessBoard类对象指针,使用setWindowTitle函数修改窗口标题,调用成员函数show()函数显示窗口,如果用户点击了“返回主菜单”按钮,则关闭当前窗口,通过this->show()函数显示主窗口:
connect(m_buttons[0], &QPushButton::clicked,={
this->hide();
m_pAgainstYourself = new ChessBoard();
m_pAgainstYourself->setWindowTitle(“JJ象棋-人人对战”);
m_pAgainstYourself->show();
//返回主窗口
connect(m_pAgainstYourself,&ChessBoard::toMenu,={
m_pAgainstYourself->close();
this->show();
});
});

3.6.4消息盒子实现

使用一个模态对话框弹出关于作者信息,由QMessageBox实现:

4 测试结果与分析

4.1 登录界面、主菜单界面测试

4.1.1登录界面测试

4.1.2主菜单界面

4.2 通用功能的测试

4.2.1计时模块的测试

4.2.2显示/隐藏步数测试

4.2.3悔棋模块的测试

4.2.4重新开始及返回主菜单的测试

4.3人人对战的测试

4.4人机对战的测试

4.5关于作者的测试

4.6总结

通过以上的测试,本项目计划的全部功能都已实现。但是本项目仍然存在很多不足的地方,比如AI不够智能(测试已经证明)…

参考文献

[1]最新QT入门到实战完整版|传智教育https://www.bilibili.com/video/BV1g4411H78N
[2] C++&QT中国象棋项目实战https://www.bilibili.com/%20video/BV1ap4y1k7pZ
[3] 范孙操,姚垚.象棋竞赛规则[M].北京:人民体育出版社,2014年
[4] Stanley,B, Lippman. C++ Primer[M].北京:人民邮电出版社,2006年
[5] 阿里巴巴图标矢量库https://www.iconfont.cn/
[6] 百度百科 http://baike.baidu.com
[7] 葛垚 Visual c++棋牌类游戏编程实例[M]北京:人民邮电出版社,2008年
[8] C++ QT中国象棋项目讲解 https://blog.csdn.net/wwxy1995/%20article
[9] 腾讯云社区,Pou光明Qt中国象棋https://cloud.tencent.com/%20developer/article
[10] 象棋人工智能算法的C++实现https://blog.csdn.net/csdnsevenn/%20article
[11] 象棋百科全书https://www.xqbase.com/index.htm

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhugenmi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值