扫雷应该是我们接触到的第一个电脑游戏,用c语言实现扫雷对初学者来说是一个不错的锻炼
编写扫雷只需要用到数组、函数和生成随机数的知识,所以比较适合成为编程学习者编写的第一个小游戏。
如果不熟悉生成随机数的知识,可以去我的上一篇文章看看《C生成随机数》
第一部分是源码复制就可以使用,每一个自定义函数在第二部分设计思路中都有详细解释,结合代码实现和设计思路理解会有一个更好的效果
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
#define MINE_NUMBER 10
void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
arr[i][j] = ch;
}
}
}
void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
{
int mine_number = MINE_NUMBER;
int row = rand() % 9 + 1;//生成一到九的行号
int col = rand() % 9 + 1;//生成一到九的列标
while (mine_number > 0)
{
if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
{
arr[row][col] = '1';//安装地雷
mine_number--;
}
else
{
row = rand() % 9 + 1;
col = rand() % 9 + 1;
}
}
}
void display(char arr[ROWS][COLS])
{
for (int i = 0; i <= COL; i++)
{
printf(" %d ", i);//打印行号
}
printf("\n\n");
for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
{ //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)
//分割线为: - - - | - - - | - - -
printf(" %d ", i);//打印列标
for (int j = 1; j <= COL; j++)
{
printf(" %c ", arr[i][j]);
if (j < COL)
{
printf("|");//为了美观,不打印最后一个'|'
}
}
printf("\n");
printf(" ");
for (int k = 1; k <= COL; k++)
{
printf("---");
if (k < COL)
{
printf("|");//为了美观,不打印最后一个'|'
}
}
printf("\n");
}
}
char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标字符型的地雷数量
{
return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
}
int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
{
int row = 0, col = 0;
int flag = 1;
do//输入合法坐标,在行号列标范围内,并且没有被走过
{
printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
scanf("%d %d", &row, &col);
if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
flag = 0;
else
printf("输入错误\n");
} while (flag);
if (mine[row][col] == '1')//踩到雷,返回-1
return -1;
else
{
show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
//将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
return 1;
}
}
void meau()//菜单
{
printf("------------------\n");
printf("| 1.play |\n");
printf("|----------------|\n");
printf("| 0.exit |\n");
printf("------------------\n");
}
void game()
{
char mine[ROWS][COLS];//mine:地雷
char show[ROWS][COLS];
reset(mine, '0');//初始化mine为全零
reset(show, '*');//初始化reset为全*
setmine(mine);//安置地雷
//display(mine);
display(show);//展现
int count = 0;
do
{
int move = player_move(mine, show);
if (move == -1)//踩到雷游戏结束
{
printf("\n---------------------------------------踩到地雷,游戏结束>\n");
display(mine);
break;
}
else if (move == 1)//没有踩到雷则步数count加一
{
count++;
if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
{
printf("\n---------------------------------------排除地雷,游戏结束>\n");
display(mine);
break;
}
}
display(show);
} while (1);
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1:
game(); break;
case 0:
break;
default:
printf("输入错误,请重新输入\n"); break;
}
} while (input);
return 0;
}
1.main()函数搭建框架:像所有的电脑游戏一样,我们需要一个菜单,通过菜单选择进入游戏和退出游戏,当一盘游戏结束时可以再次选择进入或者退出,菜单用printf()打印就可以解决,循环的进入游戏用do while()循环就可以解决
void meau()//菜单
{
printf("------------------\n");
printf("| 1.play |\n");
printf("|----------------|\n");
printf("| 0.exit |\n");
printf("------------------\n");
}
void game()
{
char mine[ROWS][COLS] = {0};//mine:地雷
char show[ROWS][COLS] = {0};//show: 展示。0
········
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1 :
game(); break;
case 0 :
break;
default:
printf("输入错误,请重新输入\n"); break;
}
} while (input);
return 0;
}
当我们输入1,进入case 1时,将运行game()函数,game函数中的代码为具体游戏的实现
我们需要一个容器来存放游戏数据,二维数组是最好的选择,我们创建一个字符数组mine存放数据,一般扫雷游戏都是9X9的棋盘界面,所以我们可以设置行数和列数都为9,但在实际代码运行中9行9列还是不够的,会出现数组下标越界,所以最好在原来的上下左右各放一行将原来的9x9数组包起,所以我们实际需要一个11x11的数组,由于我们经常会使用,不妨对它宏定义一下
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
考虑到扫雷游戏中每选择一步如果没有踩雷,都会修改字符数组mine[ROW][COL]的相应元素来显示周围的地雷个数,这种修改会对游戏造成影响,所以不妨再定义一个数组show[ROW][COL],专门显示地雷数量,将判断和显示分离开
char mine[ROW][COL]:存放放置地雷和非地雷数据,我们用字符‘0’表示安全区;字符‘1’表示地雷,通过赋值填满数组
char show[ROW][COL]:当初用来展示的数组,初始化时全部赋值为字符‘*’,每选择一步,就在相应元素处返回周围地雷个数(mine数组相应元素周围‘1’的个数)
》reset() :我们完成一局扫雷,还想下该怎么办?这时候我们构造一个函数reset(),将mine和show数组的每一个元素重置为‘0’和‘*’
该函数实际功能就是把接收到的数组中全部元素赋值为给定符号
reset()函数,遍历数组,赋值为给定字符
void reset(char arr[ROWS][COLS], char ch)//将数组的每一个元素赋值为给定值
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
arr[i][j] = ch;
}
}
}
dis_play( ):我们扫雷肯定需要“雷区”来显示我们的数据,我们通过构造一个函数dis_play()将数组打印出来来实现这一功能
dis_play()功能就是借助’|'和‘_’等特殊字符,将数组以类棋盘形式打印出来
void display(char arr[ROWS][COLS])//借助于‘|’和‘-’,将数组以游戏界面的显示打印出来
{
for (int i = 0; i <= COL; i++)
{
printf(" %d ", i);//打印行号
}
printf("\n\n");
for (int i = 1; i <= ROW; i++)//第一个和第二个for进去分别打印一行数组和一条分割线
{ //数组行为:(空格)元素(空格)|(空格)元素(空格)|(空格)元素(空格)
//分割线为: - - - | - - - | - - -
printf(" %d ", i);//打印列标
for (int j = 1; j <= COL; j++)
{
printf(" %c ", arr[i][j]);
if (j < COL)
{
printf("|");//为了美观,不打印最后一个'|'
}
}
printf("\n");
printf(" ");
for (int k = 1; k <= COL; k++)
{
printf("---");
if (k < COL)
{
printf("|");//为了美观,不打印最后一个'|'
}
}
printf("\n");
}
}
》 setmine( ):经过reset()初始化后的mine数组中是没有地雷的,我们需要利用生成随机数的知识放置地雷,这时我们构建一个函数完成
使用头文件#include
中的rand()函数可以生成一个0~32767的伪随机数,但使用rand()前先要使用srand()设置伪随机数起点
起点只要写一次,我们将srand((unsigned)time(NULL))定义在主函数,此处是一种固定的写法,time()函数需要引头文件
将rand()%9+1即%ROW+1可以生产1~9这九个随机数,可以用来做数组的行号和列标
int main()
{
int input = 0;
srand((unsigned)time(NULL));//设置起点的固定写法
do
{
meau();
scanf("%d", &input);
switch (input)
{
case 1 :
game(); break;
case 0 :
break;
default:
printf("输入错误,请重新输入\n"); break;
}
} while (input);
return 0;
}
void setmine(char arr[ROWS][COLS])//利用生成随机数的知识使用随机的行号列标在mine数组放置地雷直到将宏定义的地雷数放置完
{
int mine_number = MINE_NUMBER;
int row = rand() % 9 + 1;//生成一到九的行号
int col = rand() % 9 + 1;//生成一到九的列标
while (mine_number > 0)
{
if (arr[row][col] == '0')//用来不是地雷的地方才能放置地雷
{
arr[row][col] = '1';//安装地雷
mine_number--;
}
else
{
row = rand() % 9 + 1;
col = rand() % 9 + 1;
}
}
》player_move( ):扫雷用的棋盘打印好了,地雷放置好了下面要实现的就是移动这一个操作了,扫雷的移动只有两个结果:踩到雷还有没踩到雷,游戏扫胜利就是把除了雷之外的地块都走一次。另外,当我们每一次没有踩到雷时,要返回这个地块周围的地雷数>>
player_move()函数函数分为两个功能,第一个功能实现合法输入,输入的行号列标都应该在1~9这个范围中,另外,之前排除过的部分就不能再踩了。第二功能实现判断,踩到了雷就返回-1,没有踩到就返回1,并且应该赋值周围地雷雷数给show数组的相应元素,这个操作我们可以构造nearmine()函数完成,由于是字符型数组,nearmine()函数也要返回字符型的数字
char nearmine(char mine[ROWS][COLS], int row, int col)//返回所选坐标周围8格字符型的地雷数量
{
//return 周围8格地雷数的ascll码值用下面这种方法就可以完成
return (mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + mine[row][col - 1] + mine[row][col + 1]
+ mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] - 7 * '0');
}
int player_move(char mine[ROWS][COLS], char show[ROWS][COLS])//首先输入合法坐标然后玩家移动,每一步结果只有两个:踩到雷还有没踩到
{
int row = 0, col = 0;
int flag = 1;
do//输入合法坐标,在行号列标范围内,并且没有被走过
{
printf("\n---------------------------------------输入坐标,中间用空格隔开:>");
scanf("%d %d", &row, &col);
if (row >= 1 && row <= 9 && col >= 1 && col <= 9 && show[row][col] == '*')
flag = 0;
else
printf("输入错误\n");
} while (flag);
if (mine[row][col] == '1')//踩到雷,返回-1
return -1;
else
{
show[row][col] = nearmine(mine, row, col);//nearmine将会返回字符型的地雷数
//将用来展示的show数组的相应坐标的元素赋值为周围的地雷数
return 1;
}
}
game( )
在game()函数我们编写程序去接收player_move( )函数返回的“-1”和“1”,接收到“1”,踩到地雷,游戏结束
接收到“1”,步数count++,并且判断conut是否等于非地雷地块的数量,一旦相等,游戏胜利,并且结束
void game()
{
char mine[ROWS][COLS] ;//mine:地雷
char show[ROWS][COLS] ;
reset(mine,'0');//初始化mine为全零
reset(show,'*');//初始化reset为全*
setmine(mine);//安置地雷
//display(mine);
display(show);//展现
int count = 0;
do
{
int move = player_move(mine, show);
if (move==-1)//踩到雷游戏结束
{
printf("\n---------------------------------------踩到地雷,游戏结束>\n");
display(mine);
break;
}
else if (move == 1)//没有踩到雷则步数count加一
{
count++;
if (count == ROW * COL - MINE_NUMBER)//判断是否走完了步数,走完了游戏就成功了
{
printf("\n---------------------------------------排除地雷,游戏结束>\n");
display(mine);
break;
}
}
display(show);
} while (1);
}
整理不易,如果有帮助的话,那就给个三联吧,谢谢