C Qt毕设项目:扫雷游戏

C Qt毕设项目:扫雷游戏

首页休闲益智Make8更新时间:2024-11-25
目录一、游戏简介

扫雷游戏大家应该都玩过,就是给你一个棋盘,让你点击棋盘上的格子,如果格子下有雷则游戏失败,否则显示一个数字,表示周围八格内含雷的数量,然后根据数字来推断哪些格子是安全的。当所有无雷的方格都被排查了,游戏胜利。

接下来,让我们用Qt和c 实现一个仿制版扫雷游戏。

二、整体框架

1.Qt设计师界面类:GameWindow

为了实现扫雷游戏的可视化界面,我们仿照原版游戏,在项目中添加Qt设计师界面类GameWindow(继承自QWidget)作为游戏的主界面。因为原版也只有一个界面,所以只需添加一个Qt界面类,便足以实现游戏全部的功能。

该类带来了gamewindow.h、gamewindow.cpp、gamewindow.ui三个文件,分别用于类的定义、类成员函数的具体实现、界面的图形化设计。

2.head.h和function.cpp

为了实现完整的游戏项目,我们还需要在类GameWindow的外部定义一些函数。因此添加源文件function.cpp用于包含这些函数的实现方法,并添加头文件head.h来进行库文件的包含和函数的声明,以便于在多个源文件中共享这些函数。

此外,项目中还可能会用到一些全局变量,为了实现源文件之间的共享,同样需要在head.h中进行这些变量的外部声明。

3.源文件main.cpp

编写main函数作为程序的入口。此外,我把全局变量的定义也放在了这个文件里。

4.Qt控件类:CustomButton

为什么需要定义这个类,后面会细讲。

三、具体实现

1.界面设计

由于图形化程序主要依靠用户的鼠标点击进行交互,因此在搭建项目时,我们先从游戏界面的设计入手,在GameWindow窗口中添加一些需要的控件。

原版扫雷游戏有一块大棋盘,左键点击棋盘中的方格即可进行排雷,右键点击则是标旗。考虑到方格具有点击效果,我们在Qt中使用QPushButton按钮来实现方格的功能,棋盘里有多少方格就创建多少个按钮,如16*16的棋盘则需要16*16个按钮。

而为了方便地给这么多按钮统一设计样式,这时候就需要定义一个CustomButton类了。我们让它继承自QPushButton,便能继承普通按钮的所有功能;而通过在类的构造函数中调用setStyleSheet方法,就可将所有这样的按钮设置为同一样式。

同时,还可为每个方格添加一个int类型的状态值condition,方便在后续的游戏过程中对方块的行为进行管理。

custombutton.h

#ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include "head.h" #include <QMouseEvent> class CustomButton : public QPushButton { public: CustomButton(QWidget *parent = nullptr); void mousePressEvent(QMouseEvent *event); int condition = 0; //表示方格的状态 0.未排查未标记 1.已排查 2.已标记 }; #endif // CUSTOMBUTTON_H

custombutton.cpp

这里除了设置按钮的一般样式,还设置了左键点击(排雷)和右键点击(标旗)后的样式改变效果。

需要注意的是,由于QPushButton类中好像没有专门用于右键点击事件的槽函数(至少我没找到),因此只能重写QPushButton的mousePressEvent事件,当判断为右键点击时就执行自定义的功能(标旗),左键点击则调用父类的方法。

样式表里需要用到旗子图标作为border-image,可以自己在网上找免费素材(自己画也行),然后添加到资源文件resourse.qrc中。

#include "custombutton.h" CustomButton::CustomButton(QWidget *parent): QPushButton(parent) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); } void CustomButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { // 如果是右键点击,则修改按钮的样式 if (condition == 1) return; //如果按钮已被排查,则不可标旗 if (condition == 0) //按钮未排查未标记 { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "border-image: url(:/icons/icons/note.png);" "}"); condition = 2; //改为标记态 } else //按钮已被标记 { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); condition = 0; //改回未排查未标记状态 } } else { // 如果是其它的鼠标按键事件,则调用父类的鼠标按下事件处理方法 QPushButton::mousePressEvent(event); } }

2.雷的布置

由于游戏里的雷分布在二维的棋盘上,因此我们定义一个char类型的二维数组(mine)来存储雷的信息,让棋盘的长和宽与数组的尺寸相对应。这样,棋盘上的每一个方格都对应mine中的一个元素,如果有雷,则存放'1',无雷则存放‘0’。

因为游戏中随时可能改变棋盘的长宽,为了方便改变数组的大小,我们使用二维动态数组,将数组的行(ROW)和列(COL)定义为全局变量,这样在程序运行过程中也可进行大小的调整。

有了mine数组后,理论上棋盘的信息就完全确定下来了。但实际游戏里,棋盘上显示的信息却不能直接靠mine数组来生成。

回顾一下原版游戏:鼠标没按下时,方格是空白的,表示尚未进行排雷;左键点击后,若方格下不是雷,则显示一个数字,表示该方格周围八格的含雷数量;若是雷,则游戏失败,同时棋盘上显示出全部雷和数字的信息。

因此,我们可以再定义一个char类型的二维数组(showl)(ps:本来用的show作为数组名,表示该数组存放棋盘的实际显示信息,但似乎会与Qt某个类的自带函数重名而报错)。用‘*’将数组初始化,表明该方格还未被排查;如果某个元素对应的方格被右键点击,若不是雷,则在mine中查询该方块周围八格的含雷数,并在showl中将元素值改为数字。

在main.cpp中定义的全局变量

char **mine; char **showl; int ROW = 9; int COL = 9; //棋盘实际的行和列 int ROWS = ROW 2; int COLS = COL 2; //用于存放棋盘信息的数组的行和列 //因为扫雷时要对周围八格进行检索,为了使边缘的方格行为与内部一致,在数组棋盘的外围加一圈空白 int EASY_COUNT = 10;//雷的数量 int NOT_MINE = ROW * COL - EASY_COUNT;//不含雷的方格数量 int not_mine = 0;// 游戏中已排查的无雷方格数量

function.cpp中棋盘的初始化和雷的布置

//棋盘的初始化,set表示用于填充的值 void InitBoard(char**& board, int rows, int cols, char set) { board = new char*[rows]; for (int i=0;i<rows;i ) board[i] = new char[cols]; for (int i = 0;i < rows; i ) for (int j = 0; j < cols; j ) { board[i][j] = set; } } //布置雷 void SetMine(char **board, int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row 1; int y = rand() % col 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } }

3.界面生成

当然,扫雷时仅仅对数组进行操作是不够的,毕竟数组是储存在内存中的变量,对玩家不可见,真正可进行交互的只是图形化的按钮棋盘。为了将按钮的行为与数组的行为连接到一起,我们需要进一步对按钮的左键点击事件进行处理,这需要用到Qt的信号-槽机制。

而由于这里有多个按钮,每个按钮的点击行为都相同,所以可使用Qt 的信号-槽映射机制(Signal-Slot Mapping),将每个按钮的点击信号都连接到同一个槽函数(OnButtonClicked)上。

为了得知用户所点击的按钮具体是哪一个按钮,可以在GameWindow类中添加CustomButton*类型的QVector数组buttons作为成员,在buttons中存放所有按钮的指针,以下标来确定每一个按钮。

gamewindow.h

public: QVector<CustomButton *> buttons;

gamewindow.cpp

#include "gamewindow.h" #include "ui_GameWindow.h" #include <QSignalMapper> GameWindow::GameWindow(QWidget *parent) : QWidget(parent), ui(new Ui::GameWindow) { ui->setupUi(this); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); } //游戏中按钮棋盘的生成 void GameWindow::createBoard() { for (int i=0;i<ROW;i ) for (int j=0;j<COL;j ) { CustomButton *button=new CustomButton(this); button->setGeometry(365-(double(COL)/2)*30 j*30,180 i*30,30,30); //为了控制棋盘生成的位置在窗口的中央,我联系窗口和棋盘的尺寸进行了计算 button->show(); buttons.append(button); //将创建的按钮指针添加到buttons数组中 QSignalMapper *mapper=new QSignalMapper(this); mapper->setMapping(button,i*COL j); //将按钮对应的下标作为mapper传递的参数 connect(button, SIGNAL(clicked()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), this, SLOT(onButtonClicked(int))); } }

4.实现扫雷

注意:在原版游戏里,有时点击一个无雷的方格,只会显示出该格下面的数字,而有时则会显示出周围一大片数字。这是因为:如果点击的那个方格周围八格里至少有一个雷,那可直接将统计出的数字放进方格,让玩家来推断是周围哪几格中含雷;如果那个方格周围八格没有雷,要是仍按照刚才的做法,把数字0放进方格就结束,玩家自然知道周围八格也没有雷,但还得用鼠标一个个点击排查,徒增无用功。

所以,正确的处理方法是:如果被点击的某个方格周围八格没有雷,则程序应代替玩家对周围八格继续进行排查,直到排查到的某个方格周围八格有雷为止。这就需要用到函数递归的思想。

同时,为了美观,经过排查后的方格也应改变样式。是雷的方格还应换上地雷图标,同样自己去找,然后添加到项目的资源文件中。

扫雷过程中会涉及到游戏状态的改变(胜利或失败),失败即踩雷,调用一个自己写的reveal_All函数,将棋盘上所有信息显示出来;胜利则要通过已排查的方格数量来判断,这里用之前定义好的全局变量not_mine来更新排查过的方格数目,NOT_MINE保存所有无雷方格的数量,当not_mine与NOT_MINE相等时,判断为游戏胜利。

GameWindow.cpp中按钮点击的槽函数

void GameWindow::onButtonClicked(int index) //index为按钮在buttons中的下标 { if (buttons[index]->condition == 1 || buttons[index]->condition == 2)return; //如果按钮处于已排查或已标记状态,则点击不起作用 int x=0,y=0; x=index/COL; y=index-x*COL 1; x ; //将按钮在buttons中的一维下标转化为棋盘中的二维下标 if (mine[x][y] == '1') //踩到雷,则本局游戏结束,并调用reveal_All()显示棋盘全部信息 { not_mine=0; this->ui->label_result->setText("对不起,你踩雷了。"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/sad.png);" "}"); //这里会用到表情图标,之后会提到 reveal_All(mine, showl, this, ROW, COL); return; } else //没踩雷,则调用Sweep函数进一步扫雷 { Sweep(mine, showl, this, ROW, COL, x, y); if (not_mine == NOT_MINE) //判断为游戏胜利 { not_mine=0; //将not_mine清空,方便进行下一局游戏 this->ui->label_result->setText("恭喜你,扫雷成功!"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/cool.png);" "}"); return; } } }

function.cpp

//递归实现扫雷 void Sweep(char **mine, char **show, GameWindow* w,int row, int col, int x, int y) { //因为要在GameWindow窗口上更改按钮状态,而Sweep不是其成员函数,所以需要传递窗口指针过去 int index=y-1 (x-1)*COL; //将二维下标转化为一维下标 if (show[x][y]!='*')return; int count = mine[x - 1][y - 1] mine[x - 1][y] mine[x - 1][y 1] mine[x][y - 1] mine[x][y 1] mine[x 1][y - 1] mine[x 1][y] mine[x 1][y 1] - '0' * 8; show[x][y]=count '0'; w->buttons[index]->setText(QString::number(count)); w->buttons[index]->setStyleSheet("QPushButton{" "font-size: 20px;" "background-color: #89CDEF;" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); w->buttons[index]->condition = 1; //将按钮状态修改为已排查 not_mine ; if (count == 0) //周围八格没有雷,则递归地对八格进行扫雷 { Sweep(mine, show, w, row, col, x - 1, y - 1); Sweep(mine, show, w, row, col, x - 1, y); Sweep(mine, show, w, row, col, x - 1, y 1); Sweep(mine, show, w, row, col, x, y - 1); Sweep(mine, show, w, row, col, x, y 1); Sweep(mine, show, w, row, col, x 1, y - 1); Sweep(mine, show, w, row, col, x 1, y); Sweep(mine, show, w, row, col, x 1, y 1); } } //踩雷时,棋盘显示全部信息 void reveal_All(char **mine, char **show, GameWindow* w,int row, int col) { for (int i=1;i<=row;i ) for (int j=1;j<=col;j ) { int index=j-1 (i-1)*COL; if (mine[i][j]=='1') { w->buttons[index]->condition = 1; w->buttons[index]->setStyleSheet("QPushButton{" "background-color: #89CDEF;" "border-image: url(:/icons/icons/mine.png);" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); } else Sweep(mine, show, w, row, col, i, j); } }5.功能细化

(1)界面整改

再回去看一眼原版游戏,发现上面有五个选项可以重新选择棋盘的尺寸;此外,还有一个表情图标,用于标识游戏的状态(胜利、失败、进行中)

此外,玩家可能也需要在游戏中重置棋盘,或者选择退出游戏。

因此,我们在这里对游戏的UI进行一个整体的美化,这可以方便地在Qt的设计界面里通过改变控件的样式表来实现。同时增添改变棋盘尺寸的按钮、重新开始的按钮、退出游戏的按钮(均使用QPushButton),再用QLabel来放置表情图标。

这里我用的图标仍然来源于网上的免费素材。

(可能不算太美观,但我尽力了)

(2)尺寸更改

以“中级”(11*11的棋盘,15个雷)为示例,转到“中级”按钮的click槽函数,在该函数中实现按钮的功能。

由于旧棋盘的尺寸可能与新棋盘不一样,因此mine和showl数组的尺寸也需要更改。这里先清除掉旧的按钮棋盘(使用自定义的成员函数deleteBoard),然后将两个数组的空间全部释放,根据新的尺寸重新初始化,再重新生成新的按钮棋盘。之前定义好的ROW、COL、ROWS、COLS、EASY_COUNT、NOT_MINE这些全局变量在这里用于传递棋盘的尺寸和雷数量信息。

gamewindow.cpp中deleteBoard函数

遍历buttons,将每个按钮的空间释放,然后将buttons数组的空间释放。

void GameWindow::deleteBoard(int row_old, int col_old) { for (int i=0;i<row_old*col_old;i ) { delete buttons[i]; } QVector<CustomButton *> tmp; buttons.swap(tmp); }

gamewindow.cpp中“中级”按钮的槽函数

void GameWindow::on_pushButton_2_clicked() { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 11; COL = 11; ROWS = 13; COLS = 13; EASY_COUNT = 15; NOT_MINE = ROW * COL - EASY_COUNT; //因为初始化时会以全局变量为参数,所以在这里进行全局变量的修改 InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); }

在实现“自定义”按钮时,我原本希望点击按钮能显示一个可输入行、列、雷数据的附属窗口,但尝试实现时发现很麻烦,所以索性直接在GameWindow主窗口上添加了三个spinBox控件和一个按钮,将它们放在了“自定义”的下方。这些新控件在构造时调用hide()方式进行隐藏,点击“自定义”后使用show()显示,再点一下又进行隐藏。为了指示隐藏和显示的状态,在GameWindow类中添加bool类型成员is_custom(是否要进行自定义)

gamewindow.cpp中“自定义”按钮的槽函数

void GameWindow::on_pushButton_5_clicked() { if (is_custom) { ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); is_custom = false; return; } ui->label_2->show(); ui->label_3->show(); ui->label_4->show(); ui->spinBox->show(); ui->spinBox_2->show(); ui->spinBox_3->show(); ui->pushButton_7->show(); is_custom = true; }

自定义下“确定”按钮的槽函数

这里要保证棋盘格子的数量多于雷的数量,否则无法触发棋盘的重新生成。

void GameWindow::on_pushButton_7_clicked() { ui->label_5->setText(""); int Column = ui->spinBox->value(); //列数据,即棋盘的长 int Row = ui->spinBox_2->value(); //行数据,即棋盘的宽 int Mine = ui->spinBox_3->value(); //雷的数量 if ( Mine >= Column * Row) { ui->label_5->setText("雷的数量应该少于格子的数量!"); return; } is_custom = false; //后面的操作与尺寸更改按钮的基本一样 deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; COL = Column; ROW = Row; COLS = COL 2; ROWS = ROW 2; EASY_COUNT = Mine; NOT_MINE = ROW * COL - EASY_COUNT; not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); }(3)其他功能

gamewindow.cpp中“重新开始”按钮的槽函数

和尺寸更改按钮的槽函数差不多,只不过不用修改ROW、COL等全局变量,重新生成数组和棋盘即可。

void GameWindow::on_pushButton_restart_clicked() { InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); //由于棋盘的尺寸不变,无需释放数组的空间,调用Init进行初始化即可 SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); deleteBoard(ROW, COL); createBoard(); }

gamewindow.cpp中“退出游戏”按钮的槽函数

void GameWindow::on_pushButton_quit_clicked() { for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; deleteBoard(ROW, COL); QApplication::quit(); }

另外,为了在Qt中能打出中文,还需要在项目文件.pro中加上这样一段代码:

minesweeper.pro

msvc { QMAKE_CFLAGS = /utf-8 QMAKE_CXXFLAGS = /utf-8 }四、效果展示

五、完整代码1.CustomButton类

custombutton.h

#ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include "head.h" #include <QMouseEvent> class CustomButton : public QPushButton { public: CustomButton(QWidget *parent = nullptr); void mousePressEvent(QMouseEvent *event); int condition = 0; //表示方格的状态 0.未排查未标记 1.已排查 2.已标记 }; #endif // CUSTOMBUTTON_H

custombutton.cpp

#include "custombutton.h" CustomButton::CustomButton(QWidget *parent): QPushButton(parent) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); } void CustomButton::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { // 如果是右键点击,则修改按钮的样式 if (condition == 1) return; if (condition == 0) { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "border-image: url(:/icons/icons/note.png);" "}"); condition = 2; } else { setStyleSheet("QPushButton{" "background-color: #8FE1F3;" "border-style: solid;" "border-width: 2px;" "border-color: #71B2D2;" "}"); condition = 0; } } else { // 如果是其它的鼠标按键事件,则调用父类的鼠标按下事件处理方法 QPushButton::mousePressEvent(event); } }2.GameWindow类

gamewindow.h

#ifndef GAMEWINDOW_H #define GAMEWINDOW_H #pragma execution_character_set("utf-8") #include <QWidget> #include "head.h" #include "custombutton.h" namespace Ui { class GameWindow; } class GameWindow : public QWidget { Q_OBJECT public: explicit GameWindow(QWidget *parent = nullptr); ~GameWindow(); QVector<CustomButton *> buttons; bool is_custom = false; void createBoard(); void GameWindow::deleteBoard(int row_old, int col_old); public slots: void onButtonClicked(int index); private slots: void on_pushButton_restart_clicked(); void on_pushButton_quit_clicked(); void on_pushButton_2_clicked(); void on_pushButton_clicked(); void on_pushButton_6_clicked(); void on_pushButton_3_clicked(); void on_pushButton_4_clicked(); void on_pushButton_5_clicked(); void on_pushButton_7_clicked(); private: Ui::GameWindow *ui; }; #endif // GAMEWINDOW_H

gamewindow.cpp

#include "gamewindow.h" #include "ui_GameWindow.h" #include <QSignalMapper> GameWindow::GameWindow(QWidget *parent) : QWidget(parent), ui(new Ui::GameWindow) { ui->setupUi(this); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); } void GameWindow::createBoard() { for (int i=0;i<ROW;i ) for (int j=0;j<COL;j ) { CustomButton *button=new CustomButton(this); button->setGeometry(365-(double(COL)/2)*30 j*30,180 i*30,30,30); //为了控制棋盘生成的位置在窗口的中央,我联系窗口和棋盘的尺寸进行了计算 button->show(); buttons.append(button); QSignalMapper *mapper=new QSignalMapper(this); mapper->setMapping(button,i*COL j); connect(button, SIGNAL(clicked()), mapper, SLOT(map())); connect(mapper, SIGNAL(mapped(int)), this, SLOT(onButtonClicked(int))); } } void GameWindow::deleteBoard(int row_old, int col_old) { for (int i=0;i<row_old*col_old;i ) { delete buttons[i]; } QVector<CustomButton *> tmp; buttons.swap(tmp); } void GameWindow::onButtonClicked(int index) //棋盘按钮的点击 { if (buttons[index]->condition == 1 || buttons[index]->condition == 2)return; int x=0,y=0; x=index/COL; y=index-x*COL 1; x ; if (mine[x][y] == '1') { not_mine=0; this->ui->label_result->setText("对不起,你踩雷了。"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/sad.png);" "}"); reveal_All(mine, showl, this, ROW, COL); return; } else { Sweep(mine, showl, this, ROW, COL, x, y); if (not_mine == NOT_MINE) { not_mine=0; this->ui->label_result->setText("恭喜你,扫雷成功!"); this->ui->label_face->setStyleSheet("QLabel{" "border-image: url(:/icons/icons/cool.png);" "}"); return; } } } GameWindow::~GameWindow() { delete ui; } void GameWindow::on_pushButton_restart_clicked() //重新开始 { InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); deleteBoard(ROW, COL); createBoard(); } void GameWindow::on_pushButton_quit_clicked() //退出游戏 { for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; deleteBoard(ROW, COL); QApplication::quit(); } void GameWindow::on_pushButton_2_clicked() //“中级”按钮 { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 11; COL = 11; ROWS = 13; COLS = 13; EASY_COUNT = 15; NOT_MINE = ROW * COL - EASY_COUNT; InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_clicked() //“基础”按钮 { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 9; COL = 9; ROWS = 11; COLS = 11; EASY_COUNT = 10; NOT_MINE = ROW * COL - EASY_COUNT; InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_6_clicked() //“极简”按钮 { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 4; COL = 4; ROWS = 6; COLS = 6; EASY_COUNT = 5; NOT_MINE = ROW * COL - EASY_COUNT; InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_3_clicked() //“专家”按钮 { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 11; COL = 16; ROWS = 13; COLS = 18; EASY_COUNT = 25; NOT_MINE = ROW * COL - EASY_COUNT; InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_4_clicked() //“满屏”按钮 { deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; ROW = 11; COL = 22; ROWS = 13; COLS = 24; EASY_COUNT = 30; NOT_MINE = ROW * COL - EASY_COUNT; NOT_MINE = ROW * COL - EASY_COUNT; InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); createBoard(); } void GameWindow::on_pushButton_5_clicked() //“自定义”按钮 { if (is_custom) { ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); is_custom = false; return; } ui->label_2->show(); ui->label_3->show(); ui->label_4->show(); ui->spinBox->show(); ui->spinBox_2->show(); ui->spinBox_3->show(); ui->pushButton_7->show(); is_custom = true; } void GameWindow::on_pushButton_7_clicked() //自定义下的“确定”按钮 { ui->label_5->setText(""); int Column = ui->spinBox->value(); int Row = ui->spinBox_2->value(); int Mine = ui->spinBox_3->value(); if ( Mine >= Column * Row) { ui->label_5->setText("雷的数量应该少于格子的数量!"); return; } is_custom = false; deleteBoard(ROW, COL); for (int i = 0; i < ROWS; i) { delete[] mine[i]; delete[] showl[i]; } delete[] mine; delete[] showl; COL = Column; ROW = Row; COLS = COL 2; ROWS = ROW 2; EASY_COUNT = Mine; NOT_MINE = ROW * COL - EASY_COUNT; not_mine = 0; ui->label_result->setText(""); ui->label_face->setStyleSheet("border-image: url(:/icons/icons/smile.png);"); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); SetMine(mine, ROW, COL); createBoard(); ui->label_2->hide(); ui->label_3->hide(); ui->label_4->hide(); ui->spinBox->hide(); ui->spinBox_2->hide(); ui->spinBox_3->hide(); ui->pushButton_7->hide(); }3.head.h、function.cpp、main.cpp

head.h

#define _CRT_SECURE_NO_WARNINGS 1 extern int ROW; extern int COL; extern int ROWS; extern int COLS; extern int EASY_COUNT; extern int NOT_MINE; #include <QDebug> #include <QPushButton> #include <stdio.h> #include <stdlib.h> #include <time.h> extern int not_mine; extern char **mine; extern char **showl; class GameWindow; void InitBoard(char **&board, int rows, int cols, char set); void SetMine(char **board, int row, int col); void Sweep(char **mine, char **showl, GameWindow* w, int row, int col, int x, int y); void reveal_All(char **mine, char **show, GameWindow* w,int row, int col);

function.cpp

#include "head.h" #include "gamewindow.h" //棋盘的初始化,set表示用于填充的值 void InitBoard(char**& board, int rows, int cols, char set) { board = new char*[rows]; for (int i=0;i<rows;i ) board[i] = new char[cols]; for (int i = 0;i < rows; i ) for (int j = 0; j < cols; j ) { board[i][j] = set; } } //布置雷 void SetMine(char **board, int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row 1; int y = rand() % col 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } } //递归实现扫雷 void Sweep(char **mine, char **show, GameWindow* w,int row, int col, int x, int y) { int index=y-1 (x-1)*COL; if (show[x][y]!='*')return; int count = mine[x - 1][y - 1] mine[x - 1][y] mine[x - 1][y 1] mine[x][y - 1] mine[x][y 1] mine[x 1][y - 1] mine[x 1][y] mine[x 1][y 1] - '0' * 8; show[x][y]=count '0'; w->buttons[index]->setText(QString::number(count)); w->buttons[index]->setStyleSheet("QPushButton{" "font-size: 20px;" "background-color: #89CDEF;" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); w->buttons[index]->condition = 1; not_mine ; qDebug()<< not_mine; if (count == 0) { Sweep(mine, show, w, row, col, x - 1, y - 1); Sweep(mine, show, w, row, col, x - 1, y); Sweep(mine, show, w, row, col, x - 1, y 1); Sweep(mine, show, w, row, col, x, y - 1); Sweep(mine, show, w, row, col, x, y 1); Sweep(mine, show, w, row, col, x 1, y - 1); Sweep(mine, show, w, row, col, x 1, y); Sweep(mine, show, w, row, col, x 1, y 1); } } //踩雷时,棋盘应显示全部信息 void reveal_All(char **mine, char **show, GameWindow* w,int row, int col) { for (int i=1;i<=row;i ) for (int j=1;j<=col;j ) { int index=j-1 (i-1)*COL; if (mine[i][j]=='1') { w->buttons[index]->condition = 1; w->buttons[index]->setStyleSheet("QPushButton{" "background-color: #89CDEF;" "border-image: url(:/icons/icons/mine.png);" "border-style: solid;" "border-width: 1px;" "border-color: #71B2D2;" "}"); } else Sweep(mine, show, w, row, col, i, j); } }

main.cpp

#include "gamewindow.h" #include <QApplication> #include "head.h" char **mine; char **showl; int ROW = 9; int COL = 9; //棋盘实际的行和列 int ROWS = ROW 2; int COLS = COL 2; //用于存放棋盘信息的数组的行和列 //因为扫雷时要对周围八格进行检索,为了使边缘的方格行为与内部的一致,在数组棋盘的外围加一圈空白 int EASY_COUNT = 10;//雷的数量 int NOT_MINE = ROW * COL - EASY_COUNT;//不含雷的方格数量 int not_mine = 0;// 已排查的无雷方格数量 int main(int argc, char *argv[]) { QApplication a(argc, argv); srand((unsigned int)time(NULL)); InitBoard(mine, ROWS, COLS, '0'); InitBoard(showl, ROWS, COLS, '*'); //棋盘的初始化 SetMine(mine, ROW, COL);//雷的布置 GameWindow *w=new GameWindow; w->show(); return a.exec(); }4.gamewindow.ui

可以使用LayoutSpacer将界面规划得更工整

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved