扫雷

    xiaoxiao2021-03-25  98

    做完了才返现其实也没用到多少东西,唯一的可能就是重写了一个鼠标点击事件,其他的基本是教材前几章基础部分的内容

    用一个继承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 != 1 and j-16 > 0) //先判断左上角位于坐标内 { if(*(pt+i-16) == 0) //在判断左上角是否为地雷 ++numbers; } if(j-15 > 0) //上方 { if(*(pt+i-15) == 0) ++numbers; } if(j != 0 and j-14 > 0) //右上 { if(*(pt+i-14) == 0) ++numbers; } if(j != 1 and j-1 > 0) //左边 { if(*(pt+i-1) == 0) ++numbers; } if(j != 0 and j+1 <225)//右边 { if(*(pt+i+1) == 0) ++numbers; } if(j != 1 and j+14 < 225) //左下 { if(*(pt+i+14) == 0) ++numbers; } if(j+15 < 225) //下方 { if(*(pt+i+15) == 0) ++numbers; } if(j != 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>

    转载请注明原文地址: https://ju.6miu.com/read-15085.html

    最新回复(0)