用C语言编写一个游戏:扫雷
一、简单说明:
扫雷是一款经典大众益智类小游戏,游戏目标是在最短的时间内根据点击格子出现的数字提示找出所有非雷格子,同时避免踩雷导致游戏结束。扫雷游戏可以说是风靡一时,是不少人的童年乐趣。今天就用C语言来编写一个简单的扫雷游戏。
二、 编写思路:
编写之前,我们要考虑几个问题:
1.用什么来代替扫雷游戏中的所有格子?
在C语言中,我们可以创建二维数组来表示格子地图~~
每一个格子要表示两部分信息,一是表示未翻开状态的格子(用字符表示)和已翻开状态的格子(用数字表示);二是表示该格子是地雷或者不是地雷(可以用1表示是地雷,0表示不是)。
2.如何用数组来具体表示?
使用两个二维数组来分别表示以上的两部分信息~(假设创建一个9 9的格子地图)
第一个二维数组:char showMap[9][9];
表示每个格子是否翻开状态,用*表示未翻开,数字’0’~'8’表示已翻开的数字提示(周围有几个雷)。
第二个二维数组:char mineMap[9][9];
用’1’表示是地雷,'0’表示不是地雷。
3.如何设置随机的地雷?
通过设置随机种子(srand),利用时间戳,这样便可以避免出现重复生成相同的结果,降低游戏体验。
三、游戏流程:
1.创建地图并初始化:针对showMap来说,初始化全为’*’;针对mineMap来说,先将整个数组都初始化为全’0’,随机生成10个位置作为地雷,设为’1’
2.打印地图 showMap
3.让玩家输入坐标,表示要翻开的位置(注意:此处输入坐标时,行列应当从0开始)
4.判断是否踩雷
5.更新showMap,在翻开位置显示周围有几个雷
6.判定玩家是否翻开了所有的位置
//扫雷游戏
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include <time.h>
#define MAX_ROW 9
#define MAX_COL 9
#define MINE_COUNT 10
void init(char showMap[MAX_ROW][MAX_COL], char mineMap[MAX_ROW][MAX_COL]){
/*for (int row = 0;row < MAX_ROW;row++) {
for (int col = 0;col < MAX_COL;col++) {
showMap[row][col] = '*';
}
}*/
//mem=>memory 内存
//set :设置,集合
//memset的功能就是把一段内存上的每个字节都设置成一个具体的值
//数组隐式转成了指针,所以不可以用sizeof来求数组长度
//二维数组的每个元素都是在一个连续的内存空间上布局的
memset(showMap, '*', MAX_ROW * MAX_COL);
memset(mineMap, '0', MAX_ROW * MAX_COL);
//随机产生10个位置作为地雷
//先设置随机种子
srand((unsigned int)time(0));
int mineCount = 0;
while (mineCount<MINE_COUNT) {
int row = rand() % MAX_ROW;
int col = rand() % MAX_COL;
if (mineMap[row][col] == '1') {
continue;
}
mineMap[row][col] = '1';
mineCount++;
}
}
//希望这一个函数能同时具备打印两种地图的功能
//取决于实参填啥
//形参名字叫做"map"不太合适.C++标准库里有一个std:map
void print(char theMap[MAX_ROW][MAX_COL]) {
for (int row = 0;row < MAX_ROW;row++) {
for (int col = 0;col < MAX_COL;col++) {
printf("%c",theMap[row][col]);
}
printf("\n");
}
}
void update(char showMap[MAX_ROW][MAX_COL],
char mineMap[MAX_ROW][MAX_COL], int row, int col) {
//表示周围地雷的个数
int count = 0;
for(int r = row - 1;r <= row + 1;r++) {
for (int c = col - 1;c <= col + 1;c++) {
if (r < 0 || r >= MAX_ROW || c < 0 || c >= MAX_COL) {
//此时r,c坐标超出棋盘的范围,直接跳过
continue;
}
if (r == row && c == col) {
//不需要判断,直接下次循环
continue;
}
if (mineMap[r][c] == '1') {
count++;
}
}
}
//如果直接赋值的话,此时假设count是2
//当前row,col位置的元素就被设置成了ASCII值为2的字符
//而不是'2'
// 类似于这样的转换, 只是 C 语言中是这么写的.
// 其他的主流编程语言中, 一般是不允许 字符类型 和 整数类型 进行混合运算的
showMap[row][col] = '0'+count;
}
int main() {
//创建地图并进行初始化
char showMap[MAX_ROW][MAX_COL] = { 0 };
char mineMap[MAX_ROW][MAX_COL] = { 0 };
init(showMap, mineMap);
int openedCount = 0;
while (1) {
//2.打印地图
print(showMap);
//3.玩家输入坐标
//此处是为了验证update函数的正确性
//先把地雷的布局也打印出来
int row = 0;
int col = 0;
printf("请输入坐标(row,col):");
scanf("%d %d", &row, &col);
//进行合法性判定
if (row<0 || row>=MAX_ROW || col<0 || col>=MAX_COL) {
printf("您输入的坐标有误!请重新输入!\n");
continue;
}
if (showMap[row][col]!= '*') {
printf("您输入的位置已经翻开了!\n");
continue;
}
//4.判断是否踩雷
if (mineMap[row][col] == '1') {
printf("您踩雷了!游戏结束!\n");
break;
}
//5.更新showMap,显示当前位置周围有多少个雷
update(showMap, mineMap, row, col);
//6.进行游戏胜利的判定,统计当前一共翻开了多少个格子
openedCount++;
if (openedCount == MAX_ROW * MAX_COL - MINE_COUNT) {
printf("恭喜你游戏胜利!\n");
break;
}
}
//print(mineMap);
system("pause");
return 0;
}
运行页面如下
注意!!代码中用常量定义数字是为了避免魔鬼数字的出现。
所谓魔鬼数字,是指在代码中没有具体含义的数字、字符串。魔鬼数字主要影响了代码可读性,读者看到数字无法理解其含义,从而难以理解程序的意图。当程序中的魔鬼数字过多时,代码的可维护性将会急剧下降,代码变得难以修改,并容易引入错误。但并不是任何情况下将数字定义为常量都是有利的,如果常量的名称没有意义,无法帮助理解代码,同样也是一种魔鬼数字。除此之外,在个别情况下,将数字定义为常量反而会导致代码更加难以理解,此时就不应该强求将数字定义为常量。
另外,扫雷游戏中,要想实现连续翻开,要用到递归函数,可以在update函数中判断,如果统计出的count为0的话,就以当前位置为中心,把周围8个位置的递归代入到update函数中~~
好了,到此,一个简单的扫雷游戏程序编写就可以结束了,编写扫雷游戏对于初学者而言,还是有一定难度的,主要是用到了数组这方面的知识,还是要多多尝试,多多练习!!