【C语言-扫雷游戏全功能详解】

简介: 【C语言-扫雷游戏全功能详解】



理解扫雷原理

9*9 棋盘

上面布置10个雷

排查雷

1.如果这个位置不是雷,就计算这个位置的周围8个坐标有几个雷,并显示雷的个数

2.如果这个位置是雷,就炸死了,游戏结束了

3.如果把不是雷的位置都找出来了,那游戏也结束

梳理扫雷过程

9*9 棋盘

初始化棋盘

void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
  int i = 0;
  int j = 0;
  for ( i = 0; i < rows; i++)
  {
    for ( j = 0; j < cols; j++)
    {
      board[i][j] = set;
    }
  }
}

通过初始化函数

初始化mine这个棋盘来安置随机的地雷的位置(先放入‘0’//后面随机放入‘1’标记地雷的位置)

初始化show这个棋盘来显示扫雷游戏的过程图(先放入‘*’//后面根据坐标逐渐揭开以及周围地雷情况的数字)

 char mine[11][11]                                                                           char show[11][11]

//初始化棋盘
    InitBoard(mine, ROWS, COLS,'0');
    InitBoard(show, ROWS, COLS, '*');

显示棋盘

void DisplayBoard(char board[ROWS][COLS], int row, int col) {
  int i = 0;
  int j = 0;
  printf("----------扫雷---------\n");
  for ( i = 0; i <= col; i++)
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d ", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c ",board[i][j]);
    }
    printf("\n");
  }
  printf("----------扫雷---------\n");
}

如图所示 通过打印行数和列数方便定位

                通过----------扫雷---------来划分每个图

布置雷

要随机布置10个雷

利用rand 和 srand 函数来生成随机数去随机生成地雷的坐标

注:

rand函数是C/C++中的随机数生成函数,用于生成一个范围在0到RAND_MAX之间的随机整数。它的原型为:

int rand();

srand函数用于设置rand函数的随机数种子,以便每次生成的随机数序列不同。它的原型为:

void srand(unsigned int seed);

在使用rand函数之前,通常会先使用srand函数设置一个种子,可以使用时间戳等随机值作为种子来保证每次运行程序时生成不同的随机数序列。

rand函数生成的随机数并不是真正的随机数,而是伪随机数,它是根据某种算法生成的,每次程序运行时生成的随机数序列是相同的。因此,为了获取更好的随机性,需要使用srand函数设置不同的种子值。

void SetMine(char mine[ROWS][COLS], int row, int col) {
  int count = EASY_COUNT;
  while (count) {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (mine[x][y] == '0') {
      mine[x][y] = '1';
      count--;
    }
  }
}

srand一般在main函数里写

srand((unsigned int)time(NULL));

 

排查雷

统计x,y坐标周围有几个雷

int GetMineCount(char mine[ROWS][COLS], int x, int y){
  return mine[x - 1][y] + mine[x - 1][y - 1] +
    mine[x][y - 1] + mine[x + 1][y - 1] +
    mine[x + 1][y] + mine[x + 1][y + 1] +
    mine[x][y + 1] + mine[x - 1][y + 1] - (8 * '0');
}

 

如图所示 判断周围8个位置的地雷情况

使用递归函数来实现周围没地雷时展开多个

void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
  int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值)
  if (count == 0)
  {
    show[x][y] = ' ';
    int i = 0, j = 0;
    for (i = -1; i <= 1; i++)
    {
      for (j = -1; j <= 1; j++)
      {
        //连续排除时限制范围在棋盘范围内
        if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*')
        {
          spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除
        }
      }
    }
  }
  else
  {
    show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示
  }
}

如果周围的地雷数是0 直接将这个位置改为空格 ——逐渐化简图像

递归实现周围如果都没地雷连续排除

判断成功排除后剩下的方格数是否等于地雷数

int IsWin(char show[ROWS][COLS], int row, int col)
{
  int num = 0;
  //排除一个地雷时便进行累加
  for (int i = 1; i <= row; i++)
  {
    for (int j = 1; j <= col; j++)
    {
      if (show[i][j] == '*')
        num++;
    }
  }
  return num;
}

当最后就剩下10个‘ * ’的时候就成功了

排查函数

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  int win = 0;
  while (1) {
    printf("请输入要排查的坐标:>");
    scanf("%d %d", &x, &y);
    if (x >= 1 && x <= row && y >= 1 && y <= col) {
      if (mine[x][y] == '1')
      {
        printf("很遗憾,你被炸死了\n");
        DisplayBoard(mine, ROW, COL);
        break;
      }
      else
      {
        spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格
        DisplayBoard(show, ROW, COL);
        win++;
      }
    }
    else
    {
      printf("坐标非法,重新输入\n");
    }
    int ret = IsWin(show, row, col);
    if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除
    {
      printf("恭喜你,通关成功!\n");
      DisplayBoard(mine, ROW, COL);
      break;
    }
  }
}

 

梳理编写代码思路

头文件game.h

库函数所需要头文件

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

stdio.h

rand(头文件:stdlib.h

time(头文件:time.h

需要rand(头文件:stdlib.h)和time(头文件:time.h),结合使用可以使电脑随机布置雷

宏定义

扫雷标准是9行9列,但是由于考虑到靠边的坐标计算周围的地雷数时,还需要不包括越界的地方

所以将两个数组都扩大一圈,就不会有这方面的困扰了,即变为了11行11列的二维数组

#pragma once
#define COL 9
#define ROW 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10

使用宏定义的方式分别定义ROW9,COL9,ROWS11,COLS11,以及设置的地雷数EASY_COUNT10

注:需要在定义之前加#pragma once

#pragma once是C++中的预处理指令,用于确保头文件只被编译一次。当多个源文件包含同一个头文件时,如果没有#pragma once指令,会导致头文件被重复包含,从而引发编译错误。使用#pragma once可以避免这种情况的发生,提高编译效率。

函数声明

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int colt);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//统计x,y坐标周围有几个雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);
//判断成功排除后剩下的方格数是否等于地雷数
int IsWin(char show[ROWS][COLS], int row, int col);
//使用递归函数来实现周围没地雷时展开多个
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

源文件game.c

包含所有有关的游戏的函数

具体代码见下

源代码 test.c

菜单

void menu()
  {
    printf("*****************************\n");
    printf("********** 1.PLAY **********\n");
    printf("********** 2.EXIT **********\n");
    printf("*****************************\n");
  }

丰富游戏细节

游戏

void game() 
  {
    char mine[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };
    //初始化棋盘
    InitBoard(mine, ROWS, COLS,'0');
    InitBoard(show, ROWS, COLS, '*');
    //显示show棋盘
    DisplayBoard(mine, ROW, COL);
    //布置雷
    //显示mine棋盘
    SetMine(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
    //排查雷
    FindMine(mine, show, ROW, COL);
  }

相当于将实现游戏的代码单独列出来

main函数

int main() 
  {
    srand((unsigned int)time(NULL));
      int input = 0;
      do
      {
         menu();
         printf("请选择>:");
         scanf("%d", &input);
         switch (input)
         {
          case 1:
            game();
            break;
          case 0:
            printf("退出游戏\n");
          default:
            printf("选择错误,重新选择\n");
            break;
          }
      } while (1);
  return 0;
}

写循环来实现在游戏结束后,继续打印选择的菜单,由玩家决定是否继续进行游戏

完整代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#pragma once
#define COL 9
#define ROW 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int colt);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//统计x,y坐标周围有几个雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);
//判断成功排除后剩下的方格数是否等于地雷数
int IsWin(char show[ROWS][COLS], int row, int col);
//使用递归函数来实现周围没地雷时展开多个
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

game.c

#include "game.h"
#define _CRT_SECURE_NO_WARNINGS 1
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
  int i = 0;
  int j = 0;
  for ( i = 0; i < rows; i++)
  {
    for ( j = 0; j < cols; j++)
    {
      board[i][j] = set;
    }
  }
}
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
  int i = 0;
  int j = 0;
  printf("----------扫雷---------\n");
  for ( i = 0; i <= col; i++)
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d ", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c ",board[i][j]);
    }
    printf("\n");
  }
  printf("----------扫雷---------\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col) {
  int count = EASY_COUNT;
  while (count) {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (mine[x][y] == '0') {
      mine[x][y] = '1';
      count--;
    }
  }
}
int GetMineCount(char mine[ROWS][COLS], int x, int y){
  return mine[x - 1][y] + mine[x - 1][y - 1] +
    mine[x][y - 1] + mine[x + 1][y - 1] +
    mine[x + 1][y] + mine[x + 1][y + 1] +
    mine[x][y + 1] + mine[x - 1][y + 1] - (8 * '0');
}
int IsWin(char show[ROWS][COLS], int row, int col)
{
  int num = 0;
  //排除一个地雷时便进行累加
  for (int i = 1; i <= row; i++)
  {
    for (int j = 1; j <= col; j++)
    {
      if (show[i][j] == '*')
        num++;
    }
  }
  return num;
}
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
  int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值)
  if (count == 0)
  {
    show[x][y] = ' ';
    int i = 0, j = 0;
    for (i = -1; i <= 1; i++)
    {
      for (j = -1; j <= 1; j++)
      {
        //连续排除时限制范围在棋盘范围内
        if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*')
        {
          spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除
        }
      }
    }
  }
  else
  {
    show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示
  }
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  int win = 0;
  while (1) {
    printf("请输入要排查的坐标:>");
    scanf("%d %d", &x, &y);
    if (x >= 1 && x <= row && y >= 1 && y <= col) {
      if (mine[x][y] == '1')
      {
        printf("很遗憾,你被炸死了\n");
        DisplayBoard(mine, ROW, COL);
        break;
      }
      else
      {
        spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格
        DisplayBoard(show, ROW, COL);
        win++;
      }
    }
    else
    {
      printf("坐标非法,重新输入\n");
    }
    int ret = IsWin(show, row, col);
    if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除
    {
      printf("恭喜你,通关成功!\n");
      DisplayBoard(mine, ROW, COL);
      break;
    }
  }
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
  void menu()
  {
    printf("*****************************\n");
    printf("********** 1.PLAY **********\n");
    printf("********** 2.EXIT **********\n");
    printf("*****************************\n");
  }
  void game() 
  {
    char mine[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };
    //初始化棋盘
    InitBoard(mine, ROWS, COLS,'0');
    InitBoard(show, ROWS, COLS, '*');
    //显示show棋盘
    DisplayBoard(mine, ROW, COL);
    //布置雷
    //显示mine棋盘
    SetMine(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
    //排查雷
    FindMine(mine, show, ROW, COL);
  }
  int main() 
  {
    srand((unsigned int)time(NULL));
      int input = 0;
      do
      {
         menu();
         printf("请选择>:");
         scanf("%d", &input);
         switch (input)
         {
          case 1:
            game();
            break;
          case 0:
            printf("退出游戏\n");
          default:
            printf("选择错误,重新选择\n");
            break;
          }
      } while (1);
  return 0;
}

运行结果

部分运行截图

 

相关文章
|
27天前
|
C语言
扫雷游戏(用C语言实现)
扫雷游戏(用C语言实现)
72 0
|
28天前
|
编译器 C语言
猜数字游戏实现#C语言
猜数字游戏实现#C语言
73 1
|
29天前
|
存储 C语言
揭秘C语言:泊舟的猜数字游戏
揭秘C语言:泊舟的猜数字游戏
|
1月前
|
存储 算法 安全
C语言实现扫雷游戏
C语言实现扫雷游戏
|
28天前
|
C语言
初学者指南:使用C语言实现简易版扫雷游戏
初学者指南:使用C语言实现简易版扫雷游戏
31 0
|
30天前
|
C语言
C语言扫雷游戏(详解)
C语言扫雷游戏(详解)
35 0
|
1月前
|
程序员 C语言
初识C语言之三子棋游戏
初识C语言之三子棋游戏
32 0
|
1月前
|
C语言
初识C语言3——函数(以猜数字游戏为例)
初识C语言3——函数(以猜数字游戏为例)
65 0
|
27天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
31 3
|
18天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
32 10