关闭

扫雷

160人阅读 评论(0) 收藏 举报
分类:
做完了才返现其实也没用到多少东西,唯一的可能就是重写了一个鼠标点击事件,其他的基本是教材前几章基础部分的内容

用一个继承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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:7390次
    • 积分:32
    • 等级:
    • 排名:千里之外
    • 原创:76篇
    • 转载:0篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论
  • 学生管理系统

    qq_20553613: 不错,用txt方式模拟数据库。可以改用MySQL实现,加入搜索等功能;代码量和逻辑容易实现多了。另外...