声明:(本博主为c语言萌新小白,所以以实践角度出发,运用可能需要的理论,带着大家写c语言扫雷游戏)!!
目录
一、知识储备:
1.基本的c语言知识:
1.数据类型:了解各种数据类型的定义及其含义,如int,float,double等基本类型。
2.变量与常量:知道如何声明变量,常量以及如何使用变量,常量。
3.头文件,源文件:知道两种文件的如何建立,及如何使用。
4.控制语句:如if语句,switch语句条件语句,for语句,while语句,do while等循环语句。
5.输入输出:必须了解输入输出语句的使用方法。
2.数据结构及其算法:
1.数组:知道一维数组和二维数组的声明,初始化和操作。在扫雷游戏中,二维数组用来实现其棋盘。
2.随机数:了解如何用rand()函数生成随机数,在实际应用中,rand函数用来定义随机雷,是实现雷区的关键一环。
3.算法:在本篇中,博主没有使用及其复杂的算法,如函数递归,其目的是先让初学者先了解扫雷游戏中的各个模块的实现,
3.函数:
1.库函数:了解库函数中常用的几种函数,掌握函数的头文件、函数用法及其操作。
2.自定义函数:在扫雷游戏中,自定义函数是实现模块化的重要一节。需要熟悉自定义函数的定义,声明以及操作用法等等,避免出错。
3.函数的链式访问:在本文章中,可能会用到链式访问等用法,可以最大程度上减少代码的杂糅。
4.代码风格:
风格:本篇文章的代码风格尽量做到详细清晰,逻辑不混,希望以最详细的解释,给读者最好的阅读体验
二、实现过程:
1.游戏分析及其要求
游戏分析:扫雷游戏的本质其实是在9*9(或更高难度)的平面网格中埋藏10颗雷,通过玩家每次的输入坐标,在没有触发雷的情况下,在该坐标中显示以其坐标为中心正方形的埋藏的雷的数目,将所有雷排查完毕,获取游戏胜利。(扫雷雏形)
如果想玩扫雷的完整版的可以点击链接,感受扫雷的玩法:扫雷游戏网页版 - Minesweeper
游戏要求:因为本篇致力于教会读者简单的扫雷实现,所以要求没怎么高,按照读者的习惯来即可。
2.代码操作及其思路
为了本次的间接性,用三个文件(2个源文件,1个头文件)编写游戏。
我们将三个文件分别定义为:test.c(游戏的测试)game.c(游戏功能的实现)game.h(定义函数的头文件)
游戏编写思路:
1.设计一个可以操作的简单用户界面:
利用语句完成用户的对应操作:
switch分支语句和do while循环语句的结合,可以实现用户的操作
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1:printf("游戏开始\n");
game();
break;
case 0:printf("退出游戏\n");
break;
default:printf("输入错误,请输入对应数字:\n");
}
} while (input);
实现思路:将switch语句嵌套在do while循环中,可以实现玩家玩游戏时的可重复性,当玩家玩游戏败北或者赢取时,可以保证下一次的游戏开始。
因为input为do while语句的判断条件 。
当输入”0“是,会跳出循环,结束游戏。
知道以上两个条件之后,我们可以编写
游戏界面和思路:
#include<stdio.h>
#include"game.h"void meau()
{
printf("********重生之我用c语言玩扫雷********\n");
printf("************** 1.play ****************\n");
printf("************** 0.exit ****************\n");
printf("************请选择您的选项************\n");}
void game()
{
char mine[Rows][Cols] = { 0 };
char show[Rows][Cols] = { 0 };
//初始化棋盘
initgame(mine, Rows,Cols,'*');
initgame(show, Rows, Cols, '0');
//打印棋盘
Displaygame(mine,Row,Col);
//设置雷
Setmine(show, Row, Col);
//排查雷
Findmine(mine,show,Row,Col);
}
void text()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1:printf("游戏开始\n");
game();
break;
case 0:printf("退出游戏\n");
break;
default:printf("输入错误,请输入对应数字:\n");
}
} while (input);
}
int main()
{
text();
return 0;
}
说明:为了实现界面的模块化,我们用自定义函数来实现菜单界面。其本质都是一样的。程序从main函数进去,之后执行各个自定义函数。(text.c文件实现功能)
2.游戏功能的实现思路:
大致可以分为:棋盘的初始化,棋盘的打印,棋盘的埋雷,棋盘的排除雷四种,而这四种分别用四种不同的自定义函数来实现。(用game.c文件实现游戏功能)
我们可以分别定义为
各类变量的声名:
到这会有人会疑问:
上面也没看到又Row和Col的变量的声明呀?,为什么没报错?
这是因为为了不让代码显得杂乱,我们将变量定义在game.h的头文件中。
这是该项目用到的所有变量和函数。大大节省了开发的效率。定义在头文件的函数可以重复反复的使用。
3.游戏功能开发及其流程
游戏功能的思路有了,那怎么去实现相应的功能那?
来来来,往这看..............
1、initgame函数(棋盘初始化函数)
初始化说白了其实就是将二维数组的所有元素全部定义为相同的常量,以便统一的管理。
(举例子:当你在游戏中是个君王,你是喜欢将你的军队统一着装的好,还是五颜六色,五花八门的好?
答案肯定是前者
一是好辩认,不至于让敌军打入内部。
二是好管理,不至于问题层出不穷。)
所以最关键的一点就来了:将二维数组的所有元素定义为相同的常量
所以自定义函数就来了
void initgame(char board[Rows][Cols], int rows, int cols, char set);//初始化
所以怎么定义哪?
三步走:
1.game.h声明函数
2.game.c书写函数功能
3.text.c传给函数参数
该函数的意思是:
第一行函数(mine数组)的意思是:将mine数组的所有Rows行Cols列内的元素全初始化为星号。
棋盘的初始化。
第二行函数(show数组)的功能是:显示所有的位置是不是雷
2、Displaygame自定义函数(打印棋盘函数)
说明:这里思路很好理解,如输入与输出思想一样,如果你只是在代码中定义输入,没有进行相应的输出处理,那么用户将无法接受到你的信息,用户也无法进行游戏的一步。
所以问题就来了,怎么样进行输出哪?
哈哈哈哈哈哈,其实答案还是一样,怎样输入,怎样输出。
可是在游戏里,你定义的是ROWS*COLS(11*11)的二维数组
但注意这里:你定义11*11的目的是什么?!!!!
是为了不让边缘化错误!!!!
简单来说,为了你找雷的时候不迷糊,不出错。
详细解释:在用户输入方格坐标时,如果是雷,则爆炸。不是雷,返回周围雷的数目,在这里,周围的数目我们定义的是以坐标为中心的9个格子
但是你想哈,你点到边缘哪?咋计算,没法计算,所以我们定义11*11,输出9*9,就很好的解决了这个鸡肋问题。
所以输出函数的目标就有了:输出11*11中间的9*9的二维数组,同时输出对应的列和行。
三步走:
1.头文件声明
2.game.c声名
3.text.c传参
功能:初始化完了之后,就该打印棋盘,在运用for循环,分别打印坐标行和列,运用i,j双循环结构将字符数组的所有元素进行打印。
3、Setmine函数(埋雷函数)
okk,初始化函数和输出函数都完成了捏,看到这里的你,对自己说一声真棒!!!!
初始化完了之后,就该埋雷了,怎么埋雷捏???
随机数!!!!对滴对滴!!!!
思路:
x:0-9中出一个随机数
y0-9中出一个随机数
定义简单难度的雷为10个
定义雷的数值为‘1’
不是雷的数值为‘0’
1.头文件声明
简单难度:EAZT_COUNT 10
2.game.c声名
3.text.c传参
功能:用随机数对x和y进行赋值,根据相应坐标进行不是雷的坐标进行埋雷!!!!
随机数的定义不在进行详细赘述哦,可以参考后面的完整代码哦
4.Findmine函数(找雷函数)
最后的最后!!!
前置任务已经完成,已开启最终任务。
该让用户找雷了,但是在用户界面,我们只需要用户输出一个x和y的坐标就好,让系统自动判断是不是雷。
所以思路就出来了
1.while判断循环
(判断赢的条件:排除数目==row*col-EZAT_COUNT)
(所以进入循环的条件:排除数目<row*col-EZAT_COUNT)
2.让用户输入坐标
(是0-9的坐标,进行判断)
(不是0-9的坐标,重新输入)
3.是不是雷
(是雷,结束游戏,打印所有棋盘)
(不是雷,统计雷的数字,赋值给相应坐标数组,打印游戏棋盘)
1.头文件声明
2.game.c声名
3.text.c传参
功能:让用户输入坐标,进行扫雷游戏
5.GetmineCount函数(统计函数)
啊???为啥有个这个get什么什么的函数???
哦哦,数目返回值对吧,没有踩雷的周围雷的数目。
两个for循环搞定
1.头文件声明
2.game.c声明
3.game.c传参
这里是game.c哦!!!!!
功能:周围如果有雷则是‘1’-‘0’,ASCII码值相减则是1。所以相加起来,就是周围9个数的雷的数目。
到这里,代码基本完成
完整代码图附上:
1.测试部分
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"void meau()
{
printf("********重生之我用c语言玩扫雷********\n");
printf("************** 1.play ****************\n");
printf("************** 0.exit ****************\n");
printf("************请选择您的选项************\n");}
void game()
{
char mine[Rows][Cols] = { 0 };
char show[Rows][Cols] = { 0 };
//初始化棋盘
initgame(mine, Rows,Cols,'*');
initgame(show, Rows, Cols, '0');
//打印棋盘
Displaygame(mine,Row,Col);
//设置雷
Setmine(show, Row, Col);
//排查雷
Findmine(mine,show,Row,Col);
}
void text()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1:printf("游戏开始\n");
game();
break;
case 0:printf("退出游戏\n");
break;
default:printf("输入错误,请输入对应数字:\n");
}
} while (input);
}
int main()
{
text();
return 0;
}
2.函数定义部分
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"game.h"
void initgame(char board[Rows][Cols], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void Displaygame(char board[Rows][Cols], int row, int col)
{
int i = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);//打印列坐标
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行坐标
for ( int 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 = EAZT_COUNT;
int x = 0;
int y = 0;
while (count)
{
x = rand() % Row + 1;
y = rand() % Col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
int GetmineCount(char board[Rows][Cols], int x, int y)
{
int count = 0;
for (int i = -1; i < 1; i++)
{
for (int j =- 1; j < 1; j++)
{
count += (board[x + i][y + j] - '0');
}
}
return count;
}
void Findmine(char board[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EAZT_COUNT)
{
printf("请输入您的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '1')
{
printf("很遗憾,你猜到雷了,游戏结束\n");
Displaygame(show, row, col);
break;
}
else
{
int count = GetmineCount(show, x, y);
board[x][y] = count + '0';
printf("不是雷,游戏继续\n");
Displaygame(board, Row, Col);
win++;}
}
else
{
printf("输入有误,请输入1到9之间的坐标");
}
}
if (win == row * col - EAZT_COUNT)
{
printf("恭喜你,排雷成功\n");
Displaygame(board, Row, Col);
}
}
3.头文件部分
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
#define EAZT_COUNT 10
void initgame(char board[Rows][Cols], int rows, int cols, char set);//初始化
void Displaygame(char board[Rows][Cols], int row, int col);//打印
void Setmine(char board[Rows][Cols], int row, int col);//埋雷
void Findmine(char board[Rows][Cols], char show[Rows][Cols], int row, int col);//排除雷
int GetmineCount(char board[Rows][Cols], int x, int y);
4.游戏调试及其操作
游戏运行结果图:
1.用户操作页面:
2.游戏页面:
3.操作页面(玩游戏):
不是雷:
是雷:
5.游戏的后续优化:
哈哈哈哈哈哈,这里需要的优化还是挺多的
比如:
1.不是雷的坐标周围的标记和伸展问题
2.清屏问题
3.是否‘0’和‘1’可以被更好的字符代替。........
等等。
读者可以阅读其他文章,进行相应优化。本文章不再进行赘述。
哈哈哈哈哈哈不是因为我不会哦