在写代码前我们先创建3个分别为game.c,test.c,game.h的文件
test.c用来存放主函数main
game.h用来存放需要用到的头文件以及需要声明的函数
game.c则用来存放函数的主体
首先我们先来理一下思路,游戏肯定要有一个页面让玩家选择,所以我们在test.c里面写一个游戏页面的函数就叫test()
test.c
#include"game.h"
void test() {
int input = 0;
do {
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏开始了!\n");
game();
break;
case 0:
printf("游戏结束,退出游戏\n");
break;
default:
printf("选择错误重新选择\n");
break;
}
} while (input);
}
int main() {
test();
return 0;
}
首先要包含一下game.h头文件,因为所有的需要的库和函数声明都放在game.h里面这样我们的test.c文件就会比较简洁。
这里我们先创建一个test函数,不要返回值。游戏菜单的所有操作在test函数里面执行就行了。
test函数里面我们定义了一个input整形变量,它用于记录我们玩家的选择,然后我们写一个do while循环,循环条件就是input变量,之所以用do while结构主要我们是先输入在判断,这里do while就非常好用。do while里面我们先定义个menu函数它的功能是打印游戏最开始的菜单页面,然后打印"请选择:"提示用户选择。接着用scanf函数来接收玩家传入的选择然后再用Switch分支进行判断如果是1打印"扫雷游戏开始了!"并且执行game函数,这个game函数就是游戏运行的功能。如果输入0就打印"游戏结束,退出游戏\n",假如用户手滑输入错误了就打印"选择错误重新选择\n"这里用input作为do while循环变量当用户输入0的时候循环条件判断为假,循环直接结束test函数结束。
然后写个main函数用来运行test函数,到这里我们的小框架就完成了,接着我们再来补全一下其他函数的功能。
首先是menu函数这个特别简单
test.c
void menu() {
printf("********************************\n");
printf("**********1.play ***************\n");
printf("**********0.exit ***************\n");
printf("********************************\n");
}
menu函数可以根据自己的喜好来写。
接着就是game函数了,game函数是整个扫雷游戏的关键。我们再来理一下思路。
首先扫雷游戏的规则,我们选择一个坐标他会显示以它为中心的九宫格范围类有多少颗雷,如果它本身有雷那么直接游戏失败
请看例图,假如8这个位置是我们选的坐标,这个8代表了以它为中心的九宫格范围类有8颗雷。
了解完扫雷游戏后我们继续理一下思路。我们首先得要一个东西来装雷,我们发现扫雷的格子和二维数组简直一模一样所以我们就用一个二维数组来装雷,但是我们装了雷,总不能直接给玩家看吧,所以我们再用一个二维数组来覆盖第一个数组起到遮蔽的效果。游戏信息都在第二数组上显示给用户。
说干就干,我们现在game.h中创建四个常量 ROW COL ROWS COLS 分别对应9 9 ROW+2 COL+2
ROW COL 对应了二维数组的行和列 ROWS COLS也是如此
game.h
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
这个时候肯定有人不明白为什么要用4个常量而不是2个常量明明是二维数组只需要2个常量就行了啊
请看例图
假设x1是我们要选的坐标,如果我们要计算x1为中心九宫格内的地雷数量,我们发现x1上边左边都没有格子,如果我们按照以x1为中心九宫格这样计算,那么x1上边左边就会出现数组越界的问题,程序就直接报错了x2 x3 x4同理,假如我们加个2。
我们可以看到这样就不会出现越界问题了。
ROW 和 COL是我们操作的范围而ROWS COLS是数组实际的长度,这样做就是为了防止数组越界。
好了,回归正题我们接着完善我们的game函数
test.c
void game() {
char mine[ROWS][COLS] = { 0 };//需要初始化为'0'
char show[ROWS][COLS] = { 0 };//需要初始化为'*'
Initialization(mine, ROWS, COLS, '0'); //棋盘初始化
Initialization(show, ROWS, COLS, '*'); //棋盘初始化
SetMine(mine, ROW, COL);//埋炸弹
Checkt(mine, show, ROW, COL); //排雷
}
首先就是定义两个数组一个为mine一个为show,mine就是用来埋雷的数组,show就是我们用来给玩家展示的数组。然后我们定义一个Initialization函数它的功能就是初始化数组,之后定义一个SetMine函数用来埋我们的地雷,接着就是Checkt函数让玩家来排雷。
Initialization,SetMine,Checkt这三个函数由于内容过多我们把函数主体放入game.c 声明放在 game.h里面
接下来就是快乐的写代码时刻了。
首先是Initialization初始化函数
game.c
#include "game.h"
void Initialization(char arr[ROWS][COLS], int rows, int cols, char set) {
int i = 0;
for (i = 0; i < rows; i++) {
int j = 0;
for (j = 0; j < cols; j++) {
arr[i][j] = set;
}
}
}
代码第一行肯定要包含头文件game.h的,我们需要初始化数组就得知道数组,数组的长度宽度,以及要初始化的内容,所以我们分别传入数组本身,数组长度,数组宽度以及初始化的内容。数据传入后我们先创建一个数组长度变量i,接着写个for循环(i = 0; i < rows; i++)因为数组是从下标0开始的,所以i必须是0 ,i小于rows 因为数组是从0开始的所以数组最后的下标等于数组长度-1,所以i小于rows,当运行完一次循环后i++相当于数组横坐标下标加1,循环体里我们定义一个j变量,它用作数组宽度的变量然后同理在写一个宽度的for循环,当内部的for循环运行完的时候,数组的某一行就初始化完了。当整个for循环运行完后数组就初始化好了。
SetMine埋雷函数
game.c
void SetMine(char arr[ROWS][COLS], int row, int col) {
int count = Booww;
while (count) {
int x = rand() % row + 1, y = rand() % col + 1;
if (arr[x][y] == '0') {
arr[x][y] = '1';
count--;
}
}
}
埋地雷同样要知道数组,数组的长度宽度,同时也要知道埋多少个地雷,我们传入数组,数组长度数组宽度,但是这里的数组长度以及宽度是我们操作的范围而不是实际数组的长度。如果我们传入实际长度和宽度那么又要面临越界的问题!接着我们创建一个count变量,给他赋一个常量的值,稍后我们在game.h中给Booww常量设定一个值,Booww是炸弹的数量。接着我们写个while循环埋雷我们先在循环体内创建x和y两个变量用于埋雷下标,然后用rand() % row + 1给x赋值用 rand() % col + 1赋值,这两个公式用于随机数的生成,rand() %生成一个随机数然后取余row后+1就会生成1-row之间的随机数。数生成后我们就开始埋雷,为了防止在同一个地方埋雷我们添加个if条件,如果arr[x][y] == '0'代表这个地方没有埋过雷那么我们把'1'赋给arr[x][y]然后就埋好一颗雷了,接着count--,需要埋雷的数量-1、直到count为0雷就埋好了。
Checkt检查函数
void Checkt(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col) {
int x = 0;//行坐标
int y = 0;//列坐标
int count = 0;//地雷变量
int num = row * col - Booww;//安全位置的数量
while (num){
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);//输入坐标
if (x >= 1 && x <= row && y >= 1 && y <= col) {//判断坐标是否在范围类
if (arr2[x][y] == '*') {//表示用户选择的位置没有排查过
if (arr1[x][y] == '1') {//用户选择的位置有地雷
printf("你踩雷了BOOW!\n");
Disprint(arr1, ROW, COL);//游戏结束时打印地雷位置
break;
}
else{
count = SetCount(arr1, x, y);//用户选择的位置没有地雷,计算周边地雷数量
arr2[x][y] = count + '0';//用地雷数量替换掉用户选择的位置
Disprint(arr2, ROW, COL);//打印show数组让用户看到
num--;//安全位置-1
}
}
else{
printf("次坐标已经排查过了!\n");//用户选择的位置不为'*'表示此位置排查过了。
}
}
else{
printf("你输错坐标了!\n");//用户输入的坐标不符合范围
}
}
if (num == 0){
printf("恭喜你成功了!\n");//安全位置为0 游戏成功
Disprint(arr1, ROW, COL);
}
}
检查函数是最关键的一部,用户要输入坐标排查地雷,所以我们需要知道用户的坐标以及还剩余的雷的数量,同时还有2个数组,一个检查是否踩到雷,一个用于展示坐标中心周围的地雷数量。这里我有写了两个函数一个用于展示数组一个用于计算地雷
Disprint 打印函数
void Disprint(char arr[ROWS][COLS], int row, int col) {
printf("扫雷游戏\n");
int i = 0;
for (i = 0; i <= row; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
打印函数第一个for循环用打印坐标列,第二for循环用于打印坐标行,第三for循环用于打印数组本体
SetCount计算函数
int SetCount(char arr1[ROWS][COLS], int x, int y) {
int i = 0, sum = 0;
for (i = x - 1; i <= x + 1; i++) {
int j = 0;
for (j = y - 1; j <= y + 1; j++) {
sum += arr1[i][j] - '0';
}
}
return sum;
}
计算函数,就是计算坐标为中心九宫格范围有多少颗地雷。
最后我们再把函数声明和需要的库放在game.h里面。
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Booww 10
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//棋盘初始化函数
void Initialization(char arr[ROWS][COLS], int rows, int cols, char set);
//打印函数
void Disprint(char arr[ROWS][COLS], int row, int col);
//布置地雷函数
void SetMine(char arr[ROWS][COLS], int row, int col);
//排雷函数
void Checkt(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);
// 计算地雷函数
int SetCount(char arr1[ROWS][COLS], int x, int y);
最后我们再整理一下所有代码
test.c
#include"game.h"
void menu() {
printf("********************************\n");
printf("**********1.play ***************\n");
printf("**********0.exit ***************\n");
printf("********************************\n");
}
void game() {
char mine[ROWS][COLS] = { 0 };//需要初始化为'0'
char show[ROWS][COLS] = { 0 };//需要初始化为'*'
Initialization(mine, ROWS, COLS, '0'); //棋盘初始化
Initialization(show, ROWS, COLS, '*'); //棋盘初始化
srand((unsigned int)time(NULL));
SetMine(mine, ROW, COL);//埋炸弹
Disprint(show, ROW, COL);
Checkt(mine, show, ROW, COL); //排雷
}
void test() {
int input = 0;
do {
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏开始了!\n");
game();
break;
case 0:
printf("游戏结束,退出游戏\n");
break;
default:
printf("选着错误重新选着\n");
break;
}
} while (input);
}
int main() {
test();
return 0;
}
game.c
#include "game.h"
void Initialization(char arr[ROWS][COLS], int rows, int cols, char set) {
int i = 0;
for (i = 0; i < rows; i++) {
int j = 0;
for (j = 0; j < cols; j++) {
arr[i][j] = set;
}
}
}
void Disprint(char arr[ROWS][COLS], int row, int col) {
printf("扫雷游戏\n");
int i = 0;
for (i = 0; i <= row; i++) {
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++) {
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void SetMine(char arr[ROWS][COLS], int row, int col) {
int count = Booww;
while(count) {
int x = rand() % row + 1, y = rand() % col + 1;
if (arr[x][y] == '0') {
arr[x][y] = '1';
count--;
}
}
}
int SetCount(char arr1[ROWS][COLS], int x, int y) {
int i = 0, sum = 0;
for (i = x - 1; i <= x + 1; i++) {
int j = 0;
for (j = y - 1; j <= y + 1; j++) {
sum += arr1[i][j] - '0';
}
}
return sum;
}
void Checkt(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col) {
int x = 0;//行坐标
int y = 0;//列坐标
int count = 0;//地雷变量
int num = row * col - Booww;//安全位置的数量
while (num){
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);//输入坐标
if (x >= 1 && x <= row && y >= 1 && y <= col) {//判断坐标是否在范围类
if (arr2[x][y] == '*') {//表示用户选择的位置没有排查过
if (arr1[x][y] == '1') {//用户选择的位置有地雷
printf("你踩雷了BOOW!\n");
Disprint(arr1, ROW, COL);//游戏结束时打印地雷位置
break;
}
else{
count = SetCount(arr1, x, y);//用户选择的位置没有地雷,计算周边地雷数量
arr2[x][y] = count + '0';//用地雷数量替换掉用户选择的位置
Disprint(arr2, ROW, COL);//打印show数组让用户看到
num--;//安全位置-1
}
}
else{
printf("次坐标已经排查过了!\n");//用户选择的位置不为'*'表示此位置排查过了。
}
}
else{
printf("你输错坐标了!\n");//用户输入的坐标不符合范围
}
}
if (num == 0){
printf("恭喜你成功了!\n");//安全位置为0 游戏成功
Disprint(arr1, ROW, COL);
}
}
game.h
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Booww 10
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//棋盘初始化函数
void Initialization(char arr[ROWS][COLS], int rows, int cols, char set);
//打印函数
void Disprint(char arr[ROWS][COLS], int row, int col);
//布置地雷函数
void SetMine(char arr[ROWS][COLS], int row, int col);
//排雷函数
void Checkt(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);
// 计算地雷函数
int SetCount(char arr1[ROWS][COLS], int x, int y);
我在test.c中的game函数里添加了一个srand((unsigned int)time(NULL));用于生成随机种子也就是埋雷的x和y坐标的随机数的种子。
游戏效果: