扫雷小游戏(C语言)

简介: 扫雷小游戏(C语言)

1.游戏简介

《扫雷》是一款大众类的益智小游戏。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。

2.扫雷基本逻辑

  1. 生成雷区
  2. 打印棋盘
  3. 玩家扫雷
  4. 玩家标记雷
  5. 判断输赢

2.1 打印棋盘

2.1.1棋盘初始化

棋盘的形式可以用二维数组来表示,要先创建一个9*9的二维数组并初始化

keys:为了后面程序书写方便,我们用两个11*11的二维数组来表示雷区和玩家界面,用11*11方便计算坐标周围八个位置雷的数量,用mine[][]表示雷区,用show[][]表示玩家界面.

代码如下:`

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char flag)
{
  for (int i = 0; i < rows; i++)
  {
    for (int j = 0; j < cols; j++)
    {
      board[i][j] = flag;
    }
  }
}

2.1.2展示棋盘

初始化完成后,我们需要将棋盘展示给玩家,玩家界面用’ * '表示,该位置没有被排除.

代码如下:

//展示棋盘
void DisBoard(char board[ROWS][COLS], int row, int col)
{
  int i = 0;
  int j = 0;
  printf("********扫雷*******\n");
  for (i = 0; i <= row; i++)
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d ", i);
    for (j = 1; j <= row; j++)
    {
      printf("%c ", board[i][j]);
    }
    printf("\n");
  }
}

初始化后的玩家界面如下图所示:

2.2 生成雷区

每次游玩游戏之前都要随机生成雷区,我们需要随机生成扫雷的坐标,因此可以用rand()函数随机生成x,y坐标;并且用字符’1’代表雷区,字符’0’代表安全区.

keys:在正确使用rand()之前,要在main()函数里使用srand()函数来保证坐标随机性.

下面为生成雷区的代码:

//生成雷区 '1'为雷
void Mine(char board[ROWS][COLS], int row, int col)
{
  //x,y为随机生成坐标
  int x = 0;
  int y = 0;
  int count = 0;   //记录生成雷的数量
  while (1)
  {
    x = (rand() % row) + 1;
    y = (rand() % col) + 1;
    if (board[x][y] == '0')//该坐标没有雷,生成雷
    {
      board[x][y] = '1';
      count++;
      if (count == GAMELEVEL)
      {
        break;
      }
    }
  }
}`

效果如下图所示**:1代表雷,0代表安全区**

3. 玩家扫雷

玩家扫雷主要分为五个步骤:

  1. 接收玩家扫雷的坐标.
  2. 判断该位置是否为雷,若是雷,踩雷,游戏结束
  3. 该位置不是雷,将该位置展开,并计算周围雷区的数量
  4. 若周围没有雷,自动展开周围位置.
  5. 实现标记雷

3.1 接收坐标

由于我们用11*11二维数组,导致第0行,第10行,没有用到,1~9行棋盘恰好为我们输入坐标的逻辑.因此直接接收坐标即可.

3.2 判断是否踩雷

接收坐标(x,y)后,我们用mine[x][y],去判断是不是雷,然后在show[x][y]展开,如果踩雷游戏直接结束.

3.3 计算周围雷数量

用遍历的方法,计算坐标(x,y)的数量,并且将数量标记在show[x][y],用数量来推测雷的位置.

3.4自动展开

若排查坐标(x,y)周围没有雷,则将周围坐标展开,并再次判断周围坐标的四周有没有雷,有雷则不展开.没有雷,再次将其四周展开.因此我们可以用递归的逻辑实现这个功能.

3.5 标记雷(‘!’)

为了方便我们后续继续排雷,我们支持标记雷的功能,输入(-x,-y),即可标记雷.

3.6代码逻辑

//玩家排雷逻辑
void Player(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
  //输入坐标
  int x = 0;
  int y = 0;
  int protect = 1;
  while (1)
  {
    printf("请输入需要排雷的坐标: ");
    scanf("%d %d", &x, &y);
    while (getchar()!='\n');  
    //判断坐标是否合法
    if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
    {
      //该位置没有被排查过
      if (show[x][y] == '*')
      {
        //该位置有雷
        if (mine[x][y] == '1')
        {
          if (protect==1)
          {
            printf("你已踩雷,请再次排雷!");
            continue;
          }
          else
          {
            DisBoard(mine, ROW, COL);//打印雷区
            printf("很遗憾,你被炸死!\n");
            break;
          }
        }
        else //该位置没有雷,自动排雷
        {
          protect = 0;
          AutoFindMine(show, mine, x, y);
          if (IsWin(show, mine, ROW, COL)) {
            system("cls");
            DisBoard(show, ROW, COL);     //打印棋盘
          }
          else {
            DisBoard(mine, ROW, COL);     //打印雷区
            printf("恭喜你,找到了所有的雷!\n");
            break;
          }
        }
      }
      else  //该位置被排查过
      {
        printf("该位置已经被排查过,请重输!\n");
        continue;
      }
    }
    else if((-x >= 1 && -x <= row) && (-y >= 1 && -y <= col))  //标记
    {
      MarkMine(show,mine ,-x, -y);
    }
    else {
      printf("输入坐标不在范围内,请重输!\n");
    }
  }
}
//计算雷的数量
int FindMineNum(char mine[ROWS][COLS], int x, int y)
{
  return (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);
}
//自动翻开周围雷区
void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
  //坐标传进来,排八个坐标
  if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
  {
    //没有被排查过
    //if (show[x][y] == '*')
    //{
      int ret = FindMineNum(mine, x, y);
      if (ret == 0) //该坐标周围没雷,自动翻开用‘0’表示
      {
        show[x][y] = ' ';
      }
      else
      {
        show[x][y] = '0' + ret;
        return;
      }
    //}
    if (show[x - 1][y - 1] == '*')
    {
      AutoFindMine(show, mine, x - 1, y - 1);
    }
    if (show[x - 1][y] == '*')
    {
      AutoFindMine(show, mine, x - 1, y);
    }
    if (show[x - 1][y] == '*')
    {
      AutoFindMine(show, mine, x - 1, y);
    }
    if (show[x][y-1] == '*')
    {
      AutoFindMine(show, mine, x, y - 1);
    }
    if (show[x][y + 1] == '*')
    {
      AutoFindMine(show, mine, x, y + 1);
    }
    if (show[x+1][y - 1] == '*')
    {
      AutoFindMine(show, mine, x + 1, y - 1);
    }
    if (show[x + 1][y] == '*')
    {
      AutoFindMine(show, mine, x + 1, y);
    }
    if (show[x + 1][y+1] == '*')
    {
      AutoFindMine(show, mine, x + 1, y + 1);
    }
  }
//雷区标记函数   '!'标记
void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
  //该位置为 * 才可以标记为雷
  int mark_count = GAMELEVEL;
  int mark = 0;
  printf("1:标记雷\n");
  printf("0:取消标记雷\n");
  printf("请输入1/0: ");
  scanf("%d", &mark);
  switch (mark)
  {
    case 1:
    {
      if (show[x][y] == '*')//未排过雷
      {
        if (mark_count != 0) //标记数小于雷数
        {
          show[x][y] = '!';
          mark_count--;
          DisBoard(show, ROW, COL);
        }
        else {
          printf("已经标记%d个雷,不能继续标记!", GAMELEVEL);
        }
      }
      else
      {
        printf("该位置已经被排查,不能标记!\n");
      }
      break;
    }
    case 0:
    {
      if (show[x][y] == '!')
      {
        show[x][y] = '*';
        mark_count++;
        DisBoard(show, ROW, COL);
      }
      else {
        printf("该位置不能被取消,请重新标记");
      }
      break;
    }
  }
}

效果如下图所示:

4.判断输赢

当show[][]数组的所有非雷的位置均被翻开,扫雷即成功.

代码如下所示:

//判断输赢函数
int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS],int row,int col)
{
  //如果空格加数字等于ROW*COL-GAMELEVEL    扫雷成功!
  int i = 0;
  int j = 0;
  int iswin = 0;
  for (i = 1; i <= row; i++)
  {
    for (j = 1; j <= col; j++)
    {
      if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '9'))
      {
        iswin++;
      }
    }
  }
  if (iswin == (row * col - GAMELEVEL))//游戏结束
  {
    return 0;
  }
  else {
    return 1;//游戏继续
  }
}

5. 总工程

5.1 game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define GAMELEVEL 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS],int rows, int cols,char flag);
//展示棋盘
void DisBoard(char board[ROWS][COLS], int row, int col);
//生成雷区 '1'为雷
void Mine(char board[ROWS][COLS], int row, int col);
//扫雷
void Player(char show[ROWS][COLS], char mine[ROWS][COLS],int row, int col);
//计算雷数量
int FindMineNum(char mine[ROWS][COLS], int x, int y);
//自动翻开周围雷区
void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS],int x, int y);
//雷区标记
void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y);
//判断输赢函数
int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

5.2 text.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//菜单
void menu()
{
  printf("*****************************\n");
  printf("*******    1.Play    ********\n");
  printf("*******    0.Exit    ********\n");
  printf("*****************************\n");
}
void game()
{
  char show[ROWS][COLS] = { 0 };   // 展示给用户的界面
  char mine[ROWS][COLS] = { 0 };  //记录雷区
  //初始化数组
  InitBoard(show,ROWS,COLS,'*');   //初始化棋盘 '*'
  InitBoard(mine, ROWS, COLS,'0');//初始化雷区 '0'
  Mine(mine, ROW, COL);         //生成雷区
  DisBoard(show, ROW, COL);     //打印棋盘
  //DisBoard(mine, ROW, COL);       //展示雷区
  Player(show, mine, ROW, COL); //玩游戏     
}
int main()
{
  srand((unsigned int)time(NULL));
  int inpute = 0;
  do
  {
    menu();    //打印菜单
    scanf("%d", &inpute);
    switch (inpute)
    {
    case 1:
      game();//扫雷游戏
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("输入错误,请重输!\n");
    }
  } while (inpute);
}

5.3 game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char flag)
{
  for (int i = 0; i < rows; i++)
  {
    for (int j = 0; j < cols; j++)
    {
      board[i][j] = flag;
    }
  }
}
//展示棋盘
void DisBoard(char board[ROWS][COLS], int row, int col)
{
  int i = 0;
  int j = 0;
  printf("********扫雷*******\n");
  for (i = 0; i <= row; i++)
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d ", i);
    for (j = 1; j <= row; j++)
    {
      printf("%c ", board[i][j]);
    }
    printf("\n");
  }
}
//生成雷区 '1'为雷
void Mine(char board[ROWS][COLS], int row, int col)
{
  //x,y为随机生成坐标
  int x = 0;
  int y = 0;
  int count = 0;   //记录生成雷的数量
  while (1)
  {
    x = (rand() % row) + 1;
    y = (rand() % col) + 1;
    if (board[x][y] == '0')//该坐标没有雷,生成雷
    {
      board[x][y] = '1';
      count++;
      if (count == GAMELEVEL)
      {
        break;
      }
    }
  }
}
//玩游戏
void Player(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
  //输入坐标
  int x = 0;
  int y = 0;
  int protect = 1;
  while (1)
  {
    printf("请输入需要排雷的坐标: ");
    scanf("%d %d", &x, &y);
    while (getchar()!='\n');  
    //判断坐标是否合法
    if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
    {
      //该位置没有被排查过
      if (show[x][y] == '*')
      {
        //该位置有雷
        if (mine[x][y] == '1')
        {
          if (protect==1)
          {
            //Mine(mine,ROW,COL);//重新生成雷区再排
            //DisBoard(show, ROW, COL);  //再次打印新的棋盘
            printf("你已踩雷,请再次排雷!");
            continue;
          }
          else
          {
            DisBoard(mine, ROW, COL);//打印雷区
            printf("很遗憾,你被炸死!\n");
            break;
          }
        }
        else //该位置没有雷,自动排雷
        {
          protect = 0;
          AutoFindMine(show, mine, x, y);
          if (IsWin(show, mine, ROW, COL)) {
            system("cls");
            DisBoard(show, ROW, COL);     //打印棋盘
          }
          else {
            DisBoard(mine, ROW, COL);     //打印雷区
            printf("恭喜你,找到了所有的雷!\n");
            break;
          }
        }
      }
      else  //该位置被排查过
      {
        printf("该位置已经被排查过,请重输!\n");
        continue;
      }
    }
    else if((-x >= 1 && -x <= row) && (-y >= 1 && -y <= col))  //标记
    {
      MarkMine(show,mine ,-x, -y);
    }
    else {
      printf("输入坐标不在范围内,请重输!\n");
    }
  }
}
//自动翻开周围雷区
void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
  //坐标传进来,排八个坐标
  if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
  {
    //没有被排查过
    //if (show[x][y] == '*')
    //{
      int ret = FindMineNum(mine, x, y);
      if (ret == 0) //该坐标周围没雷,自动翻开用‘0’表示
      {
        show[x][y] = ' ';
      }
      else
      {
        show[x][y] = '0' + ret;
        return;
      }
    //}
    if (show[x - 1][y - 1] == '*')
    {
      AutoFindMine(show, mine, x - 1, y - 1);
    }
    if (show[x - 1][y] == '*')
    {
      AutoFindMine(show, mine, x - 1, y);
    }
    if (show[x - 1][y] == '*')
    {
      AutoFindMine(show, mine, x - 1, y);
    }
    if (show[x][y-1] == '*')
    {
      AutoFindMine(show, mine, x, y - 1);
    }
    if (show[x][y + 1] == '*')
    {
      AutoFindMine(show, mine, x, y + 1);
    }
    if (show[x+1][y - 1] == '*')
    {
      AutoFindMine(show, mine, x + 1, y - 1);
    }
    if (show[x + 1][y] == '*')
    {
      AutoFindMine(show, mine, x + 1, y);
    }
    if (show[x + 1][y+1] == '*')
    {
      AutoFindMine(show, mine, x + 1, y + 1);
    }
  }
  // 该坐标没有被排查过,排查过退出递归
  //先计算该坐标周围有没有雷,没有雷,赋空格。有雷退出递归
}
//计算雷的数量
int FindMineNum(char mine[ROWS][COLS], int x, int y)
{
  return (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);
}
//雷区标记函数   '!'标记
void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
  //该位置为 * 才可以标记为雷
  int mark_count = GAMELEVEL;
  int mark = 0;
  printf("1:标记雷\n");
  printf("0:取消标记雷\n");
  printf("请输入1/0: ");
  scanf("%d", &mark);
  switch (mark)
  {
    case 1:
    {
      if (show[x][y] == '*')//未排过雷
      {
        if (mark_count != 0) //标记数小于雷数
        {
          show[x][y] = '!';
          mark_count--;
          DisBoard(show, ROW, COL);
        }
        else {
          printf("已经标记%d个雷,不能继续标记!", GAMELEVEL);
        }
      }
      else
      {
        printf("该位置已经被排查,不能标记!\n");
      }
      break;
    }
    case 0:
    {
      if (show[x][y] == '!')
      {
        show[x][y] = '*';
        mark_count++;
        DisBoard(show, ROW, COL);
      }
      else {
        printf("该位置不能被取消,请重新标记");
      }
      break;
    }
  }
}
//判断输赢函数
int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS],int row,int col)
{
  //如果空格加数字等于ROW*COL-GAMELEVEL    扫雷成功!
  int i = 0;
  int j = 0;
  int iswin = 0;
  for (i = 1; i <= row; i++)
  {
    for (j = 1; j <= col; j++)
    {
      if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '9'))
      {
        iswin++;
      }
    }
  }
  if (iswin == (row * col - GAMELEVEL))//游戏结束
  {
    return 0;
  }
  else {
    return 1;//游戏继续
  }
}

源代码链接: link

以上就是扫雷的简单版本,还有很多地方需要优化,后续会继续更新,如果有更多问题,希望大佬在评论区指点!💖

目录
相关文章
|
6天前
|
C语言
【C语言】猜数字小游戏的一步一步实现2
【C语言】猜数字小游戏的一步一步实现
|
6天前
|
算法 C语言
【C语言】猜数字小游戏的一步一步实现1
【C语言】猜数字小游戏的一步一步实现
|
6天前
|
C语言
C语言之三子棋小游戏
C语言之三子棋小游戏
|
6天前
|
C语言
C语言之实现贪吃蛇小游戏篇(2)
C语言之实现贪吃蛇小游戏篇(2)
26 1
|
6天前
|
算法 编译器 C语言
C语言猜数字小游戏(也包含python实现的用法)
本文基于VS2022、pycharm和前面的知识,写一个凭借分支与循环的小游戏,比如: 写一个猜数字游戏 游戏要求: 电脑自动生成1~100的随机数 玩家猜数字,猜数的过程中,根据猜测数据的大小给出大了或小了的反馈,直到猜对,游戏结束 在pyhton中生成随机数是比较简单的,可以直接导入random的包,直接生成随机数,导致写猜数字小游戏变成了判读语句和循环语句嵌套就能写出来,所以我不做过多的介绍了,可以直接看后面的代码展示,想了解更多的python可看python的基础知识,这里面有我在学习python的过程中写的笔记
35 0
|
6天前
|
C语言
爱上C语言:扫雷小游戏,展开一片功能实现
爱上C语言:扫雷小游戏,展开一片功能实现
爱上C语言:扫雷小游戏,展开一片功能实现
|
6天前
|
人工智能 机器人 测试技术
【C语言】C语言实现猜单词小游戏(源码+报告)【独一无二】
【C语言】C语言实现猜单词小游戏(源码+报告)【独一无二】
|
6天前
|
存储 定位技术 C语言
基于C语言实现扫雷小游戏
本文介绍了使用C语言实现扫雷小游戏的过程。扫雷是一款经典的单机游戏,玩家需要通过点击方格来揭示数字或地雷,最终清除所有非地雷方格。实现过程中,首先定义了游戏所需的数据结构,如游戏地图、玩家信息等。然后,实现了游戏的初始化、渲染、输入处理等核心功能。在游戏逻辑方面,处理了点击事件、数字计算和胜负判断等。通过不断优化和完善,最终完成了基于C语言的扫雷小游戏实现,为玩家提供了一种简单有趣的游戏体验。
29 0
|
6天前
|
机器学习/深度学习 小程序 C语言
C语言初学者:原来我也可以实现扫雷小游戏(简易版)!
C语言初学者:原来我也可以实现扫雷小游戏(简易版)!
|
6天前
|
编译器 程序员 C语言
用C语言写一个扫雷小游戏
用C语言写一个扫雷小游戏
37 0

相关实验场景

更多