QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)

gdut大一下学期c++课设(得分:95)

实验报告及源码压缩包百度云下载:
链接:https://pan.baidu.com/s/1zO5ofMz09fiWihxCcZcFbg
提取码:ddav

首先需要下载mysql(我使用的是5.7)和QT creator 4.4.1
这里mysql记得配路径,在环境里配path路径。

然后打开项目。将里面的mysql代码全部改成自己的数据库名、密码、表名。即可运行。

本文目录:

五子棋程序介绍
打开运行项目教程
附上所有程序的代码

五子棋课设

摘 要

通过一学期的c++语言学习,已经掌握了数据类型、数组、指针,类等c++语言基础。根据课程实践的要求,通过已有的知识储备以及在课程过后个人的学习,完成了课程要求中的五子棋游戏的制作。由于程序使用了mysql数据库,所以程序无法实现打包发出,让别人测试。不过,若他人电脑中已配置了mysql数据库及环境,只需要修改数据库账号,密码及表名即可使用。
关键词:五子棋、QT、mysql

一、总体设计

程序框架

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第1张图片

1.1界面设计

本程序有三个界面,分别为登陆界面,注册界面,游戏界面。在界面上,一切设计都以为用户提供更好的使用体验为原则而设计。本游戏界面均用QT设计,通过定义控件及槽函数组成。

1.2功能设计

本程序可以实现基本的五子棋的双人对战,人机对战,计时,撤回,投降,认输,暂停,换背景等功能,还有数据库支持的登陆,注册,玩家信息系统等功能

1.3代码实现

在代码的实现上,基础界面的代码为QT中的自己手写槽函数、控件和类,手动摆放控件位置来实现的,并为使用ui界面。本五子棋的代码核心是窗口类的切换及游戏框架类的应用,以此来实现较多的基本功能。

1.4小结

该五子棋程序的功能基本实现,通过精美简洁的界面设计和使用方式,给用户提供更好的使用体验。可惜的是本程序基于自己的mysql数据库实现,没法打包,但是若其他人想要使用这个代码,可以更改其中的数据库账户密码,或者创造个.db的静态数据库来储存。即可使用本程序的代码。

二、详细设计

2.1界面设计详细讲解

1)登陆界面

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第2张图片
登陆界面为一个c++类,属于widget类。背景由两张图片组成。可在此界面中输入账号密码,账号密码将会在数据库中搜索,若与数据库中的数据一致,即可登陆到游戏界面中。

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第3张图片

2)注册界面

注册界面为一个c++类,属于widget类。可以输入账号及注册的密码。

注册时会搜素mysql数据库的已存在账号。
当注册账号已存在时:

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第4张图片

同时,注册账号密码不可为空

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第5张图片

3)游戏界面

注册界面由两个类组成,属于mainwindow类。一个为界面绘画类,一个为下棋类(包括人工AI下棋)
由于程序总是出bug,让我在显示数据库数据时出了问题,导致个人信息界面和排行榜界面没能显示出来,试过很多种方法还是不行,只能暂时放弃这两个功能。

2.2 部分功能详解

1) mysql数据库

首先是连接数据库,这个在很多个.cpp里都用到了。
代码如下:

  qDebug()<<QSqlDatabase::drivers();

    //添加MYSQL数据库
    QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
    //连接数据库
    db.setHostName("127.0.0.1"); //数据库服务器IP
    db.setUserName("root"); //数据库用户名
    db.setPassword("holdworld000312"); //数据库密码
    db.setDatabaseName("slimes"); //使用哪个数据库

    if(!db.open()){
        QMessageBox::information(this, "连接结果", "连接数据库失败!");
        return;
}

下面是我的mysql中的数据库。(这里数据库可视化我用SQLyog)
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第6张图片
数据库中有两个表。
一个是wuziqi_info
用来存取注册后的个人信息。
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第7张图片
其中包括id,账号,密码,赢的局数,输的局数,姓名,性别。
另一个是我用来存储登陆的账户,以便于反馈给各各界面是哪个用户在玩游戏。
在这里插入图片描述

中间涉及到的数据库操作有:
【1】 查看登陆账号密码是否正确

	QSqlTableModel model;
   	 model.setTable("wuziqi_info");
    	model.setFilter(tr("handle = '%1' and passwd = '%2'").arg(handle).arg(passwd));
	model.select();

【2】注册时存入新用户的信息

QString str=QString("insert into wuziqi_info(handle, passwd,  win, lose, name, sex) values('%1','%2', 0 , 0 ,'%3','%4')").arg(handle).arg(passwd1).arg(name).arg(sex);
        QSqlQuery query;
        query.exec(str);

【3】在游戏输赢时获取用户的输赢局数,然后决定哪个+1,再修改表中的数据

	QString  win;
       //这里就举例一个
	QString sq=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);

	QSqlQuery sqlquery=QSqlQuery(db);
	sqlquery.exec(sq);

	while (sqlquery.next()){
    win.append(sqlquery.value(3).toString());
}
//这里就举例一个
	QSqlQuery query;
       query.prepare("update wuziqi_info set win=? where handle=?");
       query.addBindValue(newwin);
       query.addBindValue(NAME);
       query.exec();
2) 背景设置

用画笔Qpainter 在paintEvent(QPaintEvent *event),获取图片名称(图片存储于与此代码统一位置中,即可实现背景画图)

	//QPainter p(this);
    	QPainter p;  //创建画家对象
   	p.begin(this); //指定当前窗口为绘图设备

    	//绘图操作						       												
   	//画背景图
   	if(color == 0)
    	p.drawPixmap(0,0,width(),height(), 	QPixmap("../Image/u=3967820435,4109070133&fm=26&gp=0.jpg"));

3) 五子棋棋盘棋子绘画

用到paintEvent(QPaintEvent *event)这个函数,可以实时更新页面。

用painter.drawLine画棋盘的格子。

画棋子(颜色,粉刷):

	brush.setColor(Qt::white);
        painter.setBrush(brush);
        painter.drawEllipse(kBoardMargin + kBlockSize * j - kRadius, kBoardMargin + kBlockSize * i - kRadius, kRadius * 2, kRadius * 2);

用mouseMoveEvent(QMouseEvent *event)通过鼠标的hover确定落子的标记
还有用一个update()函数,每次都重绘,即时时更新。
程序中用了一个数组game->gameMapVec[clickPosRow][clickPosCol]为0的时
候没有棋子,为1的时候有棋子。

4)人机代码的编写

人机代码我放在一个GameModel类中,有个初始话评分系统,从各种角度对每个格子的分数进行加分。这里我用了暴力搜索每个点的四个方向哈哈,其实如果要降低复杂度可以用dfs来优化。然后就两个量personNum(botNum),emptyNum用来记录自己连子的个数,和对方连子的个数。最后分别给黑子进行统一打分。分数高的位置优先考虑下子。
在这里插入图片描述

(在这里选择人机或非人机操作)
目前还有缺陷,就是计分时,没有考虑到优先取胜策略,只是在防守,不过,给宿友们测试了一下,发现人机还是很厉害的,成功将宿友的耐心消磨哈哈。当然可以通过修改每个落点的得分,来安排人机的攻守风格。
部分代码:

for (int i = 1; i <= 4; i++){
      if (row + i * y > 0 && row + i * y < kBoardSizeNum &&col + i * x > 0 && col + i * x < kBoardSizeNum &&gameMapVec[row + i * y][col + i * x] == 1) // 玩家的子{
     personNum++;
                                }
         else if (row + i * y > 0 && row + i * y < kBoardSizeNum &&col + i * x > 0 && col + i * x < kBoardSizeNum &&gameMapVec[row + i * y][col + i * x] == 0) // 空白位{
         emptyNum++;
         break;
}
 else// 出边界
break;


 if (personNum == 1)                      // 杀二
  scoreMapVec[row][col] += 10;
  else if (personNum == 2)                 // 杀三
{
  if (emptyNum == 1)
  scoreMapVec[row][col] += 30;
  else if (emptyNum == 2)
  scoreMapVec[row][col] += 40;
}
  else if (personNum == 3)                 // 杀四
{// 量变空位不一样,优先级不一样
  if (emptyNum == 1)
  scoreMapVec[row][col] += 60;
  else if (emptyNum == 2)
  scoreMapVec[row][col] += 110;
 }
  else if (personNum == 4)                 // 杀五
scoreMapVec[row][col] += 10100;
}

由于棋盘不大,就225个格子,每个格子八个方向扫一遍,22585=9000
所以每次操作最复杂的时间复杂度才O(1000)只需要0.00001秒,这里就不节省时间复杂度啦。

5)计时器

写个状态栏,将计时器放于状态栏中。用到time_update()函数,可以时时更新时间

    currentTimeLabel = new QLabel; // 创建QLabel控件
    QStatusBar *sBar = statusBar();
    sBar->addWidget(new QLabel("time:",this));
    sBar->addWidget(currentTimeLabel);
    timer = new QTimer(this);
    time = new QTime(0,0,0);
    timer->start(1000); //每隔1000ms发送timeout的信号
connect(timer, SIGNAL(timeout()),this,SLOT(time_update()));

void MainWindow::time_update()
{
    //time->addSecs(1);
    *time=time->addSecs(1);
    //QDateTime current_time =  new QDateTime(0,0,0);
    QString timestr = time->toString("hh:mm:ss"); //设置显示的格式
   currentTimeLabel->setText(timestr); //设置label的文本内容为时间
}

在这里插入图片描述
(本计时器可以在游戏结束或认输时重计,在暂停时停止)

6)Mainwindow的菜单栏的创建

举例:

    // 添加游戏菜单
    QMenu *gameMenu = menuBar()->addMenu(tr("Game")); // menuBar默认是存在的,直接加菜单就可以了
    QAction *actionPVP = new QAction("Person VS Person", this);
    connect(actionPVP, SIGNAL(triggered()), this,SLOT(initPVPGame()));
    gameMenu->addAction(actionPVP);

7)设置(setting)

Information 和rank 都还没完成,这里先不介绍。

【1】换背景(Background):

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第8张图片
四种背景可供选择。
Deepwood:
在这里插入图片描述
Lightwood:
在这里插入图片描述
Graywood:
在这里插入图片描述
Orangewood:

在这里插入图片描述

【2】退出登陆(Exit landing):

这是退出登陆的按钮,可以通过这个回到登陆界面,换另一个账号继续玩本游戏。

8) 操作(operation)

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第9张图片

【1】投降(give up)

投降后自己的输的局数在数据库中的记录会+1,同时计时器重新计时。(自己为白方)
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第10张图片

【2】暂停(pause)

暂停后,玩家将无法下棋,计时器也会随之暂停。
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第11张图片

总结:

很多功能都是慢慢想到的,然后再加上去,

【3】悔棋(return)

这里我用了个二维数组来存储每次放棋后的操作,若点击撤回,则now的位置变为0,然后now-1,到上一个状态。
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第12张图片 在这里插入图片描述 在这里插入图片描述QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第13张图片

【4】退出登陆(exit)

退出游戏。

2.3 总结:

这次的代码大部分是边学边完成的,所以框架较凌乱,有一些功能无法实现。但基础功能还是实现啦,由于一开始写程序框架的时候没有使用ui去做,而是自己手写控件和槽函数,所以界面比较简陋。但是基本功能都差不多实现啦。

三、实验过程:

3.1 配环境

本来想着用visual studio 2017连接mysql数据库来写程序的。但是我配了一个多星期的环境加上静态库,还是会报出一堆错误,所以果断放弃visual studio 2017 ,改用QT5.7+mysql,完成程序的编写。

本人先写了游戏框架及规则,在编写完mainwindow界面后才逐渐添加其他界面。在游戏编写的过程中,也是遇到了许许多多的问题,比如可以在网格外放棋子等。在观看众多博客后,才慢慢的将bug修改完。

之后,我才开始编写界面类,先写了登陆界面,这时候,我开始尝试数据库的连接,在我的电脑的path路径中加入mysql.dll静态数据库的路径后,总算成功连接上了数据库。之后便学了一系列的数据添加,读取,搜索。才成功实现了登陆界面及注册界面的编写。但这时候我面临了一个问题。

3.2 编写代码

因为,其实从一开始,界面就全部存在,所以,每个界面拥有的都是在那一时刻的数据,无法更新到最新,就比如我想要的个人信息查询,排行榜。。。(这两个只是显示上除了问题,获取最新的数据我已经解决了),而且每个界面都是,独立的,无法知道是哪个用户在进行游戏。当时的第一做法便是,运用信号和槽。通过登陆界面,放送信号给其他窗口。但是却在主窗口传信息给子窗口时出现了问题。而且窗口众多,导致代码越写越乱。所以我便创造了个数据库的表,用来记录,此时在线的玩家的信息。最后就是每个窗口获得最新的信息的问题了。这里,因为要让窗口显示,肯定要先按一个按钮,所以,我在按钮里增加信息的更新。这样子,这个问题就被解决啦。编写这个代码中间还遇到了大大小小的问题,有些解决了,有些仍无法解决。

3.3无法解决的问题:

1)个人信息无法显示:

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第14张图片
在这里插入图片描述
如图,我已经将数据库中的用户信息获取,并用qDebug输出,但是我尝试了好几种方法,还是不能将其显示在窗口上。现在只能根据男女,给用户安排了个头像哈哈。

2)排行榜无法显示问题:

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第15张图片
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第16张图片
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第17张图片
因为排行榜很多数据,所以我想要将数据库的数据直接排序,然后再可视化。但是这时候又出现问题了,也许因为不是主窗口,所以,我可视化的表格里,都是空白的。但是,当我重新创个项目,去尝试这个代码时,又可以显示。

3.4 总结:

编写代码中遇到好多问题,大部分得以解决,可是还有几个至今都还无法解决,不过,等有一定的时间后还是应该将其解决,加上以后,我希望可以给该游戏连上区域网,实现玩家的对战。

四、关于未来:

虽然这只是一个课设,但是我的完成程度还让我很不满意,还有很多的功能无法实现,还有实现玩家联网对抗等等,慢慢的我会在进行改进,希望可以不断完善,最后将其变成一个app应用。所以在即将到来的暑假中,我会花一定的时间去考虑如何实现以上功能,从而更好的完善该程序的功能。当然我还想加入围棋等各种棋类,慢慢的去更多了解界面设计的其他知识点。让自己学到的知识更多。

五、总结

之前社团考核用过c语言去写了一个部员管理系统,还有写过成绩管理系统,不过,当时的代码,还是只能在cmd窗口上运行,文件只能用txt保存,用链表来存数据,但是这次课设,成功脱离了小黑框,有了比较美观的界面,有很大的成就感。当然,主要还是得满足对客户的需求,所以我请了挺多同学来测试我的程序的,反响还行,人机也挺给力的哈哈。不过还是有很多不妥的地方需要我慢慢的改进,慢慢的去满足用户的需求。
通过这次课设的学习,我还是学到了好多,比如类,基类,派生类,继承,信号与槽等。对于界面设计,虽然不是主方向,但是,以后一定很多用得上的时候,所以这次的课设让我收获很大很大。

六、参考文献

【1】哔哩哔哩视频:妙韵含的QT开发全套视频

七、致谢

在这里,我要感谢**老师给予我的指导和帮助,没有老师的指明方向,我现在肯定还是一头雾水的。这学期的c++程序设计课也接近尾声了,感谢老师传授我们的知识,还有对我的关注。

打开教程:

文件目录:

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第18张图片
红色的是初始文件,蓝色的是类文件,这里要创建5个类文件。记住
GameModel和mainwindow用的是MainWindow类型,其他的均是Widget类型。在创建的时候记得选择,且不要ui页面。

文件名要一致,不然代码无法运行。

首先创建个不含ui的qt文件,具体操作可查看
https://blog.csdn.net/qq_43333395/article/details/90814951
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第19张图片
文件目录要不含中文
QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第20张图片
这里不打勾,本程序不使用ui文件

接下来就是创建类

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第21张图片

QT五子棋游戏课设及源码(连接mysql数据库含打开并运行程序的教程)_第22张图片
如此创建五个类,复制代码到其中即可。

至于mysql操作:
看博客:https://blog.csdn.net/qq_43333395/article/details/91126477
记得将mysql的数据库名和密码改为自己的。(可先创建个QT文件看看数据库是否连接成功)

注意:

上面百度云网盘中含有一个Image文件夹,里面是图片文件,记得拷贝后放在在这里插入图片描述
和项目文件统一目录下。

如果第一次运行报错,请先删除
在这里插入图片描述
该自动生成的文件后,再重新编译运行。

如果有问题请大家多多指出啦( ̄▽ ̄)",第一次用QT写项目,有不好的地方请大家见谅。

附上所有程序的代码:

1.WUZIQI.pro
2.GameModel.h
3.informationwindow.h
4.mainwindow.h
5.rankwindow.h
6.registerwindow.h
7.subwindow.h
8.GameModel.cpp
9.informationwindow.cpp
10.main.cpp
11.mainwindow.cpp
12.rankwindow.cpp
13.registerwindow.cpp
14.subwindow.cpp

1.WUZIQI.pro
这个文件代码,不用复制,只需再QT += core gui 后面加上 sql 其他的在添加类的时候会自动产生

#-------------------------------------------------
#
# Project created by QtCreator 2019-06-24T10:20:30
#
#-------------------------------------------------

QT       += core gui sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = WUZIQI
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp \
        mainwindow.cpp \
    GameModel.cpp \
    subwindow.cpp \
    registerwindow.cpp \
    informationwindow.cpp \
    rankwindow.cpp

HEADERS += \
        mainwindow.h \
    GameModel.h \
    subwindow.h \
    registerwindow.h \
    informationwindow.h \
    rankwindow.h
FORMS +=

2.GameModel.h

#ifndef GAMEMODEL_H
#define GAMEMODEL_H

// ---- 五子棋游戏模型类 ---- //
#include   //变长数组
#include 
// 游戏类型,双人还是AI(目前固定让AI下黑子)
enum GameType
{
    PERSON,
    BOT
};
// 游戏状态
enum GameStatus
{
    PLAYING,
    WIN,
    DEAD
};
// 棋盘尺寸
const int kBoardSizeNum = 18;
class GameModel
{
public:
    GameModel();
public:
    std::vector<std::vector<int>> gameMapVec; // 存储当前游戏棋盘和棋子的情况,空白为0,白子1,黑子-1
    std::vector<std::vector<int>> scoreMapVec; // 存储各个点位的评分情况,作为AI下棋依据
    bool playerFlag; // 标示下棋方
    GameType gameType; // 游戏模式
    GameStatus gameStatus; // 游戏状态

    void startGame(GameType type); // 开始游戏
    void calculateScore(); // 计算评分
    void actionByPerson(int row, int col); // 人执行下棋
    void actionByAI(int &clickRow, int &clickCol); // 机器执行下棋
    void updateGameMap(int row, int col); // 每次落子后更新游戏棋盘
    bool isWin(int row, int col); // 判断游戏是否胜利
    bool isDeadGame(); // 判断是否和棋
};
#endif // GAMEMODEL_H

3.informationwindow.h

#ifndef INFORMATIONWINDOW_H
#define INFORMATIONWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include 

class informationwindow : public QWidget
{
    Q_OBJECT
public:
    explicit informationwindow(QWidget *parent = nullptr);
    QPushButton a;
    void cout();
    QString NAME;

    QString  win;
    QString  lose;
    QString  name;
    QString  handle;
    QString  sex;

signals:
public slots:
private:
    QLabel *label1 = new QLabel(this);
    QLabel *label2 = new QLabel(this);
    QLabel *label3 = new QLabel(this);
    QLabel *label4 = new QLabel(this);
    QLabel *label5 = new QLabel(this);
    QLabel *label6 = new QLabel(this);
protected:
    //重写绘图事件
    //如果在窗口绘图,必须在绘图事件里实现
    //绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
    void paintEvent(QPaintEvent *);
};

#endif // INFORMATIONWINDOW_H

4.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include "GameModel.h"
#include 
#include "subwindow.h" //子窗口头文件
#include "informationwindow.h"
#include "rankwindow.h"
#include 

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    QPushButton b3;
    //void mySlot();
    void changeWin();
    void dealsub(QString);
    QString NAME;
    int color=0;
    int giveup=0;
    int Returns=0;

protected:
    // 绘制
    void paintEvent(QPaintEvent *event);
    // 监听鼠标移动情况,方便落子
    void mouseMoveEvent(QMouseEvent *event);
    // 实际落子
    void mouseReleaseEvent(QMouseEvent *event);



private:
    GameModel *game; // 游戏指针
    GameType game_type; // 存储游戏类型
    int clickPosRow, clickPosCol; // 存储将点击的位置
    void initGame();
    void checkGame(int y, int x);

    subwindow subwin; //子窗口
    informationwindow infwin;
    rankwindow rankwin;
    QLabel *currentTimeLabel; // 先创建一个QLabel对象
    QTime *time;
    QTimer *timer;
    int a[1000][2];
    int nows=0;

private slots:
    void chessOneByPerson(); // 人执行
    void chessOneByAI(); // AI下棋

    void initPVPGame();
    void initPVEGame();
    void showinf();
    void showrank();
    void exit();
    void backdeepwood();
    void backwood();
    void backgraywood();
    void backorangewood();
    void Giveup();
    void pause();
    void Return();
    void exland();
    //void Helpwindow();
    void time_update(); //时间更新槽函数,状态栏显示时间

signals:
     /*信号必须有signals关键字来声明
      * 信号没有返回值,但可以有参数
      * 信号就是函数的声明,只需声明,无需定义
     */
     void mySignal(QString);

};

#endif // MAINWINDOW_H

5.rankwindow.h

#ifndef TANKWINDOW_H
#define TANKWINDOW_H

#include 
#include 
#include 
#include 
#include 
#include 

class rankwindow : public QWidget
{
    Q_OBJECT
public:
    explicit rankwindow(QWidget *parent = nullptr);

signals:

public slots:

protected:
    //重写绘图事件
    //如果在窗口绘图,必须在绘图事件里实现
    //绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
    void paintEvent(QPaintEvent *);
};

#endif // TANKWINDOW_H

6.registerwindow.h

#ifndef REGISTERWINDOW_H
#define REGISTERWINDOW_H

#include 
#include 
#include 

class registerwindow : public QWidget
{
    Q_OBJECT
public:
    explicit registerwindow(QWidget *parent = nullptr);
    void sendregitslot();
    void sendreturnslot();
    QLineEdit *pNormalLineEdit1 = new QLineEdit(this);
    QLineEdit *pNormalLineEdit2 = new QLineEdit(this);
    QLineEdit *pNormalLineEdit3 = new QLineEdit(this);
    QLineEdit *pPasswordLineEdit1 = new QLineEdit(this);
    QLineEdit *pPasswordLineEdit2 = new QLineEdit(this);

protected:
    //重写绘图事件
    //如果在窗口绘图,必须在绘图事件里实现
    //绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
    void paintEvent(QPaintEvent *);


signals:
        void regitSignal();

public slots:

private:
    QPushButton d;
    QPushButton e;
};

#endif // REGISTERWINDOW_H

7.subwindow.h

#ifndef SUBWINDOW_H
#define SUBWINDOW_H
#include 
#include 
#include 
#include "registerwindow.h"
#include 

class subwindow : public QWidget
{
    Q_OBJECT
public:
    explicit subwindow(QWidget *parent = nullptr);
     void sendSlot();
     void subwindow::on_pushButton_2_clicked();
     QLineEdit *pNormalLineEdit = new QLineEdit(this);
     QLineEdit *pPasswordLineEdit = new QLineEdit(this);
     void changwin();
     void dealSlot();


protected:
    //重写绘图事件
    //如果在窗口绘图,必须在绘图事件里实现
    //绘图事件内部自动调用,窗口需要重绘的时候(状态改变)
    void paintEvent(QPaintEvent *);

signals:
     /*信号必须有signals关键字来声明
      * 信号没有返回值,但可以有参数
      * 信号就是函数的声明,只需声明,无需定义
     */
     void mySignal(QString);
public slots:

private:
    QPushButton b;
    QPushButton c;

    registerwindow regist;
};

#endif // SUBWINDOW_H

8.GameModel.cpp

#include 
#include 
#include   //设置下棋的时间
#include "GameModel.h"
#include 

GameModel::GameModel()
{

}

void GameModel::startGame(GameType type)
{
    gameType = type;
    // 初始棋盘
    gameMapVec.clear();
    for (int i = 0; i < kBoardSizeNum; i++)
    {
        std::vector<int> lineBoard;
        for (int j = 0; j < kBoardSizeNum; j++)
            lineBoard.push_back(0);
        gameMapVec.push_back(lineBoard);
    }

    // 如果是AI模式,需要初始化评分数组
    if (gameType == BOT)
    {
        scoreMapVec.clear();
        for (int i = 0; i < kBoardSizeNum; i++)
        {
            std::vector<int> lineScores;
            for (int j = 0; j < kBoardSizeNum; j++)
                lineScores.push_back(0);
            scoreMapVec.push_back(lineScores);
        }
    }

    // 己方下为true,对方下位false
    playerFlag = true;
}

void GameModel::updateGameMap(int row, int col)
{
    if (playerFlag)
        gameMapVec[row][col] = 1;
    else
        gameMapVec[row][col] = -1;

    // 换手
    playerFlag = !playerFlag;
}

void GameModel::actionByPerson(int row, int col)
{
    updateGameMap(row, col);
}

void GameModel::actionByAI(int &clickRow, int &clickCol)
{
    // 计算评分
    calculateScore();

    // 从评分中找出最大分数的位置
    int maxScore = 0;
    std::vector<std::pair<int, int>> maxPoints;

    for (int row = 1; row < kBoardSizeNum; row++)
        for (int col = 1; col < kBoardSizeNum; col++)
        {
            // 前提是这个坐标是空的
            if (gameMapVec[row][col] == 0)
            {
                if (scoreMapVec[row][col] > maxScore)          // 找最大的数和坐标
                {
                    maxPoints.clear();
                    maxScore = scoreMapVec[row][col];
                    maxPoints.push_back(std::make_pair(row, col));
                }
                else if (scoreMapVec[row][col] == maxScore)     // 如果有多个最大的数,都存起来
                    maxPoints.push_back(std::make_pair(row, col));
            }
        }

    // 随机落子,如果有多个点的话
    srand((unsigned)time(0));
    int index = rand() % maxPoints.size();

    std::pair<int, int> pointPair = maxPoints.at(index);
    clickRow = pointPair.first; // 记录落子点
    clickCol = pointPair.second;
    updateGameMap(clickRow, clickCol);
}

// 最关键的计算评分函数
void GameModel::calculateScore()
{
    // 统计玩家或者电脑连成的子
    int personNum = 0; // 玩家连成子的个数
    int botNum = 0; // AI连成子的个数
    int emptyNum = 0; // 各方向空白位的个数

    // 清空评分数组
    scoreMapVec.clear();
    for (int i = 0; i < kBoardSizeNum; i++)
    {
        std::vector<int> lineScores;
        for (int j = 0; j < kBoardSizeNum; j++)
            lineScores.push_back(0);
        scoreMapVec.push_back(lineScores);
    }

    // 计分(此处是完全遍历,其实可以用bfs或者dfs加减枝降低复杂度,通过调整权重值,调整AI智能程度以及攻守风格)
    for (int row = 0; row < kBoardSizeNum; row++)
        for (int col = 0; col < kBoardSizeNum; col++)
        {
            // 空白点就算
            if (row > 0 && col > 0 &&
                gameMapVec[row][col] == 0)
            {
                // 遍历周围八个方向
                for (int y = -1; y <= 1; y++)
                    for (int x = -1; x <= 1; x++)
                    {
                        // 重置
                        personNum = 0;
                        botNum = 0;
                        emptyNum = 0;

                        // 原坐标不算
              if (!(y == 0 && x == 0))
              {
               // 每个方向延伸4个子

              // 对玩家白子评分(正反两个方向)
              for (int i = 1; i <= 4; i++)
              {
               if (row + i * y > 0 && row + i * y < kBoardSizeNum &&col + i * x > 0 && col + i * x <kBoardSizeNum &&gameMapVec[row + i * y][col + i * x] == 1) // 玩家的子{
               personNum++;
	 }
               else if (row + i * y > 0 && row + i * y < kBoardSizeNum &&col + i * x > 0 && col + i * x < kBoardSizeNum && gameMapVec[row + i * y][col + i * x] == 0) // 空白位{
               emptyNum++;
                break;
	}
                else            // 出边界
                break;
    }

                for (int i = 1; i <= 4; i++){
                if (row - i * y > 0 && row - i * y < kBoardSizeNum &&
col - i * x > 0 && col - i * x < kBoardSizeNum &&gameMapVec[row - i * y][col - i * x] == 1) // 玩家的子 {
                  personNum++;
	}
         	else if (row - i * y > 0 && row - i * y < kBoardSizeNum &&col - i * x > 0 && col - i * x < kBoardSizeNum && gameMapVec[row - i * y][col - i * x] == 0) // 空白位 {
                 emptyNum++;
                 break;
	}
                  else            // 出边界
                  break;
  }

                   if (personNum == 1)                      // 杀二
                   scoreMapVec[row][col] += 10;
                   else if (personNum == 2)                 // 杀三
                            {
                    if (emptyNum == 1)
                    scoreMapVec[row][col] += 30;
                    else if (emptyNum == 2)
                    scoreMapVec[row][col] += 40;
           }
                    else if (personNum == 3)                 // 杀四
                    {
                    // 量变空位不一样,优先级不一样
                     if (emptyNum == 1)
                     scoreMapVec[row][col] += 60;
                     else if (emptyNum == 2)
                     scoreMapVec[row][col] += 110;
            }
                     else if (personNum == 4)                 // 杀五
                     scoreMapVec[row][col] += 10100;

                      // 进行一次清空
                      emptyNum = 0;

                      // 对AI黑子评分
                      for (int i = 1; i <= 4; i++){
                      if (row + i * y > 0 && row + i * y < kBoardSizeNum &&col + i * x > 0 && col + i * x < kBoardSizeNum &&gameMapVec[row + i * y][col + i * x] == 1) // 玩家的子
                       {
                       botNum++;
       }
        else if (row + i * y > 0 && row + i * y < kBoardSizeNum && col + i * x > 0 && col + i * x < kBoardSizeNum &&gameMapVec[row +i * y][col + i * x] == 0) // 空白位 {
                         emptyNum++;
                         break;
       }
                          else            // 出边界
                          break;
 }

         for (int i = 1; i <= 4; i++)
         {
              if (row - i * y > 0 && row - i * y < kBoardSizeNum &&col - i * x > 0 && col - i * x < kBoardSizeNum &&gameMapVec[row - i * y][col - i * x] == -1) // AI的子{
                botNum++;
}
       else if (row - i * y > 0 && row - i * y < kBoardSizeNum &&col - i * x > 0 && col - i * x < kBoardSizeNum && gameMapVec[row - i * y][col - i * x] == 0) // 空白位
        {
           emptyNum++;
           break;
    }
       else            // 出边界
         break;
}

        if (botNum == 0)                      // 普通下子
        scoreMapVec[row][col] += 5;
         else if (botNum == 1)                 // 活二
        scoreMapVec[row][col] += 10;
         else if (botNum == 2)
         {
         if (emptyNum == 1)                // 死三
         scoreMapVec[row][col] += 25;
         else if (emptyNum == 2)
         scoreMapVec[row][col] += 50;  // 活三
	 }
         else if (botNum == 3)
         {
          if (emptyNum == 1)                // 死四
          scoreMapVec[row][col] += 55;
          else if (emptyNum == 2)
          scoreMapVec[row][col] += 100; // 活四
          }
          else if (botNum >= 4)
          scoreMapVec[row][col] += 10000;   // 活五
           }
        }
     }
 }
}

bool GameModel::isWin(int row, int col)
{
    // 横竖斜四种大情况,每种情况都根据当前落子往后遍历5个棋子,有一种符合就算赢
    // 水平方向
    for (int i = 0; i < 5; i++)
    {
        // 往左5个,往右匹配4个子,20种情况
        if (col - i > 0 &&
            col - i + 4 < kBoardSizeNum &&
            gameMapVec[row][col - i] == gameMapVec[row][col - i + 1] &&
            gameMapVec[row][col - i] == gameMapVec[row][col - i + 2] &&
            gameMapVec[row][col - i] == gameMapVec[row][col - i + 3] &&
            gameMapVec[row][col - i] == gameMapVec[row][col - i + 4])
            return true;
    }

    // 竖直方向(上下延伸4个)
    for (int i = 0; i < 5; i++)
    {
        if (row - i > 0 &&
            row - i + 4 < kBoardSizeNum &&
            gameMapVec[row - i][col] == gameMapVec[row - i + 1][col] &&
            gameMapVec[row - i][col] == gameMapVec[row - i + 2][col] &&
            gameMapVec[row - i][col] == gameMapVec[row - i + 3][col] &&
            gameMapVec[row - i][col] == gameMapVec[row - i + 4][col])
            return true;
    }

    // 左斜方向
    for (int i = 0; i < 5; i++)
    {
        if (row + i < kBoardSizeNum &&row + i - 4 > 0 &&col - i > 0 &&col - i + 4 < kBoardSizeNum &&gameMapVec[row + i][col - i] == gameMapVec[row + i - 1][col - i + 1] &&gameMapVec[row + i][col - i] == gameMapVec[row + i - 2][col - i + 2] &&gameMapVec[row + i][col - i] == gameMapVec[row + i - 3][col - i + 3] &&gameMapVec[row + i][col - i] == gameMapVec[row + i - 4][col - i + 4])
            return true;
    }

    // 右斜方向
    for (int i = 0; i < 5; i++)
    {
        if (row - i > 0 &&row - i + 4 < kBoardSizeNum &&col - i > 0 &&col - i + 4 < kBoardSizeNum &&gameMapVec[row - i][col - i] == gameMapVec[row - i + 1][col - i + 1] &&gameMapVec[row - i][col - i] == gameMapVec[row - i + 2][col - i + 2] &&gameMapVec[row - i][col - i] == gameMapVec[row - i + 3][col - i + 3] &&gameMapVec[row - i][col - i] == gameMapVec[row - i + 4][col - i + 4])
            return true;
    }

    return false;
}

bool GameModel::isDeadGame()
{
    // 所有空格全部填满
    for (int i = 1; i < kBoardSizeNum; i++)
        for (int j = 1; j < kBoardSizeNum; j++)
        {
            if (!(gameMapVec[i][j] == 1 || gameMapVec[i][j] == -1))
                return false;
        }
    return true;
}

9.informationwindow.cpp

#include "informationwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include 
#include 
#include 
#include 
#include 

informationwindow::informationwindow(QWidget *parent) : QWidget(parent)
{
    a.setParent(this);
    a.setText("clike");
   connect(&a,&QPushButton::clicked,this,&informationwindow::cout);
    a.move(100,100);

    /*QTimer *timer=new QTimer;
        connect(timer, &QTimer::timeout, this, [&](){
            //5秒调用一次,想干啥都行
            //假设按钮是pushButton
            a.click();
        });
        timer->start(100);
*/
    a.hide();
    this->setWindowTitle("Personal Information");
    resize(500,500);
}

void informationwindow::cout(){

    qDebug()<<QSqlDatabase::drivers();
    //添加MYSQL数据库
    QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
    //连接数据库
    db.setHostName("127.0.0.1"); //数据库服务器IP
    db.setUserName("root"); //数据库用户名
    db.setPassword("holdworld000312"); //数据库密码
    db.setDatabaseName("slimes"); //使用哪个数据库

    if(!db.open()){
        QMessageBox::information(this, "连接结果", "连接数据库失败!");
        return;
    }


    QString sq=QStringLiteral("select *from wuziqi_user where id=1");
    QSqlQuery sqlquery=QSqlQuery(db);
    sqlquery.exec(sq);
    NAME = "";
    while (sqlquery.next())
    {
            NAME.append(sqlquery.value(1).toString());
    }

    win = "";
    QString sq5=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
    QSqlQuery sqlquery5=QSqlQuery(db);
    sqlquery5.exec(sq5);
    while (sqlquery5.next())
    {
            win.append(sqlquery5.value(3).toString());
    }

    handle = "";
    QString sq1=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
    QSqlQuery sqlquery1=QSqlQuery(db);
    sqlquery1.exec(sq1);
    while (sqlquery1.next())
    {
            handle.append(sqlquery1.value(1).toString());
    }

    lose = "";
    QString sq2=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
    QSqlQuery sqlquery2=QSqlQuery(db);
    sqlquery2.exec(sq2);
    while (sqlquery2.next())
    {
            lose.append(sqlquery2.value(4).toString());
    }

    name = "";
    QString sq3=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
    QSqlQuery sqlquery3=QSqlQuery(db);
    sqlquery3.exec(sq3);
    while (sqlquery3.next())
    {
           name.append(sqlquery3.value(5).toString());
    }

    sex = "";
    QString sq4=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
    QSqlQuery sqlquery4=QSqlQuery(db);
    sqlquery4.exec(sq4);
    while (sqlquery4.next())
    {
           sex.append(sqlquery4.value(6).toString());
    }
    resize(500,500);



    qDebug()<< NAME<<handle<<win<<lose<<name<<sex;

}

void informationwindow::paintEvent(QPaintEvent *){

    QPainter p2;  //创建画家对象
    p2.begin(this); //指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p2.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=3720865353,2633372586&fm=26&gp=0.jpg"));
    //QPainter p(this);
   if( sex == "men"){
    QPainter p;  //创建画家对象
    p.begin(this); //指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p.drawPixmap(100,100,100,100, QPixmap("../Image/7D016817BF7BEE8D56116FCBA333DBFB.jpg"));
    }
   if( sex == "women"){
    QPainter p1;  //创建画家对象
    p1.begin(this);//指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p1.drawPixmap(100,100,100,100, QPixmap("../Image/776CC876C0FAD440626034DE13CD5866.jpg"));
   }
}

10.main.cpp

#include "mainwindow.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    //w.show();

    return a.exec();
}

11.mainwindow.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   //打印
#include 
#include "mainwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

const int kBoardMargin = 30; // 棋盘边缘空隙
const int kRadius = 15; // 棋子半径
const int kMarkSize = 6; // 落子标记边长
const int kBlockSize = 40; // 格子的大小
const int kPosDelta = 20; // 鼠标点击的模糊距离上限

const int kAIDelay = 700; // AI下棋的思考时间

// -------------------- //

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{

    setWindowTitle("wuziqi");//等于this->setWindowTitle("boss");

    //显示子窗口
    subwin.show();

    //处理子窗口的信号
    void(subwindow::*funSignal)(QString)=&subwindow::mySignal;
    connect(&subwin,funSignal, this, &MainWindow::dealsub);
    //让窗口大小不变
    resize(500, 500);

    // 设置棋盘大小
    setFixedSize(kBoardMargin * 2 + kBlockSize * kBoardSizeNum, kBoardMargin * 2 + kBlockSize * kBoardSizeNum);
    //setStyleSheet("background-color:yellow;");

    // 开启鼠标hover功能,这两句一般要设置window的
    setMouseTracking(true);
    //    centralWidget()->setMouseTracking(true);

    // 添加游戏菜单
    QMenu *gameMenu = menuBar()->addMenu(tr("Game")); // menuBar默认是存在的,直接加菜单就可以了
    QAction *actionPVP = new QAction("Person VS Person", this);
    connect(actionPVP, SIGNAL(triggered()), this, SLOT(initPVPGame()));
    gameMenu->addAction(actionPVP);

    gameMenu->addSeparator(); //添加分割线

    QAction *actionPVE = new QAction("Person VS Computer", this);
    connect(actionPVE, SIGNAL(triggered()), this, SLOT(initPVEGame()));
    gameMenu->addAction(actionPVE);

    //添加信息菜单
    QMenu *gameMenuset = menuBar()->addMenu(tr("setting")); // menuBar默认是存在的,直接加菜单就可以了
    QAction *actionPVPinf = new QAction("information(&A)", this);
    connect(actionPVPinf, SIGNAL(triggered()),this ,SLOT(showinf()));
    gameMenuset->addAction(actionPVPinf);

    //添加排行榜
    QAction *actionPVPrank = new QAction("rank(&B)", this);
    connect(actionPVPrank, SIGNAL(triggered()),this ,SLOT(showrank()));
    gameMenuset->addAction(actionPVPrank);


    //添加换背景栏
    QMenu *actionPVPback = new QMenu("background(&C)", this);
    //connect(actionPVPback, SIGNAL(triggered()),this ,SLOT(showrank()));
    gameMenuset->addMenu(actionPVPback);

    //添加退出登陆
    QAction *actionPVPexland = new QAction("Exit landing(&D)", this);
    connect(actionPVPexland, SIGNAL(triggered()),this ,SLOT(exland()));
    gameMenuset->addAction(actionPVPexland);

    //添加深木色的背景
    QAction *actionPVPdeepwood = new QAction("deepwood", this);
    connect(actionPVPdeepwood, SIGNAL(triggered()),this ,SLOT(backdeepwood()));
    actionPVPback->addAction(actionPVPdeepwood);
    //添加浅木色的背景
    QAction *actionPVPwood = new QAction("lightwood", this);
    connect(actionPVPwood, SIGNAL(triggered()),this ,SLOT(backwood()));
    actionPVPback->addAction(actionPVPwood);
    //添加灰木色的背景
    QAction *actionPVPgraywood = new QAction("graywood", this);
    connect(actionPVPgraywood, SIGNAL(triggered()),this ,SLOT(backgraywood()));
    actionPVPback->addAction(actionPVPgraywood);

    //添加橙木色的背景
    QAction *actionPVPorangewood = new QAction("orangewood", this);
    connect(actionPVPorangewood, SIGNAL(triggered()),this ,SLOT(backorangewood()));
    actionPVPback->addAction(actionPVPorangewood);

    QMenu *gameMenuoper = menuBar()->addMenu(tr("operation")); // menuBar默认是存在的,直接加菜单就可以了

    //认输
    QAction *actionPVPgiveup = new QAction("give up(&E)", this);
    connect(actionPVPgiveup, SIGNAL(triggered()),this ,SLOT(Giveup()));
    gameMenuoper->addAction(actionPVPgiveup);

    //暂停
    QAction *actionPVPpause = new QAction("pause(&F)", this);
    connect(actionPVPpause, SIGNAL(triggered()),this ,SLOT(pause()));
    gameMenuoper->addAction(actionPVPpause);

    //悔棋
    QAction *actionPVPreturn = new QAction("return(&G)", this);
    connect(actionPVPreturn, SIGNAL(triggered()),this ,SLOT(Return()));
    gameMenuoper->addAction(actionPVPreturn);

    //帮助
    //QAction *actionPVPhelp = new QAction("help(&H)", this);
    //connect(actionPVPhelp, SIGNAL(triggered()),this ,SLOT(Helpwindow()));
    //gameMenuoper->addAction(actionPVPhelp);

    //添加退出
    QAction *actionPVPexit = new QAction("exit(&I)", this);
    connect(actionPVPexit, SIGNAL(triggered()),this ,SLOT(exit()));
    gameMenuoper->addAction(actionPVPexit);


    // 开始游戏
    initGame();

     //setWindowTitle("-五子棋-");

    //状态栏

    currentTimeLabel = new QLabel; // 创建QLabel控件
    QStatusBar *sBar = statusBar();
    sBar->addWidget(new QLabel("time:",this));
    sBar->addWidget(currentTimeLabel);
    timer = new QTimer(this);
    time = new QTime(0,0,0);
    timer->start(1000); //每隔1000ms发送timeout的信号
    connect(timer, SIGNAL(timeout()),this,SLOT(time_update()));
}

void MainWindow::time_update()
{
    //time->addSecs(1);
    *time=time->addSecs(1);
    //QDateTime current_time =  new QDateTime(0,0,0);
    QString timestr = time->toString("hh:mm:ss"); //设置显示的格式
   currentTimeLabel->setText(timestr); //设置label的文本内容为时间
}

//void MainWindow::Helpwindow(){
//    helpwin.show();
//}

void MainWindow::exland(){
    this->hide();
    subwin.show();
}

void MainWindow::Return(){
    Returns=1;
}

void MainWindow::pause(){
    timer->stop();
    QMessageBox::StandardButton btnValue = QMessageBox::information(this, "pause", "The game is paused.");
    timer->start();
}

void MainWindow::backwood(){
    color=0;
}


void MainWindow::backdeepwood(){
    color=1;
}

void MainWindow::backgraywood(){
    color=2;
}

void MainWindow::backorangewood(){
    color=3;
}

void MainWindow::Giveup(){
    giveup=1;
    time->setHMS(0,0,0);
}

//退出
void MainWindow::exit(){
    this->close();
}

//显示排行榜
void MainWindow::showrank(){
    rankwin.show();
}

//显示个人信息
void MainWindow::showinf(){
    infwin.show();
    infwin.a.click();
}



void MainWindow::changeWin(){
    //子窗口显示
    subwin.show();
    //本窗口隐藏
    this->hide();
}

void MainWindow::dealsub(QString str){
    NAME = str;
    qDebug()<<NAME;
    //子窗口显示
    subwin.hide();
    //本窗口隐藏
    this->show();
    time->setHMS(0,0,0);
}

MainWindow::~MainWindow()
{
    if (game)
    {
        delete game;
        game = nullptr;
    }
}

void MainWindow::initGame()
{
    // 初始化游戏模型
    game = new GameModel;
    initPVPGame();
}

void MainWindow::initPVPGame()
{
    game_type = PERSON;
    game->gameStatus = PLAYING;
    game->startGame(game_type);
    time->setHMS(0,0,0);
    update();
}

void MainWindow::initPVEGame()
{
    game_type = BOT;
    game->gameStatus = PLAYING;
    game->startGame(game_type);
    time->setHMS(0,0,0);
    update();
}

void MainWindow::paintEvent(QPaintEvent *event)
{
    //QPainter p(this);
    QPainter p;  //创建画家对象
    p.begin(this); //指定当前窗口为绘图设备

    //绘图操作
    //画背景图
    if(color == 0)
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=3967820435,4109070133&fm=26&gp=0.jpg"));

    if(color == 1)
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=867276353,2609607622&fm=72.jpg"));

    if(color == 2)
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=1985406695,1258431003&fm=11&gp=0_WPS图片.jpg"));

    if(color == 3)
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=3606751862,1869919184&fm=26&gp=0.jpg"));

    QPainter painter(this);
    // 绘制棋盘
    painter.setRenderHint(QPainter::Antialiasing, true); // 抗锯齿
//    QPen pen; // 调整线条宽度
//    pen.setWidth(2);
//    painter.setPen(pen);
    for (int i = 0; i < kBoardSizeNum + 1; i++)
    {
        painter.drawLine(kBoardMargin + kBlockSize * i, kBoardMargin, kBoardMargin + kBlockSize * i, size().height() - kBoardMargin);
        painter.drawLine(kBoardMargin, kBoardMargin + kBlockSize * i, size().width() - kBoardMargin, kBoardMargin + kBlockSize * i);
    }

    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    // 绘制落子标记(防止鼠标出框越界)
    if (clickPosRow > 0 && clickPosRow < kBoardSizeNum &&
        clickPosCol > 0 && clickPosCol < kBoardSizeNum &&
        game->gameMapVec[clickPosRow][clickPosCol] == 0)
    {
        if (game->playerFlag)
            brush.setColor(Qt::white);
        else
            brush.setColor(Qt::black);
        painter.setBrush(brush);
        painter.drawRect(kBoardMargin + kBlockSize * clickPosCol - kMarkSize / 2, kBoardMargin + kBlockSize * clickPosRow - kMarkSize / 2, kMarkSize, kMarkSize);
    }
    if(Returns==1){
        game->gameMapVec[a[nows][0]][a[nows][1]]=0;
        nows--;
        Returns=0;
    }
    // 绘制棋子
    for (int i = 0; i < kBoardSizeNum; i++)
        for (int j = 0; j < kBoardSizeNum; j++)
        {
            if (game->gameMapVec[i][j] == 1)
            {
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(kBoardMargin + kBlockSize * j - kRadius, kBoardMargin + kBlockSize * i - kRadius, kRadius * 2, kRadius * 2);
            }
            else if (game->gameMapVec[i][j] == -1)
            {
                brush.setColor(Qt::black);
                painter.setBrush(brush);
                painter.drawEllipse(kBoardMargin + kBlockSize * j - kRadius, kBoardMargin + kBlockSize * i - kRadius, kRadius * 2, kRadius * 2);
            }
        }

    if(giveup == 1){
        QString str = "black player";
        giveup = 0;
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, "congratulations", str + " win!");
        //打印QT支持的数据库驱动
        qDebug()<<QSqlDatabase::drivers();

        //添加MYSQL数据库
        QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
        //连接数据库
        db.setHostName("127.0.0.1"); //数据库服务器IP
        db.setUserName("root"); //数据库用户名
        db.setPassword("holdworld000312"); //数据库密码
        db.setDatabaseName("slimes"); //使用哪个数据库

        if(!db.open()){
            QMessageBox::information(this, "连接结果", "连接数据库失败!");
            return;
        }
            //获得数据库中win,lise的值
            QString  win;
            QString  lose;
            QString sq=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
            QSqlQuery sqlquery=QSqlQuery(db);
            sqlquery.exec(sq);
            while (sqlquery.next())
            {
                    win.append(sqlquery.value(3).toString());
            }

            QString sq1=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
            QSqlQuery sqlquery1=QSqlQuery(db);
            sqlquery1.exec(sq1);
            while (sqlquery1.next())
            {
                    lose.append(sqlquery1.value(4).toString());
            }

            //qDebug()<
            qDebug()<<lose;
            //默认用户为白棋,输
            if(str == "black player"){
            int newlose = lose.toInt()+1;
            QSqlQuery query;
            query.prepare("update wuziqi_info set lose=? where handle=?");
            query.addBindValue(newlose);
            query.addBindValue(NAME);
            query.exec();
            }
            // 重置游戏状态,否则容易死循环
            if (btnValue == QMessageBox::Ok)
            {
                game->startGame(game_type);
                game->gameStatus = PLAYING;
            }
    }
    else{
    if (clickPosRow > 0 && clickPosRow < kBoardSizeNum &&
        clickPosCol > 0 && clickPosCol < kBoardSizeNum &&
        (game->gameMapVec[clickPosRow][clickPosCol] == 1 ||
            game->gameMapVec[clickPosRow][clickPosCol] == -1))
    {
        if (game->isWin(clickPosRow, clickPosCol) && game->gameStatus == PLAYING)
        {
            qDebug() << "win";
            game->gameStatus = WIN;
            QString str;
            if (game->gameMapVec[clickPosRow][clickPosCol] == 1)
                str = "white player";
            else if (game->gameMapVec[clickPosRow][clickPosCol] == -1)
                str = "black player";
            QMessageBox::StandardButton btnValue = QMessageBox::information(this, "congratulations", str + " win!");
              time->setHMS(0,0,0);
            //打印QT支持的数据库驱动
            qDebug()<<QSqlDatabase::drivers();

            //添加MYSQL数据库
            QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
            //连接数据库
            db.setHostName("127.0.0.1"); //数据库服务器IP
            db.setUserName("root"); //数据库用户名
            db.setPassword("holdworld000312"); //数据库密码
            db.setDatabaseName("slimes"); //使用哪个数据库

            if(!db.open()){
                QMessageBox::information(this, "连接结果", "连接数据库失败!");
                return;
            }
                //获得数据库中win,lise的值
                QString  win;
                QString  lose;
                QString sq=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
                QSqlQuery sqlquery=QSqlQuery(db);
                sqlquery.exec(sq);
                while (sqlquery.next())
                {
                        win.append(sqlquery.value(3).toString());
                }

                QString sq1=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
                QSqlQuery sqlquery1=QSqlQuery(db);
                sqlquery1.exec(sq1);
                while (sqlquery1.next())
                {
                        lose.append(sqlquery1.value(4).toString());
                }

                //qDebug()<
                //qDebug()<
                //默认用户为白棋,赢
                if(str == "white player"){
                int newwin = win.toInt()+1;
                QSqlQuery query;
                query.prepare("update wuziqi_info set win=? where handle=?");
                query.addBindValue(newwin);
                query.addBindValue(NAME);
                query.exec();
            }
                //默认用户为白棋,输
                if(str == "black player"){
                int newlose = lose.toInt()+1;
                QSqlQuery query;
                query.prepare("update wuziqi_info set lose=? where handle=?");
                query.addBindValue(newlose);
                query.addBindValue(NAME);
                query.exec();
            }
            // 重置游戏状态,否则容易死循环
            if (btnValue == QMessageBox::Ok)
            {
                game->startGame(game_type);
                game->gameStatus = PLAYING;
            }
        }
}


    // 判断死局
    if (game->isDeadGame())
    {
        QMessageBox::StandardButton btnValue = QMessageBox::information(this, "oops", "dead game!");
        if (btnValue == QMessageBox::Ok)
        {
            game->startGame(game_type);
            game->gameStatus = PLAYING;
        }

    }
  }
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    // 通过鼠标的hover确定落子的标记
    int x = event->x();
    int y = event->y();

    // 棋盘边缘不能落子
    if (x >= kBoardMargin + kBlockSize / 2 &&
            x < size().width() - kBoardMargin &&
            y >= kBoardMargin + kBlockSize / 2 &&
            y < size().height()- kBoardMargin)
    {
        // 获取最近的左上角的点
        int col = x / kBlockSize;
        int row = y / kBlockSize;

        int leftTopPosX = kBoardMargin + kBlockSize * col;
        int leftTopPosY = kBoardMargin + kBlockSize * row;

        // 根据距离算出合适的点击位置,一共四个点,根据半径距离选最近的
        clickPosRow = -1; // 初始化最终的值
        clickPosCol = -1;
        int len = 0; // 计算完后取整就可以了

        // 确定一个误差在范围内的点,且只可能确定一个出来
        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY) * (y - leftTopPosY));
        if (len < kPosDelta)
        {
            clickPosRow = row;
            clickPosCol = col;
        }
        len = sqrt((x - leftTopPosX - kBlockSize) * (x - leftTopPosX - kBlockSize) + (y - leftTopPosY) * (y - leftTopPosY));
        if (len < kPosDelta)
        {
            clickPosRow = row;
            clickPosCol = col + 1;
        }
        len = sqrt((x - leftTopPosX) * (x - leftTopPosX) + (y - leftTopPosY - kBlockSize) * (y - leftTopPosY - kBlockSize));
        if (len < kPosDelta)
        {
            clickPosRow = row + 1;
            clickPosCol = col;
        }
        len = sqrt((x - leftTopPosX - kBlockSize) * (x - leftTopPosX - kBlockSize) + (y - leftTopPosY - kBlockSize) * (y - leftTopPosY - kBlockSize));
        if (len < kPosDelta)
        {
            clickPosRow = row + 1;
            clickPosCol = col + 1;
        }
    }

    // 存了坐标后也要重绘
    update();
}

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    // 人下棋,并且不能抢机器的棋
    if (!(game_type == BOT && !game->playerFlag))
    {
        chessOneByPerson();
        // 如果是人机模式,需要调用AI下棋
        if (game->gameType == BOT && !game->playerFlag)
        {
            // 用定时器做一个延迟
            QTimer::singleShot(kAIDelay, this, SLOT(chessOneByAI()));
        }
    }

}

void MainWindow::chessOneByPerson()
{
    // 根据当前存储的坐标下子
    // 只有有效点击才下子,并且该处没有子
    if (clickPosRow != -1 && clickPosCol != -1 && game->gameMapVec[clickPosRow][clickPosCol] == 0)
    {
        nows++;
        game->actionByPerson(clickPosRow, clickPosCol);
        a[nows][0]=clickPosRow;
        a[nows][1]=clickPosCol;
        // 重绘
        update();
    }
}

void MainWindow::chessOneByAI()
{
    game->actionByAI(clickPosRow, clickPosCol);
    update();
}

12.rankwindow.cpp

#include "rankwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include
#include 
#include 
#include 
#include 
#include 
rankwindow::rankwindow(QWidget *parent) : QWidget(parent)
{

}


void rankwindow::paintEvent(QPaintEvent *){

    QPainter p;  //创建画家对象
    p.begin(this); //指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=4069830077,209421225&fm=26&gp=0.jpg"));


}

13.registerwindow.cpp

#include "registerwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

registerwindow::registerwindow(QWidget *parent) : QWidget(parent)
{

    setWindowTitle("注册界面");
    d.setParent(this);
    d.setText("注册");
    d.move(300,300);
    resize(500, 500);
    connect(&d, &QPushButton::clicked, this, &registerwindow::sendregitslot);

    e.setParent(this);
    e.setText("返回登陆");
    e.move(400,300);
    connect(&e, &QPushButton::clicked, this, &registerwindow::sendreturnslot);

    pNormalLineEdit1->setPlaceholderText("handle");
    pPasswordLineEdit1->setPlaceholderText("Password");
    pPasswordLineEdit2->setPlaceholderText("Password");

    pNormalLineEdit1->move(150,100);
    pPasswordLineEdit1->move(150,125);
    pPasswordLineEdit2->move(150,150);

    // 设置显示效果
    pNormalLineEdit1->setEchoMode(QLineEdit::Normal);
    pPasswordLineEdit1->setEchoMode(QLineEdit::Password);
    pPasswordLineEdit2->setEchoMode(QLineEdit::Password);

    pNormalLineEdit2->setPlaceholderText("name");
    pNormalLineEdit3->setPlaceholderText("sex");

    pNormalLineEdit2->move(150,175);
    pNormalLineEdit3->move(150,200);

    // 设置显示效果
    pNormalLineEdit2->setEchoMode(QLineEdit::Normal);
    pNormalLineEdit3->setEchoMode(QLineEdit::Normal);

    QString data6;
    data6="账号:";
    QLabel *label6 = new QLabel(this);
    label6->setText(QString(data6));
    label6->move(50,100);

    QString data7;
    data7="密码:";
    QLabel *label7 = new QLabel(this);
    label7->setText(QString(data7));
    label7->move(50,128);

    QString data8;
    data8="重新确认密码:";
    QLabel *label8 = new QLabel(this);
    label8->setText(QString(data8));
    label8->move(50,154);

    QString data9;
    data9="姓名:";
    QLabel *label9 = new QLabel(this);
    label9->setText(QString(data9));
    label9->move(50,180);

    QString data10;
    data10="性别(men/women):";
    QLabel *label10 = new QLabel(this);
    label10->setText(QString(data10));
    label10->move(27,205);
}

void  registerwindow::sendreturnslot(){
    emit regitSignal();
}

void registerwindow::sendregitslot(){
    //打印QT支持的数据库驱动
    qDebug()<<QSqlDatabase::drivers();

    //添加MYSQL数据库
    QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
    //连接数据库
    db.setHostName("127.0.0.1"); //数据库服务器IP
    db.setUserName("root"); //数据库用户名
    db.setPassword("holdworld000312"); //数据库密码
    db.setDatabaseName("slimes"); //使用哪个数据库

    if(!db.open()){
        QMessageBox::information(this, "连接结果", "连接数据库失败!");
        return;
    }

    QString handle=pNormalLineEdit1->text();
    QString passwd1=pPasswordLineEdit1->text();
    QString passwd2=pPasswordLineEdit2->text();
    QString name=pNormalLineEdit2->text();
    QString sex=pNormalLineEdit3->text();

    if(passwd1!=passwd2){
        QMessageBox::information(this, "warning","密码不一致.");
        pNormalLineEdit1->clear();
        pPasswordLineEdit1->clear();
        pPasswordLineEdit2->clear();
    }
    else if (handle== '\0' || passwd1=='\0'){
        QMessageBox::information(this, "warning","密码/账号不能为空.");
        pNormalLineEdit1->clear();
        pPasswordLineEdit1->clear();
        pPasswordLineEdit2->clear();
    }
    else{
        QString str = QString("select *from wuziqi_info where handle = '%1'").arg(handle);
        QSqlQuery query;
        query.exec(str);
        int o=0;

        while (query.next()){
           o=1;
        }

    if(o==1){
        QMessageBox::information(this, "Warning", " Account already exists ");
        pNormalLineEdit1->clear();
        pPasswordLineEdit1->clear();
        pPasswordLineEdit2->clear();
    }
    else{
        QMessageBox::information(this, "login", " login was successful ");
        QString str=QString("insert into wuziqi_info(handle, passwd,  win, lose, name, sex) values('%1','%2', 0 , 0 ,'%3','%4')").arg(handle).arg(passwd1).arg(name).arg(sex);
        QSqlQuery query;
        query.exec(str);
        //textEdit->setText("插入成功");
        pNormalLineEdit1->clear();
        pPasswordLineEdit1->clear();
        pPasswordLineEdit2->clear();
         emit regitSignal();
    }
  }
}

void registerwindow::paintEvent(QPaintEvent *){
    //QPainter p(this);
    QPainter p;  //创建画家对象
    p.begin(this); //指定当前窗口为绘图设备

    //绘图操作
    //画背景图
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=1869757711,2217152015&fm=26&gp=0.jpg"));

}

14.subwindow.cpp

#include "subwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

subwindow::subwindow(QWidget *parent) : QWidget(parent)
{

    QString data4;
    data4="Handle/Email:";
    QLabel *label4 = new QLabel(this);
    label4->setText(QString(data4));
    label4->move(15,255);

    QString data5;
    data5="Password:";
    QLabel *label5 = new QLabel(this);
    label5->setText(QString(data5));
    label5->move(15,293);

    pNormalLineEdit->setPlaceholderText("Normal");
    pPasswordLineEdit->setPlaceholderText("Password");

    pNormalLineEdit->move(125,250);
    pPasswordLineEdit->move(125,290);

    // 设置显示效果
    pNormalLineEdit->setEchoMode(QLineEdit::Normal);
    pPasswordLineEdit->setEchoMode(QLineEdit::Password);


    this->setWindowTitle("登陆界面");
    b.setParent(this);
    b.setText("Login");
    b.move(320,300);
    connect(&b, &QPushButton::clicked, this, &subwindow::sendSlot);

    c.setParent(this);
    c.setText("Register");
    c.move(320,350);

    connect(&c, &QPushButton::released, this, &subwindow::changwin);

    QString data;
    data="抵制不良游戏,拒绝盗版游戏;";
    QLabel *label = new QLabel(this);
    label->setText(QString(data));
    label->move(25,350);

    QString data1;
    data1="注意自我保护,谨防受骗上当;";
    QLabel *label1 = new QLabel(this);
    label1->setText(QString(data1));
    label1->move(25,375);

    QString data2;
    data2="适度游戏益脑,沉迷游戏伤身;";
    QLabel *label2 = new QLabel(this);
    label2->setText(QString(data2));
    label2->move(25,400);

    QString data3;
    data3="合理安排时间,享受健康生活;";
    QLabel *label3 = new QLabel(this);
    label3->setText(QString(data3));
    label3->move(25,425);


    //让窗口大小不变
    resize(500, 460);


    //处理registerwindow的信号
    connect(&regist,&registerwindow::regitSignal, this ,&subwindow::dealSlot);

}

void subwindow::dealSlot(){
    //registerwindow隐藏
    regist.hide();
    //subwindow显示
    this->show();
}

void subwindow::changwin(){
    //registerwindow显示
    regist.show();
    //subwindow隐藏
    this->hide();
}


void subwindow::sendSlot()
{
    //打印QT支持的数据库驱动
    qDebug()<<QSqlDatabase::drivers();

    //添加MYSQL数据库
    QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL"); //数据库对象
    //连接数据库
    db.setHostName("127.0.0.1"); //数据库服务器IP
    db.setUserName("root"); //数据库用户名
    db.setPassword("holdworld000312"); //数据库密码
    db.setDatabaseName("slimes"); //使用哪个数据库

    if(!db.open()){
        QMessageBox::information(this, "连接结果", "连接数据库失败!");
        return;
    }

    QString handle=pNormalLineEdit->text();
    QString passwd=pPasswordLineEdit->text();

    QSqlTableModel model;
    model.setTable("wuziqi_info");
    model.setFilter(tr("handle = '%1' and passwd = '%2'").arg(handle).arg(passwd));
    model.select();

    if(model.rowCount() == 1){
        QString NAME = handle;
        // 表示密码验证正确,登陆成功
        // ...... 显示登录后的状态
        QMessageBox::information(this, "Login", "登录成功!");

        QSqlQuery query1;
             query1.prepare("DELETE  From wuziqi_user where id=1");
              if(!query1.exec())//如果成功执行则返回1,未执行则为零
                 qDebug()<<"fail";

        QString sex = "";
        QString sq4=QStringLiteral("select *from wuziqi_info where handle='%1' ").arg(NAME);
        QSqlQuery sqlquery4=QSqlQuery(db);
        sqlquery4.exec(sq4);
        while (sqlquery4.next())
        {
               sex.append(sqlquery4.value(6).toString());
        }
        QString str=QString("insert into wuziqi_user(id,handle,sex) values(1,'%1','%2')").arg(handle).arg(sex);
        QSqlQuery query;
        query.exec(str);
        emit mySignal(NAME);
    }
    else{
        QMessageBox::information(this, "Warning", "用户名或密码错误,请重新输入!");
        pNormalLineEdit->clear();
        pPasswordLineEdit->clear();
    }
}

void subwindow::on_pushButton_2_clicked()
{
    close();
}

void subwindow::paintEvent(QPaintEvent *){
    //QPainter p(this);
    QPainter p;  //创建画家对象
    p.begin(this); //指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p.drawPixmap(0,0,width(),height(), QPixmap("../Image/u=4105832563,1730732419&fm=26&gp=0_WPS图片.jpg"));

    QPainter p1;  //创建画家对象
    p1.begin(this);//指定当前窗口为绘图设备
    //绘图操作
    //画背景图
    p1.drawPixmap(0,-30,250,300, QPixmap("../Image/614356656556334208.png"));
}

你可能感兴趣的:(QT)