扫雷游戏的代码大概有两百多行,这些代码全写在一个文件里看起来会很乱,所以采用一个头文件(game.h),两个源文件(game.c/test.c)的形式来写。(注:我采用的编译器的VS2022,本文主要以9*9棋盘来介绍。)
一.分析——代码的具体实现
问题分析
扫雷游戏有两个重要元素:雷和棋盘。以这两个元素为中心,我们会产生很多疑问:雷怎么放,棋盘怎么打印,怎么排查雷,怎么存储排查雷后得到的信息等等,接下来我们一一解决。
二.代码的主体——代码整体逻辑的体现
(我把这部分代码放在test.c文件里)
1. 主体用do while 语句
游戏是多次进行的,因此采用循环语句。根据玩游戏的顺序,首先是玩家进入界面,选择“玩”亦或“不玩”。逻辑体现到代码里就是循环体的执行要比判断语句多一次,所以采用do while语句比较合适。接下来的大部分代码就写在循环体里。那么判断语句是什么呢?怎么保证玩家选择“退出”循环就立刻终止?
C语言里0为假,非0为真,因此,我们将“exit”设置成0,“play”设置成1,玩家的选择存储到变量input里,再把它作为判断条件,这样问题就可以得到解决
2.菜单栏函数
命名为menu,函数形式为void(因为不需要返回值)
内容包含两部分:选项play和exit,提示语
1).switch语句
根据玩家的选择,会产生三种情况:play,exit和error(即输错了)。据此,我们进入不同的分支语句。相比于if语句,switch语句更合适。
2).game函数
主要构思:
首先,打印全显示为“*”的棋盘和坐标
其次,弹出提示:请输入排查的坐标
然后,打印出排查的坐标下的信息,不是雷就继续,是雷就被炸死,游戏结束
具体内容:
棋盘是二维的,所以采用二维数组来表示。
为了方便改变数组的大小,我们用宏常量“ROWS”和“COLS”来表示数组的行和列,并在头文件里将这两个变量规定为9。棋盘要有两个:一个用来储存雷;另一个用来储存玩家扫雷的信息。接着对棋盘进行初始化,并打印出来。打印棋盘时要将坐标也一并打印出来。(打印函数放在后面)
char mine[ROWS][COLS];
char show[ROWS][COLS];
Initboard(mine, ROWS, COLS, '0');//这个棋盘存放雷
Initboard(show, ROWS, COLS, '*');//这个棋盘存放排查雷的信息
void Initboard(char board[ROWS][COLS], int rows, int cols,char set)//set表示棋盘初始化的内容,这样就可以同时初始化两个棋盘
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;//棋盘初始化
}
}
}
布置雷
雷的布置肯定是随意的,于是采用rand函数(具体内容可以看我的前一篇博客“猜数字游戏”),在随机的某行某列放置雷(用‘1’来表示)。注意,雷在放置的时候可能会重复,所以要用if语句进行判断。
#define COUNT 10
void Setmine(char board[ROWS][COLS], int rows, int cols)
{
srand((unsigned int)time(NULL));//NULL 是C语⾔中定义的⼀个标识符常量,值是0
int count = COUNT;//布置10个雷
while (count)//雷布置完,循环结束
{
int x = rand() % rows + 1;//会产生0~rows-1的余数,再加1就是行数
int y = rand() % cols + 1;//会产生0~rows-1的余数,再加1就是列数
if (board[x][y] == '0')
{
board[x][y] = '1';//用'1'来代表雷
count--;
}
}
}
具体效果:
(右边是打印出的棋盘,左边是棋盘下布置的雷)
玩家排查坐标
排查坐标后,会显示两种结果:雷和周围有几个雷。是雷,游戏就结束,不是雷,就要检查周围一圈有几个雷,并把这个数字放到被排查后的坐标上,再打印出来。所以,要写一个函数实现这个功能。进行排查的时候我们会发现一个问题,棋盘边缘那一圈怎么排查?根据我们写的这个函数,在排查棋盘边缘一圈的时候,坐标会越界,所以我们在初始化数组的时候,整体给数组扩大一圈,大小设置成11*11,这也用宏常量表示。在打印棋盘,放置雷,排查坐标的时候,只需改变数组的1~9行和1~9列就行了。
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void Displayboard(char board[ROWS][COLS], int rows ,int cols)
{
int i = 0;
int j = 0;
for (i = 0; i <=cols ; i++)
{
printf("%d ", i);//打印纵坐标
}
printf("\n");
for (i = 1; i <=rows; i++)//打印数组的1~9行
{
printf("%d ", i);//在每一行的前面打印这是第几行,当作横坐标
for (j = 1; j <= cols; j++)//打印数组的1~9列
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
int Getminecount(char board[ROWS][COLS], int rows, int cols)
{
int sum = 0;
for(int x = rows - 1; x <= rows + 1; x++)
{
for (int y = cols - 1; y <= cols + 1; y++)
{
if (board[x][y] == '1')
{
sum++;//sum 表示周围雷的数量
}
}
}
return sum;
}
由于这个函数的返回值是int类型,而我们的数组是char类型,所以,在把返回值放到数组里面之前,要加上字符‘0’的ASCII码值
Getminecount(mine, a,b);
int count = Getminecount;
show[a][b] =count+'0';
Displayboard(show, ROWS, COLS);
游戏胜利的条件
当玩家将所有未放置雷的地方都排查完时,就胜利,但这个如何写到代码里去呢?雷一共有10个,棋盘的大小是9*9,也就是说棋盘上有9*9-10个坐标没有雷。只要玩家的排查次数达到这么多,游戏就肯定胜利了。因此,我们定义一个变量win,玩家每排查一次坐标,win就+1,达到9*9-10时,游戏结束。
void Findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int rows, int cols)
{
int win = 0;
while (win<ROWS*COLS-COUNT)
{
printf("请输入要排查的坐标:");
int a, b;
scanf("%d %d", &a, &b);
if (a > 0 && a <= rows && b > 0 && b <= cols)
{
if (mine[a][b] == '1')
{
printf("很遗憾,你被炸死了\n");
Displayboard(mine, ROW, COL);//打印出所有雷的信息
break;
}
else
{
int count = Getminecount(mine, a, b);;
show[a][b] = count + '0';
Displayboard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标错误,请重新输入:");
}
}
if (win == ROWS * COLS - COUNT)
{
printf("恭喜你,游戏胜利!");
}
}
具体效果:
这样,整个扫雷游戏代码就写完了。
附:
test文件里的代码:
#include"game.h"
void menu()
{
printf("************************\n");
printf("********1.paly**********\n");
printf("********0.oxit**********\n");
printf("************************\n");
printf("请选择:");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
Initboard(mine, ROWS, COLS, '0');//这个棋盘存放雷
Initboard(show, ROWS, COLS, '*');//这个棋盘存放排查雷的信息
Displayboard(show, ROW, COL);//打印棋盘
Setmine(mine, ROW, COL);
Findmine(mine,show,ROW,COL);//排查雷
}
int main()
{
int input = 0;
do
{
menu();//打印菜单栏
scanf("%d", &input);
switch (input)
{
case 1:
game();//进入游戏
break;
case 0:
printf("游戏结束,感谢参与");
break;
default:
printf("输错了,请重新输一次");
break;
}
} while (input);
return 0;
}
头文件里的代码:
#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 COUNT 10
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);
void Displayboard(char board[ROWS][COLS], int rows, int cols);
void Setmine(char board[ROWS][COLS], int rows, int cols);
int Getminecount(char board[ROWS][COLS], int rows, int cols);
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols);
(注:前面写的片段代码都集中放到“game.c”这个文件里去。)