扫雷是一个经典的小游戏,现在就用c语言来实现扫雷,其中最主要的就是对数组的使用。
文件的创建
创建不同的模块能使代码更有逻辑和简洁,并且还可以隐藏一些程序。
test.c:作为测试文件,主要用来对整个项目的测试;game.c:游戏文件,主要用来存放书写关于游戏实现的程序--埋雷,扫雷等等;game.h:作为游戏头文件,主要用来存放头文件、游戏函数的声明和定义字符等。
先把游戏开始之前的程序写一写:
void game()
{
}
void menu()
{
printf("----------------------\n");
printf("------1. play--------\n");
printf("------0. exit--------\n");
printf("----------------------\n");
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("离开游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}while(input);
}
int main()
{
test();
return 0;
}
大体思路
1.扫雷就要有雷,雷要被存放在一个二维数组中,创建一个九行九列的二维数组。用来做九行九列的扫雷界面。用0表示没有雷,用1表示有雷,所以应该是整形的数组。但是在扫雷过程中如果没有碰到雷,又要展示出周围雷的数目,如果周围恰好有一个雷,要展示出1,那就可能会和有雷的1会混淆。那么问题又来了,如果用其他的字符会非常的混乱,所以选择创建两个数组,一个用来埋雷,一个用来展示扫雷的情况。这时两个数组就应该严格地相同,以便操作。展示的数组要用‘*’来初始化界面,所以数组的类型应该为字符型数组。
2.第二步就该初始化数组了,用来埋雷的先初始化为全‘0’,用来展示扫雷情况的初始化为全‘*’。
3.第三步埋雷,这个就需要通过下标操作数组,随机生成数字用来做埋雷元素的下标,这里注意千万不要越界(溢出数组)。
4.第四步排雷,仍然通过下标排雷。在这里考虑,排雷时,如果没有排到雷,需要知道周围有几个雷,如果排查的元素正好在中间还好说,排查周围8个元素就可以,如果在边界上呢,又会越界访问。为了避免这种情况发生,在创建数组时就多创建两行,防止数组访问越界,多出来的两行只要没有雷,都是‘0’就行了,只是在打印的时候不要打印出来。在game.h中定义符号
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
?1.初始化数组
//test.c文件下书写
void game()
{
//创建数组
char mine[ROWS][COLS] = { 0 };//埋雷专用
char show[ROWS][COLS] = { 0 };//展示雷所用
//初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
}
//game.c下写函数
//初始化数组
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
?2.打印界面
//打印
void Display_board(char board[ROWS][COLS], int row, int col)
{
//应该是1~9
int i = 0;
int j = 0;
//打印列号
for (i = 0; i <= col; i++)
{
printf("%d ", i);
if (i == 0)
printf("|");
}
printf("\n");
//打印分割行
for (i = 0; i <= col; i++)
{
printf("—");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d |", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
?打印界面的时候一定记得是1~9行,1~9列,这里在传参和给变量赋值时要特别注意。
3.埋雷
//game.c下的书写代码
//埋雷
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = MINE_COUNT;//小细节
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
在这里有个小细节,就是埋雷的个数,仍然使用的是宏定义的字符,要在game.h中定义,这样就方便了以后更改埋雷的个数(游戏难度)?。
并且如果想要获得随机的数字是小于某一个数的,就取最大数的余数,这个余数肯定是小于最大的数的,并且小1。
4.扫雷
逻辑的实现还是比较清晰的,输入坐标,超出范围重新输入;在范围内进入循环,看看选中的是否是雷,如果是雷,游戏结束,打印mine存放雷的数组,循环结束,如果不是雷,就要打印排除雷的情况的数组,这里就要打印周围雷的个数。如果一直能玩下去,那么游戏胜利的条件就是找到没埋雷的所有元素,需要设计循环结束的条件。
//计算周围的雷个数
static int Get_mine_count(char board[ROWS][COLS], int x, int y)
{
return board[x - 1][y - 1]
+ board[x - 1][y]
+ board[x - 1][y + 1]
+ board[x][y - 1]
+ board[x][y + 1]
+ board[x + 1][y - 1]
+ board[x + 1][y]
+ board[x + 1][y + 1] - 8 * '0';
}
//扫雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < col * row - MINE_COUNT)
{
printf("请输入坐标>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] != '1')
{
int a = Get_mine_count(mine, x, y);
//计算
show[x][y] = a + '0';
Display_board(show, row, col);
win++;
if (win == col * row - MINE_COUNT)
printf("恭喜你,排除掉了所有的雷\n");
}
else
{
printf("很遗憾,你被炸死了");
Display_board(mine, row, col);
break;
}
}
else
{
printf("坐标非法,请重新输入");
}
}
}
?在这里有个小技巧,就是打印坐标周围雷的数目时。在设计时就很巧妙,是雷就为‘1’,不是雷就为‘0’,但是数组的类型是字符型的,所以简单的相加肯定是不行的。我们知道,0~9的整形数字加‘0’就是字符的‘0’~‘9’。那么8个字符相加减去8个‘0’就应该是整形的雷的个数了吧。但在打印时仍有必要加上‘0’,因为数组本身就是字符类型的,不能打印整形。
ASCII码值本质上就是整形数字。所以0+48=‘0’ == 0+‘0’ =‘0’。
?5.完整代码
//game.h
#include<stdio.h>
#define ROWS ROW+2
#define COLS COL+2
#define MINE_COUNT 78
//初始化数组
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印雷盘
void Display_board(char board[ROWS][COLS], int row, int col);
//埋雷
void Setmine(char board[ROWS][COLS], int rows, int cols);
//扫雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//test.c
#include"game.h"
void game()
{
//创建数组
char mine[ROWS][COLS] = { 0 };//埋雷专用
char show[ROWS][COLS] = { 0 };//展示雷所用
//初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//埋雷
Setmine(mine, ROW, COL);
//打印雷盘
Display_board(mine, ROW, COL);
//扫雷
Findmine(mine,show, ROW, COL);
}
void menu()
{
printf("----------------------\n");
printf("------1. play--------\n");
printf("------0. exit--------\n");
printf("----------------------\n");
}
void test()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("离开游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}while(input);
}
int main()
{
test();
return 0;
}
//game.c
#include"game.h"
//初始化数组
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印雷盘
void Display_board(char board[ROWS][COLS], int row, int col)
{
//应该是1~9
int i = 0;
int j = 0;
//打印列号
for (i = 0; i <= col; i++)
{
printf("%d ", i);
if (i == 0)
printf("|");
}
printf("\n");
//打印分割行
for (i = 0; i <= col; i++)
{
printf("—");
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d |", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//埋雷
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = MINE_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//计算周围的雷个数
static int Get_mine_count(char board[ROWS][COLS], int x, int y)
{
return board[x - 1][y - 1]
+ board[x - 1][y]
+ board[x - 1][y + 1]
+ board[x][y - 1]
+ board[x][y + 1]
+ board[x + 1][y - 1]
+ board[x + 1][y]
+ board[x + 1][y + 1] - 8 * '0';
}
//扫雷
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < col * row - MINE_COUNT)
{
printf("请输入坐标>");
scanf("%d %d", &x, &y);//错误
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] != '1')
{
int a = Get_mine_count(mine, x, y);
//计算
show[x][y] = a + '0';
Display_board(show, row, col);
win++;
if (win == col * row - MINE_COUNT)
printf("恭喜你,排除掉了所有的雷\n");
}
else
{
printf("很遗憾,你被炸死了");
Display_board(mine, row, col);
break;
}
}
else
{
printf("坐标非法,请重新输入");
}
}
}
|