扫雷

来源:互联网 发布:酒神淘宝店地址 编辑:程序博客网 时间:2024/06/06 14:16
做完了才返现其实也没用到多少东西,唯一的可能就是重写了一个鼠标点击事件,其他的基本是教材前几章基础部分的内容
用一个继承QPushButton的按钮来做地雷,在一个对话框上放置了15X15个用于来模拟地雷
扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我

雷周围的8个格子肯定为数字,所以空白格子周围的8个格子肯定不会为地雷

扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我
 
扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我
 
胜利条件为打开所有的空格(数字+空白)并且右键标记出所有的地雷(右键标记为?号)

扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我
然后这里做了个简单的难度选择,在选项菜单中选择不同难度,
简单10
中等 20
困难 30
~~~
在准备做这个游戏的时候其实我都没玩过一次胜利的扫雷(我猜你就算有空也不会去玩扫雷。。扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我
然后问题来了。。。
扫雷这个游戏有一个自动解开一定范围内空白的机制,在win自带的扫雷解开雷区(貌似)是一个四分递归,但由于在开始写的时候压根没考虑到这个事情,等到坐完才发现这个问题扫雷 - 板烧鱿鱼 - 我只是个路人甲,别在意我
这个游戏我采用了比较折(dan)中(teng)的2分递归,空白区域只会在左右自动打开,不会像上下自动打开,应为布局255个地雷的时候我用的0-224来编号这些地雷(按钮),同时进行诸如地雷判断,感觉一位数组用来4分递归有点复(bu)杂(hui)所以最后也就么该。。


另外游戏最右下角的那个按钮肯定不会是地雷,这样通过先点击这个按钮可以避免点击一次就游戏失败
 ~~~
http://pan.baidu.com/s/1kTtFjqZ
注意这里文件是我在linux(ubuntu14.04)下写的文件,在win系统下编译没问题,但打开可能会出现乱码.如果有这里问题,下面有源代码(PS:源代码里没有.pro文件的源代码)
这里pro文件写好了,编译的时候记得打开编译器的c++11选择(如果你没开的话)

~~~~~~以下是源代码~~~~~~
 GameEnd.cxx
#include"GameEnd.h"
#include<QVBoxLayout>
GameEnd::GameEnd(QWidget* parent):QDialog(parent)
{
con = true;
show_label = new QLabel;
show_label->setFixedSize(250,100);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(show_label);
setLayout(layout);
setWindowTitle(tr("Come On"));
setFixedSize(250,100); //设置窗体和标签一样大
}
void GameEnd::win_show()
{
con = true;
how_show();
}
void GameEnd::lost_show()
{
con = false;
how_show();
}
void GameEnd::how_show()
{
QPalette pt;
if(con == true) //显示胜率or失败,同时显示不同颜色
{
show_label->setText(tr("胜利!"));
pt.setColor(QPalette::WindowText,Qt::green);
}
else if(con == false)
{
show_label->setText(tr("失败!"));
pt.setColor(QPalette::WindowText,Qt::red);
}
QFont ft;
ft.setPointSize(35);
show_label->setFont(ft); //设置字体大小
show_label->setPalette(pt); //设置字体颜色
show_label->setAlignment(Qt::AlignCenter);//设置字体居中
exec();
}
void GameEnd::closeEvent(QCloseEvent*)
{
hide();//确保关闭窗口只是隐藏窗口
}


GameEnd.h

#ifndef GAMEEND_H_
#define GAMEEND_H_
#include<QDialog>
#include<QLabel>
#include<QCloseEvent>
class GameEnd:public QDialog
{
Q_OBJECT
private:
bool con;
QLabel* show_label;
public:
GameEnd(QWidget* parent = 0);//构造函数
void how_show();
protected:
void closeEvent(QCloseEvent*);
public slots:
void win_show(); //显示胜利or失败
void lost_show();
};
#endif

GameInstrcution.cxx

#include"GameInstruction.h"
#include<QHBoxLayout>
GameInstruction::GameInstruction(QWidget* parent):QDialog(parent)
{
instruction = new QTextEdit;
instruction->setFixedSize(500,500);
QString A(tr("1.游戏有三个不同难度等级,可在选项中选择."));
QString B(tr("2.使用右键正确标记出所有地雷且点开其余格子方可获得胜利."));
QString C(tr("3.在游戏创建后改变游戏难度,本局游戏难度不会改变."));
QString D(tr("警告/反作弊:在游戏过程中更改难度,本局游戏不会获得胜利!"));
instruction->append(A);
instruction->append(B);
instruction->append(C);
instruction->append(D);
instruction->setReadOnly(true); //谢绝编辑 :)
QHBoxLayout* main_layout = new QHBoxLayout;
main_layout->addWidget(instruction);
setLayout(main_layout);
setFixedSize(500,500);
setWindowTitle(tr("帮助"));
}
void GameInstruction::closeEvent(QCloseEvent*)
{
hide(); //不关闭窗口,只是影藏
}

GameInstruction.h

#include<QDialog>
#include<QTextEdit>
#include<QCloseEvent>
class GameInstruction:public QDialog
{
private:
QTextEdit* instruction;
public:
GameInstruction(QWidget* parent = 0);
protected:
void closeEvent(QCloseEvent*);
};

GameOption.h

#ifndef GAMEOPTION_H_
#define GAMEOPTION_H_
#include<QDialog>
#include<QPushButton>
#include<QGroupBox>
#include<QRadioButton>
#include<QCloseEvent>
class GameOption:public QDialog
{
Q_OBJECT
private:
int levels; //难度等级,1为10歌雷,2为20个,3为30个
QPushButton* ok_button;
QRadioButton* easy_button;
QRadioButton* mid_button;
QRadioButton* hard_button;
QGroupBox* group;
public:
GameOption(QWidget* parent = 0);
protected:
void closeEvent(QCloseEvent*);
private slots:
void chose_level(); //确定是那个难度被选中了
signals:
void new_level(int);
};
#endif


GameOption.cxx
#include"GameOption.h"
#include<QVBoxLayout>
#include<QHBoxLayout>
GameOption::GameOption(QWidget* parent):QDialog(parent)
{
levels = 1; //默认为简单
ok_button = new QPushButton(tr("确定"));
group = new QGroupBox(tr("难度设定"));
easy_button = new QRadioButton(tr("简单难度"));
easy_button->setChecked(true); //默认选中简单难度
mid_button = new QRadioButton(tr("中等难度"));
hard_button = new QRadioButton(tr("困难"));
//布局安装
QVBoxLayout* button_layout = new QVBoxLayout;
button_layout->addWidget(easy_button);
button_layout->addWidget(mid_button);
button_layout->addWidget(hard_button);
group->setLayout(button_layout);
QHBoxLayout* ok_layout = new QHBoxLayout;
ok_layout->addStretch();
ok_layout->addWidget(ok_button);
QVBoxLayout* main_layout = new QVBoxLayout;
main_layout->addWidget(group);
main_layout->addLayout(ok_layout);
setLayout(main_layout);
setWindowTitle(tr(" "));

connect(ok_button,SIGNAL(clicked()),this,SLOT(chose_level()));
connect(ok_button,SIGNAL(clicked()),this,SLOT(hide())); //点击确定,隐藏对话框
}
void GameOption::chose_level()
{
if(easy_button->isChecked())
levels = 1;
else if(mid_button->isChecked())
levels = 2;
else if(hard_button->isChecked())
levels = 3;
emit new_level(levels);
}
void GameOption::closeEvent(QCloseEvent*)
{
emit new_level(levels);
hide(); //关闭对话框只是影藏
}


mineButton.h

#ifndef MINEBUTTON_H_
#define MINEBUTTON_H_H
#include<QPushButton>
#include<QMouseEvent>
class mineButton:public QPushButton
{
Q_OBJECT
private:
int NO; //按钮的编号,0-224
bool right_click; //是否被右键标记,false=未标记,ture=已经标记,
int button_type; //确定按钮是地雷=0,1-8为数字,9=空白,未点击过初始状态为10
bool enable_click; //是否接受点击
public:
mineButton(int , QWidget* parent = 0); //构造函数
bool type_button()const; //返回按钮类型
bool is_click()const; //返回是否已经被左键点击过
void resetType(int i = 0); //设置按钮的类型
void like_click(); //设置按钮图片
void unclick(); //设置按钮不可被点击,放置游戏结束后再次点击按钮
protected:
void mousePressEvent(QMouseEvent*); //鼠标点击事件,设置图片
signals:
void return_style();
void find_mine(); //右键成功标记到地雷发射信号信号
void get_mine(); //左键点到地雷
void NO_button(int); //该信号告诉父窗体,显示该按钮周围的空白按钮
};
#endif

mineButton.cxx

#include"mineButton.h"
mineButton::mineButton(int i , QWidget* parent):QPushButton(parent)
{
NO = i; //设置每个按钮单独的编号
setFixedSize(50,50); //固定大小
}
void mineButton::mousePressEvent(QMouseEvent* event)
{
if(event->button() == Qt::LeftButton) //如果是地雷,显示地雷图片
{
if(button_type == 0 and enable_click == true)
{
emit get_mine(); //点到地雷了
setIcon(QIcon(":/images/bomber.png"));
}
if(button_type > 0 and button_type < 9 and enable_click == true) //如果是数字,则显示数字图片
{
QString name = ":/images/" + QString::number(button_type);
name = name + ".png";
setIcon(QIcon(name));
}
if(button_type == 9 and enable_click == true) //如果是空白,显示空白图片
{
setIcon(QIcon(":/images/cd.png"));
emit NO_button(NO); //无论点击数字还是空白,都发射信号
}
enable_click = false; //左键点击完后,不在接受点击
}
if(event->button() == Qt::RightButton)
{
if(right_click == false and enable_click == true) //如果尚未被右键标记
{
setIcon(QIcon(":/images/know.png"));
right_click = true; //已经被右键标记
}
else if(right_click == true and enable_click == true)
{
setIcon(QIcon(":/images/empty1.png"));
right_click = false; //取消右键标记
}
}
// if(right_click == true and button_type == 0) //右键标记到正确的地雷,发射信号
emit find_mine();
setIconSize(size());
QPushButton::mousePressEvent(event);
}
bool mineButton::type_button()const
{
bool TYPE = true;
if(right_click == true and button_type == 0) //如果按钮被右键标记且该按钮类型为地雷,返回true
TYPE = true;
else TYPE = false;
return TYPE;
}
void mineButton::resetType(int i)
{
button_type = i;
right_click = false;
enable_click = true; //初始画按钮的各项状态
setIcon(QIcon(":/images/empty.png"));
setIconSize(size());
}
void mineButton::like_click() //该函数用于模拟鼠标点击
{
if(enable_click == false) //如果已左键点击过,直接推出
return;
if(button_type == 0)
setIcon(QIcon(":/images/bomber.png"));
if(button_type > 0 and button_type < 9) //如果是数字,则显示数字图片
{
QString name = ":/images/" + QString::number(button_type);
name = name + ".png";
setIcon(QIcon(name));
}
if(button_type == 9) //如果是空白,显示空白图片
setIcon(QIcon(":/images/cd.png"));
setIconSize(size());
enable_click = false;
}
bool mineButton::is_click()const
{
bool TYPE = false;
if(enable_click == false)
TYPE = true; //已被点击
else TYPE = false;
return TYPE;
}
void mineButton::unclick()
{
enable_click = false;
}

mineGame.h

#ifndef MINEGAME_H_
#define MINEGAME_H_
#include"mineLand.h"
#include"GameEnd.h"
#include"GameOption.h"
#include"GameInstruction.h"
#include<QMainWindow>
#include<QAction>
#include<QMenu>
#include<QMenuBar>
class mineGame:public QMainWindow
{
Q_OBJECT
private:
QAction* begin_action;
QAction* option_action; //难度选项
QAction* close_action;
QAction* info_action; //游戏说明
QMenu* game_menu;
QMenu* help_menu;
QMenuBar* menus; //菜单栏
mineLand* mine_land; //游戏窗体
GameOption* options; //难度选择框
GameEnd* game_end; // 游戏结束对话框
GameInstruction* helps; //游戏帮助说明
public:
mineGame(QWidget* parent = 0); //构造函数
private slots:
void new_level(); //显示难度选择对话框
void show_help(); //显示帮助对话框
};
#endif

mineGame.cxx

#include"mineGame.h"
#include<QLayout>
mineGame::mineGame(QWidget* parent):QMainWindow(parent)
{
//设置中心窗体
mine_land = new mineLand;
setCentralWidget(mine_land);
//设置游戏说明对话框
helps = new GameInstruction(this);
//设置游戏难度选择对话框
options = new GameOption(this);
connect(options,SIGNAL(new_level(int)),mine_land,SLOT(set_level(int)));
//设置游戏结束对话框
game_end = new GameEnd(this);
connect(mine_land,SIGNAL(win()),game_end,SLOT(win_show()));
connect(mine_land,SIGNAL(lost()),game_end,SLOT(lost_show()));
//游戏菜单上的动作
begin_action = new QAction(tr("开始"),this);
begin_action->setStatusTip(tr("开始新游戏."));
connect(begin_action,SIGNAL(triggered()),mine_land,SLOT(new_game()));
option_action = new QAction(tr("选项"),this);
option_action->setStatusTip(tr("选择游戏难度."));
connect(option_action,SIGNAL(triggered()),this,SLOT(new_level()));
close_action = new QAction(tr("结束游戏"),this);
connect(close_action,SIGNAL(triggered()),this,SLOT(close()));
//help菜单上的动作
info_action = new QAction(tr("说明"),this);
info_action->setStatusTip(tr("胜利条件."));
connect(info_action,SIGNAL(triggered()),this,SLOT(show_help()));
//把动作添加到对于的菜单
game_menu = new QMenu(tr("游戏."),this);
game_menu->addAction(begin_action);
game_menu->addAction(option_action);
game_menu->addSeparator(); //加个分割线
game_menu->addAction(close_action);

help_menu = new QMenu(tr("信息"),this);
help_menu->addAction(info_action);
//把菜单+到菜单栏上
menus = menuBar();
menus->addMenu(game_menu);
menus->addMenu(help_menu);
//生成状态栏
statusBar();
layout()->setSizeConstraint(QLayout::SetFixedSize); //固定大小,谢绝拉伸
setWindowIcon(QIcon(tr(":/images/read.png")));
}
void mineGame::new_level()
{
options->exec();
}
void mineGame::show_help()
{
helps->exec();
}


mineLand.h

#ifndef MINELAND_H_
#define MINELAND_H_
#include<QDialog>
#include"mineButton.h"
class mineLand:public QDialog
{
Q_OBJECT
private:
int levels; //等级,分别有10,20,30三个等级
QList<mineButton*> mines; //放置255个mineButton
QList<int> type_button; //放置255个整数用于表示对应的button类型
int probability; //用于不同等级下生成地雷的概率
void nus(){probability = 225 / levels;}
public:
mineLand(QWidget* parent = 0); //构造函数
void create_mine(); //
signals:
void win();
void lost();
public slots:
void show_empty(int); //显示被点击按钮周围的空白按钮
void winner(); //标记出所有地雷
void loster(); //点到地雷,游戏结束
void new_game();
void set_level(int); //设置不同的雷数
};
#endif

mineLand.cxx

#include"mineLand.h"
#include<QGridLayout>
#include<ctime>
mineLand::mineLand(QWidget* parent):QDialog(parent)
{
levels = 10; //默认难度等级为最简单
for(int i = 0 ; i < 225 ; ++i)
mines.push_back(new mineButton(i)); //容器内安防255个地雷
QGridLayout* layout = new QGridLayout;
auto MINE = mines.begin();
for(int i = 0 ; i < 15 ; ++i)
{
for(int j = 0 ; j < 15 ; ++j)
{
layout->addWidget(*MINE,i,j); //安装225个地雷
connect(*MINE,SIGNAL(NO_button(int)),this,SLOT(show_empty(int))); //建立连接,点击空白区域,自动展开雷区
connect(*MINE,SIGNAL(find_mine()),this,SLOT(winner())); //标记出所有地雷
connect(*MINE,SIGNAL(get_mine()),this,SLOT(loster())); //点到地雷
++MINE;
}
}
layout->setSpacing(0);
setLayout(layout);
type_button.push_back(100); //初始化容器
create_mine();
setWindowIcon(QIcon(":/images/bomber.jpg"));
}
void mineLand::create_mine()
{
srand(unsigned(time(0))); //制作随机数种子a
if(!type_button.isEmpty())
type_button.clear(); //清空数组
for(int i = 0 ; i < 225 ; ++i)
type_button.push_back(9);
nus(); //生成对应等级的概率

//第一步:在225个中生成level*15个地雷
auto pt = type_button.begin();
int buttons = 0; //地雷计数
do{
for(int i = 0 ; i < 224 ; ++i) //这里设定最后一个格子肯定不会是类
{
int nu = rand()%probability;
if(nu == 1 and (*(pt+i)) != 0) //随即到莫个数。且这个数不为9(地雷)
{
++buttons;//雷数目+1
*(pt+i) = 0; //生成了一个雷
}
if(buttons == levels)//如果一个循环内生成的地雷>levels*20,则立刻推出循环
break;
}
}while(buttons < levels);
//第二部,在每个按钮周边如果有N个雷,就生成数字N
for(int i = 0 ; i < 225 ; ++i)
{
int j = i+1;
int numbers = 0; //代表周围地雷数目
if(j%15 != 1 and j-16 > 0) //先判断左上角位于坐标内
{
if(*(pt+i-16) == 0) //在判断左上角是否为地雷
++numbers;
}
if(j-15 > 0) //上方
{
if(*(pt+i-15) == 0)
++numbers;
}
if(j%15 != 0 and j-14 > 0) //右上
{
if(*(pt+i-14) == 0)
++numbers;
}
if(j%15 != 1 and j-1 > 0) //左边
{
if(*(pt+i-1) == 0)
++numbers;
}
if(j%15 != 0 and j+1 <225)//右边
{
if(*(pt+i+1) == 0)
++numbers;
}
if(j%15 != 1 and j+14 < 225) //左下
{
if(*(pt+i+14) == 0)
++numbers;
}
if(j+15 < 225) //下方
{
if(*(pt+i+15) == 0)
++numbers;
}
if(j%15 != 0 and j+16 < 225) //右下
{
if(*(pt+i+16) == 0)
++numbers;
}
if((*(pt+i)) != 0 and numbers != 0) //如果自身不是雷且周围有雷,则设为数字
*(pt+i) = numbers;
}
//第三步,把类型和地雷关联
auto BUTTON = mines.begin();
for(int i = 0 ; i < 225 ; ++i)
{
int m = *(pt+i);
(*(BUTTON+i))->resetType(m);
}
}
void mineLand::show_empty(int nu)
{
if(nu < 0 or nu >224 or (*(mines.begin()+nu))->is_click())
return; //越界就停止递归
if((*(type_button.begin() + nu)) > 0 and (*(type_button.begin() + nu)) < 9)
{
(*(mines.begin() + nu))->like_click(); //如果是数字则停止搜索
return;
}
if((*(type_button.begin() + nu)) == 9)
{
(*(mines.begin() + nu))->like_click();
show_empty(nu-1);
show_empty(nu+1);
}
}
void mineLand::winner()
{
int real_mine = 0;
int fake_mine = 0;
for(auto A : mines)
{
if(A->type_button() == true)
++real_mine;
if(A->is_click() == true)
++fake_mine;
}
if(real_mine == levels and real_mine+fake_mine == 225)//胜利条件为所有地雷正确标出,且其他都点击过
{
emit win();
for(auto A : mines)
A->unclick(); //游戏胜利后放置左键点击地雷造成重复判断
}
}
void mineLand::new_game()
{
for(auto A : mines)
A->resetType(10); //所有按钮都重置为初始状体
create_mine(); //重置地雷
}
void mineLand::loster()
{
for(auto A : mines)
A->like_click(); //失败后所有按钮全部点开
emit lost();
}
void mineLand::set_level(int i)
{
levels = i*10;
}

main.cxx

#include"mineGame.h"
#include<QApplication>
int main(int argc , char** argv)
{
QApplication app(argc,argv);
mineGame B;
B.show();
return app.exec();
}

pix.qrc

<RCC>
<qresource>
<file>images/bomber.png</file>
<file>images/bomber.jpg</file>
<file>images/empty.png</file>
<file>images/empty1.png</file>
<file>images/know.png</file>
<file>images/read.png</file>
<file>images/2.png</file>
<file>images/1.png</file>
<file>images/3.png</file>
<file>images/4.png</file>
<file>images/5.png</file>
<file>images/6.png</file>
<file>images/7.png</file>
<file>images/8.png</file>
<file>images/9.png</file>
<file>images/cd.png</file>
</qresource>
</RCC>

0 0
原创粉丝点击