实验报告及源码压缩包百度云下载:
链接: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设计,通过定义控件及槽函数组成。
本程序可以实现基本的五子棋的双人对战,人机对战,计时,撤回,投降,认输,暂停,换背景等功能,还有数据库支持的登陆,注册,玩家信息系统等功能
在代码的实现上,基础界面的代码为QT中的自己手写槽函数、控件和类,手动摆放控件位置来实现的,并为使用ui界面。本五子棋的代码核心是窗口类的切换及游戏框架类的应用,以此来实现较多的基本功能。
该五子棋程序的功能基本实现,通过精美简洁的界面设计和使用方式,给用户提供更好的使用体验。可惜的是本程序基于自己的mysql数据库实现,没法打包,但是若其他人想要使用这个代码,可以更改其中的数据库账户密码,或者创造个.db的静态数据库来储存。即可使用本程序的代码。
登陆界面为一个c++类,属于widget类。背景由两张图片组成。可在此界面中输入账号密码,账号密码将会在数据库中搜索,若与数据库中的数据一致,即可登陆到游戏界面中。
注册界面为一个c++类,属于widget类。可以输入账号及注册的密码。
注册时会搜素mysql数据库的已存在账号。
当注册账号已存在时:
同时,注册账号密码不可为空
注册界面由两个类组成,属于mainwindow类。一个为界面绘画类,一个为下棋类(包括人工AI下棋)
由于程序总是出bug,让我在显示数据库数据时出了问题,导致个人信息界面和排行榜界面没能显示出来,试过很多种方法还是不行,只能暂时放弃这两个功能。
首先是连接数据库,这个在很多个.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)
数据库中有两个表。
一个是wuziqi_info
用来存取注册后的个人信息。
其中包括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();
用画笔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"));
用到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的时候有棋子。
人机代码我放在一个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秒,这里就不节省时间复杂度啦。
写个状态栏,将计时器放于状态栏中。用到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的文本内容为时间
}
举例:
// 添加游戏菜单
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);
Information 和rank 都还没完成,这里先不介绍。
四种背景可供选择。
Deepwood:
Lightwood:
Graywood:
Orangewood:
这是退出登陆的按钮,可以通过这个回到登陆界面,换另一个账号继续玩本游戏。
投降后自己的输的局数在数据库中的记录会+1,同时计时器重新计时。(自己为白方)
很多功能都是慢慢想到的,然后再加上去,
这里我用了个二维数组来存储每次放棋后的操作,若点击撤回,则now的位置变为0,然后now-1,到上一个状态。
退出游戏。
这次的代码大部分是边学边完成的,所以框架较凌乱,有一些功能无法实现。但基础功能还是实现啦,由于一开始写程序框架的时候没有使用ui去做,而是自己手写控件和槽函数,所以界面比较简陋。但是基本功能都差不多实现啦。
本来想着用visual studio 2017连接mysql数据库来写程序的。但是我配了一个多星期的环境加上静态库,还是会报出一堆错误,所以果断放弃visual studio 2017 ,改用QT5.7+mysql,完成程序的编写。
本人先写了游戏框架及规则,在编写完mainwindow界面后才逐渐添加其他界面。在游戏编写的过程中,也是遇到了许许多多的问题,比如可以在网格外放棋子等。在观看众多博客后,才慢慢的将bug修改完。
之后,我才开始编写界面类,先写了登陆界面,这时候,我开始尝试数据库的连接,在我的电脑的path路径中加入mysql.dll静态数据库的路径后,总算成功连接上了数据库。之后便学了一系列的数据添加,读取,搜索。才成功实现了登陆界面及注册界面的编写。但这时候我面临了一个问题。
因为,其实从一开始,界面就全部存在,所以,每个界面拥有的都是在那一时刻的数据,无法更新到最新,就比如我想要的个人信息查询,排行榜。。。(这两个只是显示上除了问题,获取最新的数据我已经解决了),而且每个界面都是,独立的,无法知道是哪个用户在进行游戏。当时的第一做法便是,运用信号和槽。通过登陆界面,放送信号给其他窗口。但是却在主窗口传信息给子窗口时出现了问题。而且窗口众多,导致代码越写越乱。所以我便创造了个数据库的表,用来记录,此时在线的玩家的信息。最后就是每个窗口获得最新的信息的问题了。这里,因为要让窗口显示,肯定要先按一个按钮,所以,我在按钮里增加信息的更新。这样子,这个问题就被解决啦。编写这个代码中间还遇到了大大小小的问题,有些解决了,有些仍无法解决。
如图,我已经将数据库中的用户信息获取,并用qDebug输出,但是我尝试了好几种方法,还是不能将其显示在窗口上。现在只能根据男女,给用户安排了个头像哈哈。
因为排行榜很多数据,所以我想要将数据库的数据直接排序,然后再可视化。但是这时候又出现问题了,也许因为不是主窗口,所以,我可视化的表格里,都是空白的。但是,当我重新创个项目,去尝试这个代码时,又可以显示。
编写代码中遇到好多问题,大部分得以解决,可是还有几个至今都还无法解决,不过,等有一定的时间后还是应该将其解决,加上以后,我希望可以给该游戏连上区域网,实现玩家的对战。
虽然这只是一个课设,但是我的完成程度还让我很不满意,还有很多的功能无法实现,还有实现玩家联网对抗等等,慢慢的我会在进行改进,希望可以不断完善,最后将其变成一个app应用。所以在即将到来的暑假中,我会花一定的时间去考虑如何实现以上功能,从而更好的完善该程序的功能。当然我还想加入围棋等各种棋类,慢慢的去更多了解界面设计的其他知识点。让自己学到的知识更多。
之前社团考核用过c语言去写了一个部员管理系统,还有写过成绩管理系统,不过,当时的代码,还是只能在cmd窗口上运行,文件只能用txt保存,用链表来存数据,但是这次课设,成功脱离了小黑框,有了比较美观的界面,有很大的成就感。当然,主要还是得满足对客户的需求,所以我请了挺多同学来测试我的程序的,反响还行,人机也挺给力的哈哈。不过还是有很多不妥的地方需要我慢慢的改进,慢慢的去满足用户的需求。
通过这次课设的学习,我还是学到了好多,比如类,基类,派生类,继承,信号与槽等。对于界面设计,虽然不是主方向,但是,以后一定很多用得上的时候,所以这次的课设让我收获很大很大。
【1】哔哩哔哩视频:妙韵含的QT开发全套视频
在这里,我要感谢**老师给予我的指导和帮助,没有老师的指明方向,我现在肯定还是一头雾水的。这学期的c++程序设计课也接近尾声了,感谢老师传授我们的知识,还有对我的关注。
红色的是初始文件,蓝色的是类文件,这里要创建5个类文件。记住
GameModel和mainwindow用的是MainWindow类型,其他的均是Widget类型。在创建的时候记得选择,且不要ui页面。
文件名要一致,不然代码无法运行。
首先创建个不含ui的qt文件,具体操作可查看
https://blog.csdn.net/qq_43333395/article/details/90814951
文件目录要不含中文
这里不打勾,本程序不使用ui文件
接下来就是创建类
至于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, ®isterwindow::sendregitslot);
e.setParent(this);
e.setText("返回登陆");
e.move(400,300);
connect(&e, &QPushButton::clicked, this, ®isterwindow::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(®ist,®isterwindow::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"));
}