QT vs2017《五子棋》人机对战项目

Posted cpp_learners

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT vs2017《五子棋》人机对战项目相关的知识,希望对你有一定的参考价值。

一年了。一年前,自己学习qt语言时,顺便写了一个五子棋的小项目,个人感觉,这个五子棋项目还算挺大型的,整个项目代码量加起来可能有1w行,现在分享出来给有需要的朋友,参考借鉴。

项目包括实现功能:背景音乐、悔棋、链接MySQL进行存档和读档操作、删库跑路(也就是删除所有存档)、最小化系统托盘、游戏声音调节、人机对战算法等!


一、游戏截图

下面是游戏截图:

GIF演示:

麻雀虽小五脏俱全!


二、项目代码

这是项目文件:

项目源码代码
https://download.csdn.net/download/cpp_learner/76123482

需要自己创建一个名为gobang的数据库,并创建表gather_table_name
创建表mysql语句:create table gather_table_name(id int PRIMARY KEY AUTO_INCREMENT, table_name varchar(128) NOT NULL, save_time timestamp NOT NULL);

gather_table_name表存储的是所有存档的档名,通过这些档名就可以获取对应存档的表名,就可以读取里面的数据了。

创建好后,再修改代码中自己的数据库账户和密码即可连接成功,就可以实现存档和读档的功能了!


三、项目大致讲解

1. Qt_Gobang类

这是主窗体类!用于下棋的,判断“五子连珠”也是在这里实现的!

“五子连珠”的判断思路:
我们的棋盘每个下棋点都是有固定的坐标的,且每个点间隔50像素。
所以,当鼠标点击时,获取该点的坐标,从此位置开始向左、向右、向上、向下、向左上、向右下、向右上、向右下进行统计是否有五个相同的棋子,
有即使“五子连珠”,可以结束棋盘了!

例如:

如图2-4,从中间空着这那个点开始统计,假设我们即将要将黑色的棋子下在空这个哪里,首先向上统计,发现只有一个,加上自身也就只有两个,不够,那么先记录已经有两个相同的棋子了,然后开始向下统计,发现有三个,刚好与两个相加为5个,符合“五子连珠”于是乎结束棋盘。
如果竖着的方向统计不够,那么就开始其他方向的统计!

不知道这样讲听懂了没有呢?
下面是判断五子连珠的代码:

这里我是通过switch的方式,然后通过循环进行判断操作的!

// 触发检测“五子连珠”的槽方法
void Qt_Gobang::on_Ninth_Game() 
	RESULT result = RESULT::No;

    result = doDetectionNinth_Game();
    if (result == RESULT::Yes) 
        m_victoryFlags = true;
		this->update();
        m_startGame = false;    // 胜利后,棋盘锁上,防止玩家再次点击棋盘下棋

		// 计时全部停止,倒计时清零
		m_pBlackChessStepTime->stop();
		m_pWhiteChessStepTime->stop();
		m_pTotalTime->stop();
		emit blackChessSucceed();
		emit whiteChessSucceed();
    




// “五子连珠”执行判断标志
static int caseIndex = 1;
// 黑棋白棋“五子连珠”统计
static int ninth_gameCount = 1;
// 棋子统计辅助(ij)
static int chessCount = 1;
// 判断统计黑白棋子的标志,0统计黑棋,1统计白棋
static int black_white = 1;	

RESULT Qt_Gobang::doDetectionNinth_Game() 
    RESULT result = RESULT::Working;
    caseIndex = 1;
    ninth_gameCount = 1;
    chessCount = 1;
	black_white = 1;

	// 新下的一个棋子为黑色,则赋值0进行黑色棋子统计
	if (m_chessRecords.lastKey() % 2 == 0) 
		black_white = 0;
	


    do 
        result = detectionNinth_Game();
     while (result == RESULT::Working);

    return result;


/*
 * case 1:  左
 * case 10: 右
 * case 2:  上
 * case 20: 下
 * case 3:  左上
 * case 30: 右下
 * case 4:  右上
 * case 40: 左下
 */


RESULT Qt_Gobang::detectionNinth_Game() 
    RESULT result = RESULT::Working;

    switch (caseIndex) 
        case 1:
			if (m_pointIJ.y() - chessCount >= 0 && Recall(m_pointIJ.x(), m_pointIJ.y() - chessCount, black_white)) 
				ninth_gameCount++;	// “五子连珠”统计个数加一
				chessCount++;		// 辅助统计加一

				// “五子连珠”
				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 10;
            break;

        case 10:
			if (m_pointIJ.y() + chessCount <= 14 && Recall(m_pointIJ.x(), m_pointIJ.y() + chessCount, black_white)) 
				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 2;
			ninth_gameCount = 1;	// 每开始统计新的方向时,统计“五子连珠”个数赋值1
            break;

        case 2:
			if (m_pointIJ.x() - chessCount >= 0 && Recall(m_pointIJ.x() - chessCount, m_pointIJ.y(), black_white)) 
				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 20;
            break;

        case 20:
			if (m_pointIJ.x() + chessCount <= 14 && Recall(m_pointIJ.x() + chessCount, m_pointIJ.y(), black_white)) 
				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 3;
			ninth_gameCount = 1;
            break;

        case 3:
			if (m_pointIJ.x() - chessCount >= 0 && m_pointIJ.y() - chessCount >= 0 &&
				Recall(m_pointIJ.x() - chessCount, m_pointIJ.y() - chessCount, black_white)) 

				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 30;
            break;

        case 30:
			if (m_pointIJ.x() + chessCount <= 14 && m_pointIJ.y() + chessCount <= 14 &&
				Recall(m_pointIJ.x() + chessCount, m_pointIJ.y() + chessCount, black_white)) 

				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 4;
			ninth_gameCount = 1;
            break;

        case 4:
			if (m_pointIJ.x() - chessCount >= 0 && m_pointIJ.y() + chessCount <= 14 &&
				Recall(m_pointIJ.x() - chessCount, m_pointIJ.y() + chessCount, black_white)) 

				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			chessCount = 1;
			caseIndex = 40;
			break;

        case 40:
			if (m_pointIJ.x() + chessCount <= 14 && m_pointIJ.y() - chessCount >= 0 &&
				Recall(m_pointIJ.x() + chessCount, m_pointIJ.y() - chessCount, black_white)) 

				ninth_gameCount++;
				chessCount++;

				if (ninth_gameCount == 5) 
					result = RESULT::Yes;
				
				break;
			

			result = RESULT::No;
			break;
    
    
    return result;

2. MyThread类

这是一个线程类,作用是(循环)播放音乐的,不知为什么,使用QMediaPlayer进行播放音乐,只能单曲循环,不能循环播放。
于是就想到了使用线程,每十秒钟进行播放一次,当音乐还有在播放时,再每十秒的播放一次是没有任何反应的,当音乐停止后再进行播放就会重新开始播放音乐。

然后就到了循环播放的效果,方便发很笨,但有效…

3. GameTime类

这是一个计时类,下棋计时用的!

4. ReadData类

这是一个读档的操作类。

从数据库中读取表gather_table_name中的所有数据,显示在QTableWidget中!

这样就可以显示当前有多少存档了,选择其中一项,点击读档,就会读取对应表的数据初始化棋盘!

5. SaveData类

这是一个存档的操作类。

简而言之就是存档,数据存档名字后,单击确定就会将档名存入gather_table_name表中,创建一个与档名相同的表,将当前棋盘的数据存进入。

6. Systray类

这是一个系统托盘类。

就是最小化,会显示在系统托盘哪里,然后会提示一下!

7. Ai_Chess类

这是一个人机算法类。

里面的代码和五子连珠类似。

算法思想:
通过遍历全盘坐标,找到符合对应机制的模块,进行加分统计,得到最高分的一个坐标,就将棋子下载哪里。

机制是什么?就是那些活四、冲四、活三、眠三等…具体自己上网看一下,如下图,我写一个简单的文档用于统计分的,在项目中页包含了此文档。

根据不同的分数机制,就可以控制ai的智能程度。

另外,调节音量大小也是在这个类中实现的。


四、总结

到此就讲解完了。
其实这个项目是在一年前写的,我也一年没有碰过了,都忘记的差不多了,只能讲个大概,代码已经上传,需要的自提回去慢慢研究吧!

以上是关于QT vs2017《五子棋》人机对战项目的主要内容,如果未能解决你的问题,请参考以下文章

C#五子棋小游戏源码(人机对战)

Android 蓝牙对战五子棋项目实现(含人机对战功能)

五子棋人机对战

人机对战-黑白棋

安卓五子棋-人机对战

Java五子棋课程设计