前言
扫雷这个游戏想必大家都不陌生吧,规则我就不多说了,我们今天就来用C语言来简单实现它。
一、游戏分析
游戏左上角的数字是雷的数量,上图显示的数字是数字周围一圈含雷的个数,由图我们可以创建一个 9*9 的数组来模拟方格,如果我选择最外的一圈的格子,那么周围格子就没有一圈了,怎么办呢?
就像上图,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"); } }
结果:
打印结果能够一目了然,哪个元素是几行几列,我们棋盘打印已经完成下一步就是布置雷了。
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--; } } }
输出:
我们可以观察到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"); } }
如果排到雷了,我们应该让游戏结束,添加一个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); } }
最后
跟三子棋一样,都是代码一大堆,看得令人头疼,其实不然,只要我们把它细分下来,就会发现其实都挺简单的,都是我们学过的,只是见的少罢了,这就要求我们多做练习提高见识,这样才能写出好代码。
扫雷游戏也就圆满完成了,希望大家都能有所收获,有所成长。
下期见啦~