从零到一快速学会扫雷

简介: 从零到一快速学会扫雷

前言


扫雷这个游戏想必大家都不陌生吧,规则我就不多说了,我们今天就来用C语言来简单实现它。


一、游戏分析


ea5dd3fb6c48460ca8bd0eb286452d0c.png

游戏左上角的数字是雷的数量,上图显示的数字是数字周围一圈含雷的个数,由图我们可以创建一个 9*9 的数组来模拟方格,如果我选择最外的一圈的格子,那么周围格子就没有一圈了,怎么办呢?


6b839e03fcb8412ea0dadde3d47fee10.png


就像上图,2周围只有3个格子,怎么样才能让它周围也有8个格子呢?

我们可以多加2行、2列,这样图中每个格子周围都有8个格子,好判断。

那我们就创建一个11*11的数组。用1表示雷,0表示非雷。

问题又来了,如果格子周围只有一个雷,那它把1存进去显示1,那判断的时候岂不是变成雷了,那我们可以再创建一个数组,专门存放显示的数字。


1、创建2个数组,一个数组(a)用来存放布置好的雷的信息,另一个数组(b)存放排查出的雷的信息。

2、a 数组初始化为 ’ 0 ’ ,布置雷的时候改为 ’ 1 ’ 。

3、b 数组初始化为 ’ * ’ ,排除雷后,具体位置改为数字字符,如 ’ 3 ’ 。


二、游戏的实现


1、菜单


void menu()
{
  printf("*****************************\n"); 
  printf("*******    1.play    ********\n"); 
  printf("*******    0.exit    ********\n");
  printf("*****************************\n");
}


2、主函数


首先,我们思考一下,本次游戏应该最少执行一次,在执行过程中进行选择,那么我们应该使用 do while 循环来实现。根据选择不同,来执行相应的程序,那么应该使用 switch 语句。

下面我们来看代码:


int main()
{
  int input = 0;
  do
  {
  menu();                        //每次在选择前打印菜单
  printf("请选择:>");
  scanf("%d", &input);
  switch (input)
  {
  case 1:
    game();                    //开始游戏
    break;
  case 0:
    printf("退出游戏\n");
    break;
  default:
    printf("选择错误,重新选择!\n");
    break;
  }
  } while (input);                //根据输入的值来确定是否进行循环,0则退出循环
  return 0;                       //这也是为什么在菜单中把退出游戏设置成0的原因
}


3、game函数的实现


3.1、创建数组并初始化


#define M 11
#define N 11
void game()
{
  //设计两个数组存放信息
  char a[M][N] = { 0 };
  char b[M][N] = { 0 };
  // 初识化棋盘   
  // a数组初始化为'0'
  // b数组初始化为'*'
  chu_shi(a, M, N, '0');   //将 0 和 * 传过去方便初始化
  chu_shi(b, M, N, '*');
}
void chu_shi(char p[M][N], int m, int n, char w)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    p[i][j] = w;
}


3.2、打印棋盘


void game()
{
  //设计两个数组存放信息
  char a[M][N] = { 0 };
  char b[M][N] = { 0 };
  //初识化棋盘   
  // a数组初始化为'0'
  // b数组初始化为'*'
  chu_shi(a, M, N, '0');
  chu_shi(b, M, N, '*');
  //打印棋盘
  da_yin(a, M - 2, N - 2);      //M-2,N-2就是9,因为我们的游戏是9*9的,所以传9过去,方便打印
  da_yin(b, M - 2, N - 2);
}
void da_yin(char p[M][N], int m, int n)
{
  int i = 0;
  int j = 0;
  for (j = 0; j <= n; j++)    //打印列号
  printf("%d ", j);
  printf("\n");
  for (i = 1; i <= m; i++)
  {
  printf("%d ", i);      //打印行号
  for (j = 1; j <= n; j++)
    printf("%c ", p[i][j]);     //注意:数组p是char型,用%c打印每个元素
  printf("\n");
  }
}


结果:


195046feea574bc5b7a6b0f4989501e4.png


打印结果能够一目了然,哪个元素是几行几列,我们棋盘打印已经完成下一步就是布置雷了。


3.3、布置雷


我们要布置10个雷在数组 a 里,怎么样才能做到随机生成呢?

我们可以生成随机数,来达到效果。

随机数的生成:


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
  srand((unsigned int)time(NULL));    //设置一个随机数的生成器 
  int m = rand()%9;                 //因为 rand 生成随机数范围0~32767
  printf("%d",m);                   //所以 %9 使随机数生成范围为0~8
}


#include<stdio.h>
#include<stdlib.h>
#include<time.h>
srand((unsigned int)time(NULL));     //我们需要放在 main 函数中
void game()
{
  char a[M][N] = { 0 };
  char b[M][N] = { 0 };
  chu_shi(a, M, N, '0');
  chu_shi(b, M, N, '*');
  //布置雷
  bu_lei(a, M - 2, N - 2);
  da_yin(a, M - 2, N - 2);
}
void bu_lei(char p[M][N], int m, int n)
{
  int c = 10;
  while (c)
  {
  int x = rand() % m + 1;
  int y = rand() % n + 1;
  if (p[x][y] == '0')      //避免雷的重复
  {
    p[x][y] = '1';
    c--;
  }
  }
}


输出:


4fa2772384504dd2a1da7614def87cf0.png


我们可以观察到10个雷已经布置成功,接下来就是排雷了。


3.4、排雷


首先,我们排雷是一步一步排的,这样我们就需要一个while循环来首先,我们输入一个行和列都是1到9的坐标,然后显示出这个坐标旁边8个格子的雷的个数,这样我们便可以写出如下代码:


int ji_shu(char a[M][N], int x, int y)
{
  return (a[x - 1][y] +
      a[x - 1][y - 1] +
      a[x][y - 1] +
      a[x + 1][y - 1] +
      a[x + 1][y] +
      a[x + 1][y + 1] +
      a[x][y + 1] +
      a[x - 1][y + 1] - 8 * '0');     
}             //每个数字字符减去一个‘0’即可得到相应数字
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  while (1)
  {
  printf("请输入坐标:>");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
  {
    int k = ji_shu(a, x, y);   //计算旁边8个格子的雷的个数
    b[x][y] = k + '0';         //因为字符0的ASCII码值为48,加上对应数字0到9就能得到对应的数字字符
    da_yin(b, M - 2, N - 2);   //每次输入完后打印棋盘b
  }
  else
    printf("坐标非法,请重新输入:>\n");
  }
}


1812a57a28b44f638898f24de79b8860.png


如果排到雷了,我们应该让游戏结束,添加一个if 语句即可:


void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  while (1)
  {
  printf("请输入坐标:>");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
  {
    if (a[x][y] == '1')    //排到雷了
    {
    printf("你被炸死了\n");
    da_yin(a, M - 2, N - 2);  //让你看看雷的位置
    break;                   //退出 while 循环
    }
    else
    {
    int k = ji_shu(a, x, y);
    b[x][y] = k + '0';
    da_yin(b, M - 2, N - 2);
    }
  }
  else
    printf("坐标非法,请重新输入:>\n");
  }
}


如果排完了,那我们应该要退出呀,我们应该控制 while 循环来退出来:


void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  int win = 0;
  while (win<(M-2)*(N-2)-10)      //排完71个格子退出循环
  {
  printf("请输入坐标:>");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
  {
    if (a[x][y] == '1')
    {
    printf("你被炸死了\n");
    da_yin(a, M - 2, N - 2);
    break;
    }
    else
    {
    int k = ji_shu(a, x, y);
    b[x][y] = k + '0';
    da_yin(b, M - 2, N - 2);
    win++;
    }
  }
  else
    printf("坐标非法,请重新输入:>\n");
  }
  if (win == (M - 2) * (N - 2) - 10)
  {
  printf("恭喜你,排雷成功\n");
  da_yin(a, M-2, N-2);
  }
}


我们这个代码还是不够完善,当我们输入2 2后再输入2 2,它还是会输出,我们应该用 if 语句限制一下:


void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  int win = 0;
  while (win<(M-2)*(N-2)-10)
  {
  printf("请输入坐标:>");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
  {
    if (b[x][y] == '*')      //防止重复排查
    {
    if (a[x][y] == '1')
    {
      printf("你被炸死了\n");
      da_yin(a, M - 2, N - 2);
      break;
    }
    else
    {
      int k = ji_shu(a, x, y);
      b[x][y] = k + '0';
      da_yin(b, M - 2, N - 2);
      win++;
    }
    }
    else
    printf("该坐标已经被排查过了\n");
  }
  else
    printf("坐标非法,请重新输入:>\n");
  }
  if (win == (M - 2) * (N - 2) - 10)
  {
  printf("恭喜你,排雷成功\n");
  da_yin(a, M-2, N-2);
  }
}


好了,我们游戏具体解析已经完成了,接下来就是游戏封装了。


4、游戏具体代码


test.c:扫雷游戏的测试逻辑

game.h:游戏函数的声明

game.c :游戏函数的实现


4.1、game.h


#pragma once
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
#define M 11
#define N 11
//初始化棋盘
void chu_shi(char p[M][N], int m, int n, char w);
//打印棋盘
void da_yin(char p[M][N], int m, int n);
//布置雷
void bu_lei(char p[M][N], int m, int n);
//排雷
void pai_lei(char a[M][N], char b[M][N], int m, int n);


4.2、test.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 a[M][N] = { 0 };
  char b[M][N] = { 0 };
  chu_shi(a, M, N, '0');
  chu_shi(b, M, N, '*');
  bu_lei(a, M - 2, N - 2);
  pai_lei(a, b, M - 2, N - 2);
}
int main()
{
  int input = 0;
  srand((unsigned int)time(NULL));
  do
  {
  menu();
  printf("请选择:>");
  scanf("%d", &input);
  switch (input)
  {
  case 1:
    game();
    break;
  case 0:
    printf("退出游戏\n");
    break;
  default:
    printf("选择错误,请重新选择\n");
    break;
  }
  } while (input);
  return 0;
}


4.3、game.c


#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void chu_shi(char p[M][N], int m, int n, char w)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    p[i][j] = w;
}
void da_yin(char p[M][N], int m, int n)
{
  int i = 0;
  int j = 0;
  for (j = 0; j <= n; j++)
  printf("%d ", j);
  printf("\n");
  for (i = 1; i <= m; i++)
  {
  printf("%d ", i);
  for (j = 1; j <= n; j++)
    printf("%c ", p[i][j]);
  printf("\n");
  }
}
void bu_lei(char p[M][N], int m, int n)
{
  int c = 10;
  while (c)
  {
  int x = rand() % m + 1;
  int y = rand() % n + 1;
  if (p[x][y] == '0')
  {
    p[x][y] = '1';
    c--;
  }
  }
}
int ji_shu(char a[M][N], int x, int y)
{
  return (a[x - 1][y] +
      a[x - 1][y - 1] +
      a[x][y - 1] +
      a[x + 1][y - 1] +
      a[x + 1][y] +
      a[x + 1][y + 1] +
      a[x][y + 1] +
      a[x - 1][y + 1] - 8 * '0');
}
void pai_lei(char a[M][N], char b[M][N], int m, int n)
{
  int x = 0;
  int y = 0;
  int win = 0;
  while (win<(M-2)*(N-2)-10)
  {
  printf("请输入坐标:>");
  scanf("%d %d", &x, &y);
  if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
  {
    if (b[x][y] == '*')
    {
    if (a[x][y] == '1')
    {
      printf("你被炸死了\n");
      da_yin(a, M - 2, N - 2);
      break;
    }
    else
    {
      int k = ji_shu(a, x, y);
      b[x][y] = k + '0';
      da_yin(b, M - 2, N - 2);
      win++;
    }
    }
    else
    printf("该坐标已经被排查过了\n");
  }
  else
    printf("坐标非法,请重新输入:>\n");
  }
  if (win == (M - 2) * (N - 2) - 10)
  {
  printf("恭喜你,排雷成功\n");
  da_yin(a, M-2, N-2);
  }
}


最后


跟三子棋一样,都是代码一大堆,看得令人头疼,其实不然,只要我们把它细分下来,就会发现其实都挺简单的,都是我们学过的,只是见的少罢了,这就要求我们多做练习提高见识,这样才能写出好代码。

扫雷游戏也就圆满完成了,希望大家都能有所收获,有所成长。

下期见啦~


相关文章
|
5月前
|
网络协议 网络安全 网络虚拟化
路由器详细讲解
路由器是连接不同网络并转发数据包的关键设备,工作在OSI模型第三层(网络层)。它通过路由表选择最佳路径,支持数据转发、NAT转换、防火墙保护等功能。路由器分为家用、商用和工业级,各有针对性的性能与功能。其配置包括硬件连接、登录管理界面及网络、无线、安全等设置,选购时需关注处理能力、无线速率、端口速率和功能需求等关键指标。
555 22
|
5月前
|
缓存 编解码 数据安全/隐私保护
Harmony OS Next《ArkUI全组件终极指南 | 从布局到交互一站式精通》
本文全面解析HarmonyOS的ArkUI五大核心组件(布局容器、图片处理、文本、输入交互和按钮),通过零基础友好的分步讲解与实战场景覆盖,助你轻松打造美观且功能强大的应用。涵盖样式定制、性能优化及常见问题解决,适合教育科普行业学习参考。无论是初学者还是进阶开发者,都能从中掌握高效开发技巧,提升应用设计水平。
361 11
|
5月前
|
存储 安全 Ubuntu
从Linux到Windows:阿里云服务器系统镜像适配场景与选择参考
阿里云为用户提供了丰富多样的服务器操作系统选择,以满足不同场景下的应用需求。目前,云服务器的操作系统镜像主要分为公共镜像、自定义镜像、共享镜像、镜像市场和社区镜像五大类。以下是对这些镜像类型的详细介绍及选择云服务器系统时需要考虑的因素,以供参考。
|
7月前
|
NoSQL Ubuntu 网络安全
在 Ubuntu 20.04 上安装和配置 Redis
在 Ubuntu 20.04 上安装和配置 Redis 的步骤如下:首先更新系统包,然后通过 `apt` 安装 Redis。安装后,启用并启动 Redis 服务,检查其运行状态。可选配置包括修改绑定 IP、端口等,并确保防火墙设置允许外部访问。最后,使用 `redis-cli` 测试 Redis 功能,如设置和获取键值对。
275 1
|
8月前
|
SQL 数据库
数据库数据恢复—SQL Server报错“错误 823”的数据恢复案例
SQL Server数据库附加数据库过程中比较常见的报错是“错误 823”,附加数据库失败。 如果数据库有备份则只需还原备份即可。但是如果没有备份,备份时间太久,或者其他原因导致备份不可用,那么就需要通过专业手段对数据库进行数据恢复。
|
机器学习/深度学习 人工智能 监控
生成式 AI 与 LangCHain(二)(4)
生成式 AI 与 LangCHain(二)
512 5
|
存储 缓存 NoSQL
Redis中大Key与热Key的解决方案
在工作中,Redis作为一款高性能缓存数据库被广泛应用,但常遇到“大key”和“热key”问题。“大key”指单个键包含大量数据,导致内存消耗高、性能下降及持久化效率降低;“热key”则是频繁访问的键,会引起CPU占用率高、请求阻塞等问题。本文详细分析了这些问题的定义、影响、原因,并提供了相应的解决方案,如合理设置缓存时间和数据结构、拆分大key、采用热点数据分片等方法。
872 4
Redis中大Key与热Key的解决方案
|
搜索推荐 API 对象存储
10分钟学会构建端到端的图片搜索服务
本文介绍在没有向量数据的情况下,怎样通过OpenSearch-向量检索版快速从零搭建图像搜索服务。
83315 69
|
存储 资源调度 Kubernetes
新书自荐《深入集群:大型数据中心资源调度与管理》
深入集群 大型数据中心资源调度与管理,已经第2版了(2021-10月)。之前在ata和百晓生发布了新书自荐,这次同步到社区。
885 1
新书自荐《深入集群:大型数据中心资源调度与管理》