C语言+easyX带你实现:扫雷游戏(六边形升级版)!

描述

每天一个编程小项目,提升你的编程能力!

程序简介

扫雷游戏升级版!六边形扫雷(寻宝模式)稍稍介绍一下哈~

他也是要把所有安全的地方点出来。

他没有扫雷模式的消零算法。每一个安全的点都需要单独挖出来,一次显示一个格子。

添加了生命值的概念,也就是说存在一定的容错。

显示的数字有别于扫雷模式。点击宝藏点,会显示周围宝藏点数量,绿色;点击地雷,会显示周围地雷数量,黑色。注意,这个数字不包括自己,显示的范围自然就是 0~6 了。点击地雷会减生命值,生命值归零则结束。

所以雷和宝藏都是有价值的,都是能给准确信息的。

我能给一个参考难度:占总格子数 40%的地雷,占总地雷数 50 %的生命值。

【注:需要编译器+图形库插件可以在文末领取】

程序运行展示

编程

简单了解游戏后我们就来试试吧!(直接上源码,大家可以看注释)

 

////////////////////////////////////////
// 程序:六边形扫雷:寻宝模式


# include 
# include 
# include 
# include 


static double pi = acos (-1.0);      // 圆周率 π
static HWND hOut;            // 画布


// 定义一个结构体,按钮
struct Node1
{
  int posx1, posy1, posx2, posy2;    // 坐标
  LPTSTR text;            // 文字
  int mod;              // 状态
};


// 定义一个结构体,六边形格子
struct Node2
{
  int i, j, k;            // 特征值
  int mod_life;            // 翻开
  int mod_mine;            // 雷
  int mod_flag;            // 标记
  int posx, posy;            // 坐标
  int num_mine;            // 周围雷数
  int num_peace;            // 周围空地块
};


// 定义一个类
class Gary
{
public:
  void carry ();            // 主进程
  void initialization ();        // 初始化
  void draw_scene ();          // 绘制界面函数
  void draw_box (int num_box);    // 绘制格子
  void draw_flag (int num_box);    // 绘制标记
  void draw_num (int num_box, int num);  // 绘制数字
  void move ();            // 窗口主视角
  void create ();            // 地雷生成
  void check_over ();          // 结束判定


  int num_button;            // 按钮数量参数
  int exit_carry;            // 主循函数控制参数
  int exit_move;            // 开始界面控制参数
  int exit_game;            // 游戏进行控制参数
  int num_life;            // 生命值
  int num_size;            // 边长
  int num_mine;            // 总雷数
  int num_box;            // 总地块数
  int num_flag;            // 标记数
  COLORREF color_text[2];        // 按钮绘制填充
  Node1 boxm[30];            // 按钮,预制 30 个
  Node2 box[1000];          // 地块
};


// 标记绘制函数
void Gary::draw_flag (int num_box)
{
  setlinestyle (PS_SOLID, 1);
  setlinecolor (BLACK);
  line (box[num_box].posx + 2, box[num_box].posy + 7, box[num_box].posx + 2, box[num_box].posy - 7);
  setfillcolor (LIGHTRED);
  setlinecolor (LIGHTRED);
  fillrectangle (box[num_box].posx - 7 + 2, box[num_box].posy - 7, box[num_box].posx + 2, box[num_box].posy - 1);
}


// 数字绘制函数
void Gary::draw_num (int num_box, int num)
{
  int i;
  // 画六边形,格子处于点击后状态
  setfillcolor (RGB (170, 170, 170));
  setlinecolor (RGB (85, 85, 85));
  POINT pts[6];
  setlinestyle (PS_SOLID, 1);
  for (i = 0; i < 6; i++)
  {
    pts[i].x = long(box[num_box].posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
    pts[i].y = long(box[num_box].posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
  }
  fillpolygon (pts, 6);


  // 数字绘制
  TCHAR s[15];
  settextstyle (20, 0, _T ("Consolas"));
  _stprintf_s (s, _T ("%0.1d"), num);
  outtextxy (box[num_box].posx - 5, box[num_box].posy - 10, s);
}


// 场景绘制函数
void Gary::draw_scene ()
{
  TCHAR s[15];
  int i, j;
  setlinecolor (BLACK);
  setfillcolor (WHITE);
  setlinestyle (PS_SOLID, 1);
  // 主界面
  fillrectangle (401, 0, 650, 400);
  // 根据按钮数量绘制  
  settextcolor (BLACK);
  for (i = 0; i < num_button; i++)
  {
    setfillcolor (color_text[boxm[i].mod]);
    setbkcolor (color_text[boxm[i].mod]);
    // 边框
    fillrectangle (boxm[i].posx1, boxm[i].posy1, boxm[i].posx2, boxm[i].posy2);
    // 文字
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + 4, boxm[i].text);
  }


  // 设置参数
  setbkcolor (WHITE);
  settextcolor (BLACK);
  setlinecolor (BLACK);


  // 变量绘制
  j = 25;
  // 生命值
  i = 1;
  setbkcolor (color_text[boxm[i].mod]);
  _stprintf_s (s, _T ("%0.1d"), num_life);
  outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
  // 边长
  i = 2;
  setbkcolor (color_text[boxm[i].mod]);
  _stprintf_s (s, _T ("%0.1d"), num_size);
  outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
  // 总地雷数
  i = 3;
  setbkcolor (color_text[boxm[i].mod]);
  _stprintf_s (s, _T ("%0.1d"), num_mine);
  outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
  // 格子
  i = 4;
  setbkcolor (color_text[boxm[i].mod]);
  _stprintf_s (s, _T ("%0.1d"), num_box);
  outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
  // 标记数
  i = 5;
  setbkcolor (color_text[boxm[i].mod]);
  _stprintf_s (s, _T ("%0.1d"), num_flag);
  outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);


  FlushBatchDraw ();
}


// 地雷生成函数
void Gary::create ()
{
  int i, j;
  // 设置雷
  for (i = 0; i < num_mine; i++)
  {
    // 随机
    j = rand () % 1000;
    while (box[j].mod_mine == 1 || box[j].mod_life == 1)
    {
      // 随机
      j = rand () % 1000;
    }
    // 是雷
    box[j].mod_mine = 1;
  }
  // 周边雷数统计
  // 遍历
  for (i = 0; i <= 888; i++)
  {
    if (box[i].mod_life == 0)
    {
      // 遍历
      for (j = 0; j <= 999; j++)
      {
        // 排除自己
        if (j != i && box[j].mod_life == 0)
        {
          // 周围六个
          if ((box[j].posx - box[i].posx) * (box[j].posx - box[i].posx) + (box[j].posy - box[i].posy) * (box[j].posy - box[i].posy) <= 900)
          {
            // 是雷
            if (box[j].mod_mine == 1)
            {
              // 周边雷数参数加一
              box[i].num_mine++;
            }
            // 不是雷
            else if (box[j].mod_mine == 0)
            {
              // 周边安全数参数加一
              box[i].num_peace++;
            }
          }
        }
      }
    }
  }
}


// 结束判断函数
void Gary::check_over ()
{
  int i, k;
  k = 0;
  for (i = 0; i <= 888; i++)
  {
    // 每有一个翻开且不是雷的点,则加一
    if (box[i].mod_mine == 0 && box[i].mod_life == 1)
    {
      k++;
    }
  }
  // 全翻开则结束
  if (k == num_box - num_mine)
  {
    // 将所有未翻开雷做上标记
    for (i = 0; i <= 888; i++)
    {
      if (box[i].mod_mine == 1 && box[i].mod_life == 0)
      {
        draw_flag (i);
      }
    }
    // 胜利标志:笑脸
    setfillcolor (WHITE);
    setlinecolor (WHITE);
    fillrectangle (50, 20, 75, 45);
    settextstyle (30, 0, _T ("Wingdings"));
    setbkmode (TRANSPARENT);
    settextcolor (BLACK);
    outtextxy (50, 20, 0x4A);
    setbkmode (OPAQUE);
    settextstyle (20, 0, _T ("Consolas"));
    // 结束变化
    exit_game = 1;
    boxm[1].mod = 0;
    boxm[2].mod = 0;
    boxm[3].mod = 0;
    boxm[6].mod = 0;
    boxm[7].mod = 1;
    num_flag = 0;
    // 绘制
    draw_scene ();
  }
}


// 格子绘制函数
void Gary::draw_box (int num_box)
{
  int i;
  int posx, posy;


  // 六边形绘制
  posx = box[num_box].posx;
  posy = box[num_box].posy;


  POINT pts[6];
  setlinestyle (PS_SOLID, 2);
  // 背景色
  setfillcolor (RGB (255, 255, 255));
  for (i = 0; i < 6; i++)
  {
    pts[i].x = long(posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
    pts[i].y = long(posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
  }
  solidpolygon (pts, 6);
  // 灰边
  setlinecolor (RGB (85, 85, 85));
  line (pts[0].x, pts[0].y, pts[1].x, pts[1].y);
  line (pts[5].x, pts[5].y, pts[0].x, pts[0].y);
  line (pts[1].x, pts[1].y, pts[2].x, pts[2].y);
  // 前景色
  setfillcolor (RGB (170, 170, 170));
  for (i = 0; i < 6; i++)
  {
    pts[i].x = long(posx + 12.0 * cos (60.0 * double (i) * pi / 180.0));
    pts[i].y = long(posy + 12.0 * sin (60.0 * double (i) * pi / 180.0));
  }
  solidpolygon (pts, 6);
  FlushBatchDraw ();
}


// 初始化函数
void Gary::initialization ()
{
  int i, j, k, t;
  // 随机初始化
  srand ((unsigned)time (NULL));
  // 颜色初始化
  color_text[0] = WHITE;
  color_text[1] = RGB (170, 170, 170);


  // 按钮的初始化
  num_button = 10;


  // 坐标
  for (i = 0; i < 10; i++)
  {
    boxm[i].posx1 = 410 + 120 * (i % 2);
    boxm[i].posy1 = 25 + 75 * (i / 2);
    boxm[i].posx2 = 520 + 120 * (i % 2);
    boxm[i].posy2 = 75 + 75 * (i / 2);
  }


  // 内容
  boxm[0].text = _T ("寻宝模式");  boxm[1].text = _T ("生命值");
  boxm[2].text = _T ("地图边长");  boxm[3].text = _T ("总地雷数");
  boxm[4].text = _T ("总地块数");  boxm[5].text = _T ("已标记数");
  boxm[6].text = _T ("开始");    boxm[7].text = _T ("重置");
  boxm[8].text = _T ("截图");    boxm[9].text = _T ("退出");


  // 状态
  boxm[0].mod = 1;
  boxm[1].mod = 1;
  boxm[2].mod = 1;
  boxm[3].mod = 1;
  boxm[4].mod = 1;
  boxm[5].mod = 1;
  boxm[6].mod = 1;
  boxm[7].mod = 0;
  boxm[8].mod = 0;
  boxm[9].mod = 0;


  num_box = 3 * num_size * (num_size - 1) + 1;
  num_flag = 0;


  // 绘制参数初始化
  setlinecolor (BLACK);
  setlinestyle (PS_SOLID, 1);
  settextstyle (20, 0, _T ("Consolas"));
  // 第一次绘制
  draw_scene ();


  // 重置
  setfillcolor (WHITE);
  fillrectangle (0, 0, 400, 400);


  // 平静脸
  setfillcolor (WHITE);
  setlinecolor (WHITE);
  fillrectangle (50, 20, 75, 45);
  settextstyle (30, 0, _T ("Wingdings"));
  setbkmode (TRANSPARENT);
  settextcolor (BLACK);
  outtextxy (50, 20, 0x4B);
  setbkmode (OPAQUE);
  settextstyle (20, 0, _T ("Consolas"));


  // 格子初始化
  for (t = 0; t <= 999; t++)
  {
    // 已翻开
    box[t].mod_life = 1;
    // 城墙
    box[t].mod_mine = 2;
    // 坐标,点不到
    box[t].posx = -200;
    box[t].posy = -200;
  }


  // 初始化
  for (i = 0; i < num_size; i++)
  {
    for (j = 0; j < num_size; j++)
    {
      for (k = 0; k < num_size; k++)
      {
        // 特征值至少一个为零
        if (i == 0 || j == 0 || k == 0)
        {
          // 编号
          t = i * 100 + j * 10 + k;
          // 特征值
          box[t].i = i;
          box[t].j = j;
          box[t].k = k;
          // 未翻开
          box[t].mod_life = 0;
          // 不是雷
          box[t].mod_mine = 0;
          // 未标记
          box[t].mod_flag = 0;
          // 坐标
          box[t].posx = 200 + 22 * (j - k);
          box[t].posy = 200 - 25 * i + 13 * (j + k);
          // 周围雷数初始化
          box[t].num_mine = 0;
          box[t].num_peace = 0;
          // 绘制地块
          draw_box (t);
        }
      }
    }
  }
  // 地雷生成函数
  create ();
}


// 窗口主视角函数,获取用户操作
void Gary::move ()
{
  // 鼠标定义
  ExMessage m;
  TCHAR ss[15];
  int i, t;
  exit_move = 0;
  exit_game = 0;
  while (exit_move == 0)
  {
    // 鼠标信息
    if (peekmessage (&m, EM_MOUSE | EM_KEY))
    {
      // 左键单击判断
      if (m.message == WM_LBUTTONDOWN)
      {
        // 判断是否点击了格子
        if (m.x > 0 && m.y > 0 && m.x < 400 && m.y < 400 && exit_game == 0)
        {
          for (t = 0; t <= 888; t++)
          {
            // 成功点击未标记的空格子
            if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0 && box[t].mod_flag == 0)
            {
              // 点击的格子不是雷
              if (box[t].mod_mine == 0)
              {
                // 绿色,安全,绘制
                settextcolor (LIGHTGREEN);
                draw_num (t, box[t].num_peace);
                // 改为翻开
                box[t].mod_life = 1;
              }
              // 点击的格子雷
              else if (box[t].mod_mine == 1)
              {
                // 扣除生命值
                num_life--;
                // 黑色,危险,绘制
                settextcolor (BLACK);
                draw_num (t, box[t].num_mine);
                // 改为翻开
                box[t].mod_life = 1;
                // 生命值减为零
                if (num_life <= 0)
                {
                  // 失败标志:哭脸
                  setfillcolor (WHITE);
                  setlinecolor (WHITE);
                  fillrectangle (50, 20, 75, 45);
                  settextstyle (30, 0, _T ("Wingdings"));
                  setbkmode (TRANSPARENT);
                  settextcolor (BLACK);
                  outtextxy (50, 20, 0x4C);
                  setbkmode (OPAQUE);
                  settextstyle (20, 0, _T ("Consolas"));
                  // 失败
                  exit_game = 1;
                  boxm[1].mod = 0;
                  boxm[2].mod = 0;
                  boxm[3].mod = 0;
                  boxm[6].mod = 0;
                  boxm[7].mod = 1;
                  num_flag = 0;
                }
                // 绘制
                draw_scene ();
              }
              // 成功结束判断
              check_over ();
              break;
            }
          }
        }


        // 判断是否点击了可点击按钮
        for (i = 0; i < num_button; i++)
        {
          if (m.x > boxm[i].posx1 && m.y > boxm[i].posy1 && m.x < boxm[i].posx2 && m.y < boxm[i].posy2 && boxm[i].mod == 0)
          {
            break;
          }
        }


        // 点击矩形按钮
        switch (i)
        {
        // 生命值:num_life
        case 1:
        {
          // 输入
          InputBox (ss, 10, _T ("输入生命值(1 ~ 999)"));
          _stscanf_s (ss, _T ("%d"), &i);
          if (i > 0 && i <= 999)
          {
            num_life = i;
          }
          else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
          // 绘制
          draw_scene ();
          break;
        }
        // 地图边长:num_size
        case 2:
        {
          // 输入
          InputBox (ss, 10, _T ("输入边长(2 ~ 8)"));
          _stscanf_s (ss, _T ("%d"), &i);
          if (i > 1 && i <= 8)
          {
            num_size = i;
            num_box = 3 * num_size * (num_size - 1) + 1;
          }
          else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
          // 绘制
          draw_scene ();
          break;
        }
        // 总地雷数:num_mine
        case 3:
        {
          InputBox (ss, 10, _T ("输入地雷数(1 ~ 总格子数)"));
          _stscanf_s (ss, _T ("%d"), &i);
          if (i > 0 && i < num_box)
          {
            num_mine = i;
          }
          else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
          // 绘制
          draw_scene ();
          break;
        }
        // 开始
        case 6:
        {
          num_box = 3 * num_size * (num_size - 1) + 1;
          if (num_mine < num_box && num_life > 0)
          {
            exit_game = 0;
            // 初始化
            initialization ();
          }
          else
          {
            MessageBox (hOut, _T ("请将雷数修改为小于格子数或将生命值修改为大于零"), _T ("来自小豆子的提醒"), MB_OK);
          }
          break;
        }
        // 重置
        case 7:
        {
          // 结束游戏进程,进入准备阶段
          if (exit_game == 0)
          {
            exit_game = 1;
            boxm[1].mod = 0;
            boxm[2].mod = 0;
            boxm[3].mod = 0;
            boxm[6].mod = 0;
            boxm[7].mod = 1;
            num_flag = 0;
            // 绘制
            draw_scene ();
          }
          break;
        }
        // 截图
        case 8:
        {
          saveimage (_T ("image.png"));
          break;
        }
        // 退出
        case 9:
        {
          exit_game = 1;
          exit_move = 1;
          exit_carry = 1;
          break;
        }
        default:break;
        }
      }
      // 右键,且处于游戏进行状态
      else if (m.message == WM_RBUTTONDOWN && exit_game == 0)
      {
        for (t = 0; t <= 888; t++)
        {
          // 成功点击空格子
          if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0)
          {
            // 标记状态转换
            box[t].mod_flag = (box[t].mod_flag == 0 ? 1 : 0);
            // 绘制
            draw_box (t);
            // 画小旗子
            if (box[t].mod_flag == 1)
            {
              draw_flag (t);
              num_flag++;
            }
            else
            {
              num_flag--;
            }
            // 绘制
            draw_scene ();
          }
        }
      }
    }
  }
}


// 主进程
void Gary::carry ()
{
  // 窗口定义
  hOut = initgraph (651, 401);
  SetWindowText (hOut, _T ("六边形扫雷:扫雷模式"));
  // 参数初始化
  num_size = 5;
  num_mine = 10;
  num_life = 3;
  // 背景绘制
  setbkcolor (WHITE);
  cleardevice ();
  // 进程控制
  exit_carry = 0;
  while (exit_carry == 0)
  {
    initialization ();
    move ();
  }
  closegraph ();
}


// 主函数
int main (void)
{
  Gary G;
  G.carry ();
  return 0;
}





 

大家赶紧去动手试试吧!

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分