这款游戏相信大家并不陌生,关于它的玩法,在这里就不赘述了,重点来介绍一下如何用C语言完成这个项目。我们先来思考一下,扫雷的棋盘肯定是通过二维数组来实现的,在这个二维数组中既要埋雷,又要扫雷,还得给玩家展示,实在是容易混淆,这里我们不妨通过两个二维数组来实现,一个用来埋雷,另一个用来扫雷并且这个棋盘可以向玩家展示,降低了难度。


首先,我们来屡一下这个游戏的思路。

  1. 埋雷;这里可以设置难度扫10个雷或者20个雷,相应的雷数多的话棋盘也应该设置大一些,这样才好玩。
    但这里我设置的棋盘大小都是一样的,只是增加雷数来改变难度。

  2. 扫雷 ;通过用户输入坐标来确定要排雷的位置,可以在棋盘周围加上行号和列号,便于玩家确定坐标。这部分可以实现以下三个功能:
    ①.第一次玩家输入有雷的坐标,把雷挪开,保证第一次不死,增强可玩性;
    ②.玩家输入的坐标周围没有雷,就利用递归把周围没有雷的地方都展开并且最外圈显示的雷数;
    ③.玩家觉得哪个位置有雷,可以实现用“ * ”标记雷,便于观察。
    有一个问题:在统计某个坐标周围的雷数时,若坐标位于边角处,肯定会出现越界访问。为了避免这种情况,我们可以在原棋盘的基础上多创建一圈。(如下图)

  3. 检查是否胜利;我的判断方法是玩家无论是否标记雷,只要没展开的格子数和标记的雷数相加等于总的雷数时,玩家获胜。

然后在屡清了思路后,可以开始敲代码了,为了方便起见,我们创建3 个文件来写这个项目:测试文件、游戏文件和头文件。(如下图)



先来写 test.c 文件,写出游戏的逻辑以及需要用到的函数,在 game.c 里来实现具体函数的内容。
(ROW和COL为9,分别代表行和列,在 game.h 中有定义)

#include "game.h"
void menu()
{printf("*******************************************\n");printf("*       欢迎来到扫雷小游戏(*^▽^*)        *\n");printf("*               1.play                    *\n");printf("*               0.exit                    *\n");printf("*******************************************\n");
}void game()
{int count = 0;char mine[ROWS][COLS]={0};//创建一个埋雷的数组char show[ROWS][COLS]={0};//创建一个可以向玩家展示的数组Init(mine,ROWS,COLS,'0');//初始化埋雷的数组,Init(show,ROWS,COLS,'\03');//初始化展示的数组,在这里我用的是爱心,也可以自己定义字符do{printf("请选择难度:1.EASY(10个雷)      2.HARD(20个雷)\n");scanf("%d",&count);switch(count){case 1:SetMine(mine,ROW,COL,EASY);//埋雷//Print(mine,ROW,COL);Print(show,ROW,COL);//打印棋盘Safe(mine,show,ROW,COL);//第一次不炸FindMine(mine,show,ROW,COL,EASY);//排雷break;case 2 :SetMine(mine,ROW,COL,HARD);//埋雷//Print(mine,ROW,COL);Print(show,ROW,COL);//打印棋盘Safe(mine,show,ROW,COL);//第一次不炸FindMine(mine,show,ROW,COL,HARD);//排雷break;default:printf("选择错误,请重新选择:>\n");break;}}while((count!=1)&&(count!=2));}void test()
{int input = 0;srand((unsigned int)time(NULL));//do{menu();//打印菜单printf("请选择:>\n");scanf("%d",&input);switch(input){case 1:game();break;case 0:printf("退出游戏\n");default:printf("请重新输入\n");break;}}while(input);
}int main()
{test();
}

一、初始化棋盘
这里介绍一个C语言的库函数 memset 函数,void * memset(void *s, int value, size_t num);
memset 是内存设置函数,它的功能是将s中当前位置后面的num个字节 (typedef unsigned int size_t )用 value 替换并返回 s 。它的第一个参数为要替换的地址,第二个参数是你想要替换成的字符,第三个参数为替换的字节数。
一个小小的提醒提醒\color{#DC143C}{提醒}:可以将数组初始化成全 0,但不能将数组替换成全1,因为 memset 函数是按字节进行替换的。

void Init(char board[ROWS][COLS], int rows, int cols, char str)//初始化棋盘
{
    memset(board, str,sizeof(board[0][0])*rows*cols);
}

二、输出棋盘
打印棋盘,顺便打印上行列号,便于观察。

void Print(char board[ROWS][COLS], int row, int col)//输出棋盘
{int i = 0;int j = 0;for(i=0; i<=row; 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");}

三、埋雷
利用随机数生成雷的坐标,每埋一个雷,雷数减一。rand()函数虽然是生成随机数的函数,但它并不是实际意义的随机数,每次编译,出现的随机数都是一样的。为了让雷的埋更随机些,需要在 test.c 中的 test 函数中加入srand((unsigned int)time(NULL));来实现真正意义的随机。

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

四、计算某个坐标周围的雷数
通过传递来的一个坐标,来计算周围一圈8个格子中的雷数,由于雷和非雷是字符型的 ‘ 1 ’ 、 ‘ 0 ’,我们要的是数字 1、0,因此需要将字符转换为数字。字符 ‘ 0 ’比数字 0 的 ASCII 码值大48,可以把8个格子的雷数加起来减去 8*48 ,或者可以直接减去 8 *‘ 0 ’也是一样的。

static int CountMine(char board[ROWS][COLS], int x, int y)
{
    return (board[x-1][y]+board[x-1][y+1]+
            board[x][y+1]+board[x+1][y+1]+
            board[x+1][y]+board[x+1][y-1]+
            board[x][y-1]+board[x-1][y-1])-8*'0';
            //值为数字
}

五、展开函数
通过递归函数实现展开,并且调用计算某个坐标周围雷数的函数来输出雷数。

static void OpenMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row ,int col, int x,int y)
{int ret = 0;ret = CountMine(mine, x, y);//计算x,y周围的雷数if(ret == 0){show[x][y]=' ';//若周围没有雷,输出空格if(x-1>0 && y>0 && show[x-1][y]=='\03')OpenMine(mine, show, row, col, x-1, y);if(x-1>0 && y+1<=col && show[x-1][y+1]=='\03')OpenMine(mine, show, row, col,  x-1, y+1);if(x>0 && y+1<=col && show[x][y+1]=='\03')OpenMine(mine, show, row, col, x, y+1);if(x+1<=row && y+1<=col && show[x+1][y+1]=='\03')OpenMine(mine, show, row, col, x+1, y+1);if(x+1<=row && y>0 && show[x+1][y]=='\03')OpenMine(mine, show, row, col, x+1, y);if(x+1<=row && y-1>0 && show[x+1][y-1]=='\03')OpenMine(mine, show, row, col, x+1, y-1);if(x>0 && y-1>0 && show[x][y-1]=='\03')OpenMine(mine, show, row, col, x, y-1);if(x-1>0 && y-1>0 && show[x-1][y-1]=='\03')OpenMine(mine, show, row, col, x-1, y-1);}else{show[x][y] = CountMine(mine, x, y)+'0';//若坐标周围有雷,显示雷数}}

六、确保第一次不死
当第一次玩家踩到雷时,悄悄地把雷挪走,保证玩家第一次不被炸死,增强可玩性。

void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = 0;int ret = 1;int a = 0;int b = 0;printf("请输入要排查位置的坐标:>");scanf("%d%d",&x,&y);a = x;b = y;//保存一下玩家输入的坐标值if(mine[x][y]=='1'){mine[x][y]='0';//把雷变成非雷count = CountMine(mine, x,y);show[x][y] = count+'0';while(ret)//重新设一个雷{x = rand()%9+1;y = rand()%9+1;if(mine[x][y]=='0'){mine[x][y] = '1';}ret--;}}OpenMine(mine, show, row, col, a, b);//展开玩家输入坐标的周围//Print(mine,ROW,COL);Print(show,ROW,COL);}

七、检查是否胜利
我的判断方法是玩家无论是否标记雷,只要没展开的格子数和标记的雷数相加等于总的雷数时,玩家就获胜了。

static int CheckWin(char show[ROWS][COLS],int row,int col)
{int i = 0;int j = 0;int c = 0;for(i=1; i<=row; i++){for(j=1; j<=col; j++){if((show[i][j] == '*') || (show[i][j] == '\03'))c++;}}return c;
}

八、标记雷
这里我写了标记雷的函数,也可以不写,只是为了方便观察,当玩家已经断定某个位置有雷的时候,可以进行标记。(用 “ * ” 标记雷)

static void SignMine(char show[ROWS][COLS], int row, int col, const int count)
{int input = 0;int a = 0;int b = 0;do{printf("是否需要标记雷: 1.是        0.否\n");scanf("%d", &input);switch(input){case 1:printf("请输入要标记的坐标:>");scanf("%d%d", &a, &b);if(show[a][b]=='\03'){show[a][b]='*';Print(show,row,col);//标记后打印一下棋盘}else if(show[a][b]=='*'){printf("该坐标已经被标记\n");}break;case 0:break;}}while(input);
}

九、排雷

void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col, const int count)//排雷
{int x = 0;int y = 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");Print(mine,ROW,COL);printf("\a\a\a");break;}else{int c = CountMine(mine,x,y);show[x][y]=c+'0';OpenMine( mine, show,row,col,x,y);//展开函数//Print(mine,ROW,COL);Print(show,ROW,COL);//打印棋盘if(CheckWin(show, row, col) == count)//检查是否胜利{break;}}}else{printf("坐标非法\n");}SignMine(show, row, col, count);//标记雷}if(CheckWin(show, row, col) == count){printf("恭喜你,胜利了!\n");Print(mine,ROW,COL);}
}

将上述函数写全部在 game.c 中,源文件如下:

#include "game.h"void Init(char board[ROWS][COLS], int rows, int cols, char str)//初始化棋盘
{memset(board, str,sizeof(board[0][0])*rows*cols);
}void Print(char board[ROWS][COLS], int row, int col)//输出棋盘
{int i = 0;int j = 0;for(i=0; i<=row; 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 board[ROWS][COLS], int row, int col, int count)//埋雷
{int x = 0;int y = 0;//1-9while(count){x = rand()%9+1;//%9-> 0-8y = rand()%9+1;if(board[x][y]=='0'){board[x][y]='1';count--;}}
}static int CountMine(char board[ROWS][COLS], int x, int y)//计算某个坐标周围的雷数
{return (board[x-1][y]+board[x-1][y+1]+board[x][y+1]+board[x+1][y+1]+board[x+1][y]+board[x+1][y-1]+board[x][y-1]+board[x-1][y-1]) -8*'0';//数字
}static void OpenMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row ,int col, int x,int y)
{int ret = 0;ret = CountMine(mine, x, y);//计算x,y周围的雷数if(ret == 0){show[x][y]=' ';//若周围没有雷,输出空格if(x-1>0 && y>0 && show[x-1][y]=='\03')OpenMine(mine, show, row, col, x-1, y);if(x-1>0 && y+1<=col && show[x-1][y+1]=='\03')OpenMine(mine, show, row, col,  x-1, y+1);if(x>0 && y+1<=col && show[x][y+1]=='\03')OpenMine(mine, show, row, col, x, y+1);if(x+1<=row && y+1<=col && show[x+1][y+1]=='\03')OpenMine(mine, show, row, col, x+1, y+1);if(x+1<=row && y>0 && show[x+1][y]=='\03')OpenMine(mine, show, row, col, x+1, y);if(x+1<=row && y-1>0 && show[x+1][y-1]=='\03')OpenMine(mine, show, row, col, x+1, y-1);if(x>0 && y-1>0 && show[x][y-1]=='\03')OpenMine(mine, show, row, col, x, y-1);if(x-1>0 && y-1>0 && show[x-1][y-1]=='\03')OpenMine(mine, show, row, col, x-1, y-1);}else{show[x][y] = CountMine(mine, x, y)+'0';//若坐标周围有雷,显示雷数}}void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = 0;int ret = 1;int a = 0;int b = 0;printf("请输入要排查位置的坐标:>");scanf("%d%d",&x,&y);a = x;b = y;//保存一下玩家输入的坐标值if(mine[x][y]=='1'){mine[x][y]='0';//把雷变成非雷count = CountMine(mine, x,y);show[x][y] = count+'0';while(ret)//重新设一个雷{x = rand()%9+1;y = rand()%9+1;if(mine[x][y]=='0'){mine[x][y] = '1';}ret--;}}OpenMine(mine, show, row, col, a, b);//展开玩家输入坐标的周围//Print(mine,ROW,COL);Print(show,ROW,COL);}static int CheckWin(char show[ROWS][COLS],int row,int col)//检查有没有赢,计算所有未展开的数量
{int i = 0;int j = 0;int c = 0;for(i=1; i<=row; i++){for(j=1; j<=col; j++){if((show[i][j] == '*') || (show[i][j] == '\03'))c++;}}return c;
}static void SignMine(char show[ROWS][COLS], int row, int col, const int count)
{int input = 0;int a = 0;int b = 0;do{printf("是否需要标记雷: 1.是        0.否\n");scanf("%d", &input);switch(input){case 1:printf("请输入要标记的坐标:>");scanf("%d%d", &a, &b);if(show[a][b]=='\03'){show[a][b]='*';Print(show,row,col);//标记后打印一下棋盘}else if(show[a][b]=='*'){printf("该坐标已经被标记\n");}break;case 0:break;}}while(input);
}void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col, const int count)//排雷
{int x = 0;int y = 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");Print(mine,ROW,COL);printf("\a\a\a");break;}else{int c = CountMine(mine,x,y);show[x][y]=c+'0';OpenMine( mine, show,row,col,x,y);//展开函数//Print(mine,ROW,COL);Print(show,ROW,COL);//打印棋盘if(CheckWin(show, row, col) == count)//检查是否胜利{break;}}}else{printf("坐标非法\n");}SignMine(show, row, col, count);//标记雷}if(CheckWin(show, row, col) == count){printf("恭喜你,胜利了!\n");Print(mine,ROW,COL);}
}

再然后把用到的头文件、函数以及宏定义全部写到 game.h 中,源文件如下:

#ifndef _GAME_H__
#define _GAME_H__#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2#define EASY 10
#define HARD 20void Init(char board[ROWS][COLS], int rows, int cols, char str);//初始化棋盘
void Print(char board[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char board[ROWS][COLS], int row, int col, int count);//埋雷
void FindMine(char board[ROWS][COLS],char show[ROWS][COLS], int row, int col,const int count);//排雷
void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);//第一次不炸#endif //_GAME_H__

最后测试程序,这一步可以先将埋雷的棋盘打印出来,便于测试,测试完无误后,记得将埋雷的棋盘屏蔽掉,不要让玩家看到我们埋雷的棋盘哦~






END

小游戏——扫雷(可以标记)相关推荐

  1. C++小游戏扫雷——如何写出一个简易的扫雷

    C++小游戏扫雷--如何写出一个简易的扫雷 其实很简单,但是这段代码不知道是否有BUG,有的话可以提出,谢谢大家. all.h函数库 #include "all.h" using ...

  2. Windows经典小游戏--扫雷精装版

    Windows经典小游戏–扫雷精装版 最开始玩的电脑游戏就是蜘蛛纸牌和扫雷了,那时候觉得很神奇的事情现在自己也可以做了.下面就展示一下C语言扫雷的代码. 首先创建一个"扫雷"的项目 ...

  3. java小游戏-扫雷游戏

    java小游戏-ava小游戏-扫雷游戏 1 窗口绘制 2 雷区绘制 3 界面规划 4 底层地图 5 地雷生成 6 地雷重合 7 数字生成 8 顶层绘制 9 双缓存技术 10 鼠标事件 11 左键翻开 ...

  4. java做小游戏扫雷(附源码)

    下载解压即可玩:扫雷 项目代码下载:java代码 目录 图片 怎么自己也能做 代码细节 感受 全部代码 GameWin.java类 : MapBottom.java类: GameUtil.java类: ...

  5. 小游戏------扫雷

    扫雷游戏,大家应该都不陌生,一张棋盘上会有固定数量的雷,可以在知道某个坐标周围雷的总个数的前提下,判断出雷的位置.我的这个小游戏主要是在9*9的棋盘上,设置了10个雷.这个游戏相对于三子棋与猜数字,这 ...

  6. python小游戏扫雷怎么玩的技巧_用 Python 实现扫雷小游戏

    扫雷是一款益智类小游戏,最早于 1992 年由微软在 Windows 上发行,游戏适合于全年龄段,规则简单,即在最短的时间内找出所有非雷格子且在中间过程中不能踩到雷,踩到雷则失败,需重新开始. 本文我 ...

  7. 用C++写一个小游戏——扫雷(1)

    一.项目准备 1.安装VS2022 (1)从官网下载Visal Studio(Community 2022): Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (mic ...

  8. java实现简单窗口小游戏“扫雷”

    原创:享学课堂讲师 转载请声明出处! 前言 忘记是从何处看到过关于扫雷小程序的文章,所以这次也就跟着做一下.其实很简单的,如果有java入门的同学也可以尝试一下自己做这种java小程序.几行代码做几遍 ...

  9. 用C语言实现电脑小游戏——扫雷

    目录 1.菜单制作 2.游戏的重复进行 3.扫雷游戏的实现:game函数 3.1初始化棋盘 3.2打印棋盘 3.3布置地雷 3.4玩家排雷 4.实现输入一个坐标显示一片信息 5.扫雷原码 众所周知,扫 ...

最新文章

  1. edgesForExtendedLayout、automaticallyAdjustsScrollV
  2. Spring框架入门基础,不可多得的干货
  3. 【计算机视觉】深度相机(六)--Kinect v2.0 手势样本库制作
  4. AntDB上使用表空间
  5. 精读《你不知道的javascript》中卷
  6. vfifo控制mig_virtual fifo的使用
  7. 楚留香服务器维护时间,2019年06月28日官方维护公告
  8. 模电试题_数电试题 综合测试
  9. 使用dos2unix批量转换文件
  10. marlin固件烧录教程_marlin固件中文(marlin固件下载)【配置教程】
  11. 计算机网络初探(ip协议)
  12. flutter能开发游戏吗_Flutter Flame游戏开发上手(1)
  13. PHP 对HTML页面进行压缩
  14. prometheus 告警配置以及配置项解释
  15. javaScript(正则,DOM)
  16. 超硬核!MySQL优化从执行计划开始(explain超详细)
  17. 简述几种常用的编码器数据格式
  18. 嵌入式linux零基础培训,零基础精通嵌入式linux系统有那么容易吗
  19. 深度解密:软银孙正义如何成为美国硅谷最有权势之人
  20. 每天五分钟机器学习:如何使用单个神经元实现逻辑与、或的计算

热门文章

  1. gif动图怎么制作?手机怎么制作动态图
  2. java工程师的工作述职报告,java程序员述职报告
  3. element清除表单校验
  4. Acwing周赛57-数字操作-(思维+分解质因数)
  5. MySQL之IN的优化
  6. 北京市2012年职工平均月工资5223元
  7. C++ 多态介绍详解
  8. Jenkins --- 三种安装方式
  9. 怎么写软件功能测试报告,分享详细专业的功能检测报告模板
  10. C# 9 新功能“源代码生成器”,你用了吗?