C语言初阶之扫雷
目录
扫雷
hello!各位小伙伴们,今天小春宝将给大家分享扫雷,扫雷其实并没有大家想象中那么困难,接下来,教大家如何创建一个扫雷项目
准备环境
再写扫雷之前,我们得新建一个项目和三个文件
1.game.h
2.game.c
3.test.c
老规矩,先给大家献上头文件的内容
#ifndef __GAME_H__
#define __GAME_H__
#define _CRT_SECURE_NO_WARNINGS 1
#define H 9
#define L 9
#define N 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void menu();
void game();
void board(char board[H][L], int x, int y);
void playboard(char board[H][L], int x, int y);
void board2(char arr1[H][L], int x, int y, int a, int b);
void play(char arr[H][L], char arr1[H][L], int x, int y);
int win(char arr[H][L], int x, int y);
void clearance(char arr[H][L], char arr1[H][L], int a, int b, int x, int y);
#endif
大致思路分析
扫雷与三子棋不同,我们得有一个大致的框架才能够顺利的开始扫雷的构建,扫雷游戏的玩耍是翻开遮蔽的地方,如果是雷我们就被炸死了,如果不是会告诉我们附近是否有雷,这个时候我们需要两个棋盘,一个棋盘是给玩家看的,一个棋盘是实际上的棋盘,这个棋盘既要有雷,也要有提示雷的信息,为了防止用户第一步就被炸死了,我们需要在下完的第一步才确定雷的位置,为了尽量还原游戏的功能,我们可以增加标记这一功能来确定雷的位置。
编写主函数
看过三子棋的小伙伴们,应该对主函数的构建并不陌生,因为三子棋的主函数构建也是如出一辙,话不多说,直接上代码
#include"game.h"
void menu() {
printf("**********************n");
printf("****1.play 0.exit*****n");
printf("**********************n");
}
int main() {
int n;
do {
menu();
a:
printf("请选择:");
scanf("%d", &n);
switch (n)
{
case 1:
game();
break;
case 0:
printf("退出程序n");
break;
default:
printf("输出错误,请重新输入n");
goto a;
}
} while (n);
return 0;
}
重点塑造的game.c
1.game
由于game函数本体十分简单,这里直接给大家上game
void game() {
char arr[H][L] = { 0 };
char arr1[H][L] = { 0 };
int n;
printf("扫雷游戏即将开始,敬请期待n");
board(arr, H, L);
playboard(arr, H, L);
n = 1;
while (n)
{
play(arr, arr1, H, L);
n = win(arr, H, L);
}
}
2.board
borad函数是初始化那个给玩家看的棋盘,他的载体是board[9][9]
为了方便修改棋盘内容,我们定义两个常量
H代表行,L代表列,我们今天要实现的扫雷是一个9*9十个雷的棋盘
为了一种什么感,我们选择'*'来给大家看到
void board(char board[H][L], int x, int y) {
int i, j;
for (i = 0; i < x; i++)
for (j = 0; j < y; j++)
board[i][j] = '*';
}
3.playboard
接下来又是今天的一个难题,在我看来,扫雷的棋盘相比于三子棋的棋盘难度更高些,实现这个棋盘需要大家不断的尝试和调整,直接上我们要的效果
首先我们先得打印而不去管那些数字,数字是为了提高用户体验之后加的
void playboard(char board[H][L], int x, int y) {
int i, j;
for (i = 0; i < x; i++) {
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
for (j = 0; j < y; j++) {
printf("| %c ", board[i][j]);
}
printf("|n");
}
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
}
这个是效果,但这个棋盘太大,我们需要数字来提示用户,添加数组这一步大家一定要多加尝试
void playboard(char board[H][L], int x, int y) {
int i, j,k;
for (k = 0; k < x + 1; k++) {
if (k == 0)
printf("%d ", k);
else printf("%d ", k);
}
printf("n");
for (i = 0; i < x; i++) {
printf(" ");
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
printf("%d", i + 1);
for (j = 0; j < y; j++) {
printf("| %c ", board[i][j]);
}
printf("|n");
}
printf(" ");
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
}
这样之后扫雷就是完全体了,当然大家也可以靠着自己的意愿进行修改,我的这只是一个参考
4.board2
首先我们暂时以A代表雷,每个字符暂定为0,只要周围有雷就加1
因为雷是随机的,我们这个时候需要随机值rand来协助我们完成
void board2(char arr1[H][L], int x, int y, int a, int b) {//a,b是用户第一次扫雷的位置的坐标
int i, j, k, m, n;
srand((unsigned)time(NULL));
for (i = 0; i < x; i++)
for (j = 0; j < y; j++)
arr1[i][j] = '0';
for (k = 0; k < N; k++)
{
a:
i = rand() % x;
j = rand() % y;
if (arr1[i][j] != 'A' && i != a && j != b)
arr1[i][j] = 'A';
else
goto a;
for (m = i - 1; m <= i + 1; m++) {
for (n = j - 1; n <= j + 1; n++) {
if (m < x && m >= 0 && n < y && n >= 0 && arr1[m][n] != 'A')
arr1[m][n]++;
}
}//这两层循环是用来给雷旁边的字符都加一的,并且我们需要判断该位置的合法性和是不是雷
}
}
5.play
我们得让用户选择操作,开始扫雷,进行标记并且取消标记,开始扫雷
我们先来实现扫雷功能
void play(char arr[H][L], char arr1[H][L], int x, int y) {
int i, j, n;
static int k = 0;
b: printf("(1.标记 0.开始扫雷 2.取消标记 )请选择:");
scanf("%d", &n);
switch (n)
{
case 0:
a: printf("(提示,以x y的形式输入,x代表行,y代表列)请输入你要扫的坐标:");
scanf("%d%d", &i, &j);
if (k < 1) {
board2(arr1, x, y, i - 1, j - 1);
k++;
}
if (i >= 1 && i <= x && j >= 1 && j <= 9)
{
if (arr[i - 1][j - 1] == '*')
arr[i - 1][j - 1] = arr1[i - 1][j - 1];
else {
printf("该位置无法进行扫雷操作,请重新输入n");
goto a;
}
}
else
{
printf("输入坐标非法,请重新输入n");
goto a;
}
break;
case 1:
break;
case 2:
break;
default:
printf("选择错误,请重新选择n");
goto b;
}
system("cls");
playboard(arr, H, L);
}
这里面包括了play函数的框架,这个时候我们再来进行标记的编写,我们约定被标记过的雷用'*'来表示
接下来我们直接编写case 1中的内容
c: printf("请选择你要标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d %d", &i, &j);
if (arr[i - 1][j - 1] == '*')
{
arr[i - 1][j - 1] = '#';
printf("标记成功n");
}
else {
printf("输出错误,请重新输入n");
goto c;
}
break;
接着便是标记以后,若是用户觉得标记错误或者是不小心标记了,我们可以随时让用户选择取消标记
printf("请选择你要取消标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d%d", &i, &j);
if (arr[i - 1][j - 1] == '#')
{
printf("取消成功n");
arr[i - 1][j - 1] = '*';
}
else {
printf("取消标记失败n");
goto b;
}
//为预防用户误点取消标记,而当还未进行标记时就一直出不来了,所以避免这种情况,我们选择让用户直
//接回到b标记点的位置来重新选择操作
//接下来我们组装一下便可得到play
void play(char arr[H][L], char arr1[H][L], int x, int y) {
int i, j, n;
static int k = 0;
b: printf("(1.标记 0.开始扫雷 2.取消标记 )请选择:");
scanf("%d", &n);
switch (n)
{
case 0:
a: printf("(提示,以x y的形式输入,x代表行,y代表列)请输入你要扫的坐标:");
scanf("%d%d", &i, &j);
if (k < 1) {
board2(arr1, x, y, i - 1, j - 1);
k++;
}
if (i >= 1 && i <= x && j >= 1 && j <= 9)
{
if (arr[i - 1][j - 1] == '*')
arr[i - 1][j - 1] = arr1[i - 1][j - 1];
else {
printf("该位置无法进行扫雷操作,请重新输入n");
goto a;
}
}
else
{
printf("输入坐标非法,请重新输入n");
goto a;
}
break;
case 1:
c: printf("请选择你要标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d %d", &i, &j);
if (arr[i - 1][j - 1] == '*')
{
arr[i - 1][j - 1] = '#';
printf("标记成功n");
}
else {
printf("输出错误,请重新输入n");
goto c;
}
break;
case 2:
printf("请选择你要取消标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d%d", &i, &j);
if (arr[i - 1][j - 1] == '#')
{
printf("取消成功n");
arr[i - 1][j - 1] = '*';
}
else {
printf("取消标记失败n");
goto b;
}
break;
default:
printf("选择错误,请重新选择n");
goto b;
}
system("cls");
playboard(arr, H, L);
}
这个play函数比较长,但是大家在编写play函数时千万不要着急,我们最后还会对play函数进行一次修改
6.win
我们不能无止境的扫雷,而win函数就是用于判断是否游戏结束的函数
int win(char arr[H][L], int x, int y) {
int i = 0, j = 0;
int count = 0;
for (i = 0; i < x; i++) {
for (j = 0; j < y; j++) {
if (arr[i][j] == 'A')
{
printf("很遗憾,你输了n");
return 0;
}
else if (arr[i][j] == '*' || arr[i][j] == '#')
count++;
}
}
if (count > N)
return 1;
else
{
printf("恭喜你,你赢了n");
return 0;
}
}
//根据我们编写的game函数可知win返回0的时候游戏继续,返回1游戏将继续
7.clearance
其实扫雷写到这里就可以结束了,但是如果就这样结束可能会让大家玩的很累,扫雷还有一种性质,只要旁边没有雷就会推开一片,然后不断的推开,知道旁边有雷,这个操作需要使用递归的思想,所以希望大家在编写这个函数时一定要多加思考,切勿盲目的编写,而这个函数的出现迫使我们不得不对play也进行修改
我们先来设计一下clearance函数
void clearance(char arr[H][L], char arr1[H][L], int a, int b, int x, int y) {
int m,n;
for (m = a ; m <= a+2; m++) {
for (n = b; n <= b + 2; n++) {
if (m >= 0 && m < x && n < y && n >= 0) {
if (arr1[m][n] == '0' && arr[m][n] == '*')
{
arr[m][n] = arr1[m][n];
clearance(arr, arr1, m - 1, n - 1, x, y);
}
else if (arr[m][n] != '#')
arr[m][n] = arr1[m][n];
}
}
}
}
然后我们再对play函数进行小小的修改
a: printf("请选择你要扫的位置(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d %d", &i, &j);
if (k < 1) {
board2(arr1, H, L, i - 1, j - 1);
k++;
}
if (i <= x && i >= 1 && j <= y && j >= 1) {
if (arr[i - 1][j - 1] == '*')
{
arr[i - 1][j - 1] = arr1[i - 1][j - 1];
if (arr[i - 1][j - 1] == '0')
clearance(arr, arr1, i - 2, j - 2, H, L);
}
else {
printf("该位置已经扫过,请重新输入n");
goto a;
}
}
else
{
printf("位置错误,请重新输入n");
goto a;
}
将case 0中的内容进行以下修改
当你看完这里,今天的扫雷就实现完成了,看完之后,大家应该可以做出属于自己的扫雷,最后送上完整的game.c
如果有需要完整的代码的也可以上我的码云去寻找
#include"game.h"
void board(char board[H][L], int x, int y) {
int i, j;
for (i = 0; i < x; i++)
for (j = 0; j < y; j++)
board[i][j] = '*';
}
void playboard(char board[H][L], int x, int y) {
int i, j,k;
for (k = 0; k < x + 1; k++) {
if (k == 0)
printf("%d ", k);
else printf("%d ", k);
}
printf("n");
for (i = 0; i < x; i++) {
printf(" ");
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
printf("%d", i + 1);
for (j = 0; j < y; j++) {
printf("| %c ", board[i][j]);
}
printf("|n");
}
printf(" ");
for (j = 0; j < y; j++) {
printf("|---");
}
printf("|n");
}
void board2(char arr1[H][L], int x, int y, int a, int b) {
int i, j, k, m, n;
srand((unsigned)time(NULL));
for (i = 0; i < x; i++)
for (j = 0; j < y; j++)
arr1[i][j] = '0';
for (k = 0; k < N; k++)
{
a:
i = rand() % x;
j = rand() % y;
if (arr1[i][j] != 'A' && i != a && j != b)
arr1[i][j] = 'A';
else
goto a;
for (m = i - 1; m <= i + 1; m++) {
for (n = j - 1; n <= j + 1; n++) {
if (m < x && m >= 0 && n < y && n >= 0 && arr1[m][n] != 'A')
arr1[m][n]++;
}
}
}
}
void play(char arr[H][L], char arr1[H][L], int x, int y) {
int i, j, n;
static int k = 0;
b: printf("(1.标记 0.开始扫雷 2.取消标记 )请选择:");
scanf("%d", &n);
switch (n)
{
case 0:
a: printf("请选择你要扫的位置(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d %d", &i, &j);
if (k < 1) {
board2(arr1, H, L, i - 1, j - 1);
k++;
}
if (i <= x && i >= 1 && j <= y && j >= 1) {
if (arr[i - 1][j - 1] == '*')
{
arr[i - 1][j - 1] = arr1[i - 1][j - 1];
if (arr[i - 1][j - 1] == '0')
clearance(arr, arr1, i - 2, j - 2, H, L);
}
else {
printf("该位置已经扫过,请重新输入n");
goto a;
}
}
else
{
printf("位置错误,请重新输入n");
goto a;
}
break;
case 1:
c: printf("请选择你要标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d %d", &i, &j);
if (arr[i - 1][j - 1] == '*')
{
arr[i - 1][j - 1] = '#';
printf("标记成功n");
}
else {
printf("输出错误,请重新输入n");
goto c;
}
break;
case 2:
printf("请选择你要取消标记的坐标(提示以x y的形式输入x代表行,y代表列)n");
scanf("%d%d", &i, &j);
if (arr[i - 1][j - 1] == '#')
{
printf("取消成功n");
arr[i - 1][j - 1] = '*';
}
else {
printf("取消标记失败n");
goto b;
}
break;
default:
printf("选择错误,请重新选择n");
goto b;
}
system("cls");
playboard(arr, H, L);
}
int win(char arr[H][L], int x, int y) {
int i = 0, j = 0;
int count = 0;
for (i = 0; i < x; i++) {
for (j = 0; j < y; j++) {
if (arr[i][j] == 'A')
{
printf("很遗憾,你输了n");
return 0;
}
else if (arr[i][j] == '*' || arr[i][j] == '#')
count++;
}
}
if (count > N)
return 1;
else
{
printf("恭喜你,你赢了n");
return 0;
}
}
void clearance(char arr[H][L], char arr1[H][L], int a, int b, int x, int y) {
int m,n;
for (m = a ; m <= a+2; m++) {
for (n = b; n <= b + 2; n++) {
if (m >= 0 && m < x && n < y && n >= 0) {
if (arr1[m][n] == '0' && arr[m][n] == '*')
{
arr[m][n] = arr1[m][n];
clearance(arr, arr1, m - 1, n - 1, x, y);
}
else if (arr[m][n] != '#')
arr[m][n] = arr1[m][n];
}
}
}
}
void game() {
char arr[H][L] = { 0 };
char arr1[H][L] = { 0 };
int n;
printf("扫雷游戏即将开始,敬请期待n");
board(arr, H, L);
playboard(arr, H, L);
n = 1;
while (n)
{
play(arr, arr1, H, L);
n = win(arr, H, L);
}
}
最后奉上码云网站C语言代码: 储存一些认真编写的C语言代码 (gitee.com)https://gitee.com/build-xiaochunbao/c-language-code