本篇博客主要讲一下简单版本的扫雷。
目录
2.1 初始化棋盘模块 —— void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
2.2 显示棋盘函数 —— void DisplayBoard(char board[ROWS][COLS], int row, int col);
2.3 埋雷函数模块 —— void SetMine(char mine[ROWS][COLS], int row, int col, int count);
1.扫雷项目的整体设计
首先,还是和三子棋一样,我们先要搭起一个大的框架,从刚开始的简易用户界面,然后到选择不同选项的结果,最后是具体的游戏内容模块的编写。
大致的框架:
#include<stdio.h>
int main()
{
int input = 0;
do
{
menu(); //菜单
printf("请选择》");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏!");
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input);
return 0;
}
大致的运行流程:
程序先运行起来,然后通过输入,来判断是不是进行游戏。
而这里的menu(简易菜单函数),我们可以按照自己的想法,来做一个简易菜单,如:
void menu()
{
printf("****************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("****************************\n");
}
以上做完之后,就该详细的设计game函数中的功能模块了。
2.game函数中的功能模块实现
2.1 初始化棋盘模块 —— void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
这个模块主要的作用就是在游戏开始时,把棋盘中初始化成我们希望的内容,好方便我们后面的步骤。
这里要注意的就是,扫雷和三子棋有点不一样的地方是,三子棋只需要一个棋盘,而扫雷我们需要两个棋盘,也就是两个二维数组来操作(一个用来存放雷的信息,另一个用来存放排雷的信息),这样就可以使整体结构更清晰简单。因为是两个二维数组,所以在设计这个初始化函数时,要有棋盘的首元素地址、行、列和所需要初始化成的字符。
详细代码如下:
//声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
//定义
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ret; //把想初始化的内容放到棋盘里面
}
}
}
2.2 显示棋盘函数 —— void DisplayBoard(char board[ROWS][COLS], int row, int col);
顾名思义,这个函数就是单纯的打印棋盘,这样我们可以看到在排雷过程中的内容。
详细代码如下:
//声明
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//定义
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("------------------------------------\n"); //分割线
int i = 0;
for (i = 0; i <= row; i++) //列的坐标
{
printf("%d ", i);
}
printf("\n"); //坐标和棋盘之间用换行隔开
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i); //把行的坐标也给打印出来
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("------------------------------------\n"); //分割线
}
在设计这个函数时,我们也可以想上面的代码一样,上下可以加条分割线,同时打印出行和列的坐标,使界面效果更好。
而接下来就是最后的两个环节了 —— 埋雷 和 排雷。
2.3 埋雷函数模块 —— void SetMine(char mine[ROWS][COLS], int row, int col, int count);
代码如下:
//声明
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count);
//定义
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
while (count)
{
x = rand() % row + 1; //0 - 8 加1就变成了 1 - 9
y = rand() % col + 1;
if (mine[x][y] != '1') //判断该坐标有没有雷
{
mine[x][y] = '1'; //没雷,就埋雷
count--; //成功埋雷,总数减1,直到count为0时,停止
}
}
}
这里的埋雷函数模块中间使用了rand函数,所以在main函数中我们使用srand函数和time函数来设置一个起始时间。
//用时间戳来确定初始时间
srand((unsigned int)time(NULL));
2.4 排雷函数模块 —— void MoveMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);
这个模块的内容就是真正的游戏核心了。
代码如下:
//声明
//排雷
void MoveMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);
//定义
//排雷
void MoveMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
int ret = 0;
while (1) //要进行多次排雷,所以这里用的是循环
{
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col) //判断是不是合法坐标
{
if (mine[x][y] == '1') //判断该坐标是不是雷
{
printf("很遗憾,你被炸死了!\n");
break;
}
else if (show[x][y] != '*') //判断该坐标是不是已经被排过雷
{
printf("坐标已被占用,重新输入!\n");
}
else
{
//统计周围雷的个数
show[x][y] = TotalMine(mine, x, y) + '0';
//打印棋盘
DisplayBoard(show, ROW, COL);
ret++;
if (ret == (row * col - count)) //如果所有的雷被找出
{
printf("恭喜你,排雷成功!\n"); //游戏成功
DisplayBoard(mine, ROW, COL);
break;
}
}
}
else
{
printf("坐标不合法!\n");
}
}
}
注意:在设计排雷函数时,我们又设计了一个统计函数(用来统计所排坐标周围雷的个数)
代码如下:
//声明
//统计周围雷的个数
int TotalMine(char mine[ROWS][COLS], int x, int y);
//定义
//统计周围雷的个数
int TotalMine(char mine[ROWS][COLS], int x, int y)
{
return ((mine[x - 1][y - 1]) +
(mine[x - 1][y]) +
(mine[x - 1][y + 1]) +
(mine[x][y - 1]) +
(mine[x][y + 1]) +
(mine[x + 1][y - 1]) +
(mine[x + 1][y]) +
(mine[x + 1][y + 1])) - (8 * '0');
//原理就是把该坐标周围的坐标中的内容加起来,然后减去8个初始化的字符的值,最后得到雷的个数
}
当完成以上的内容后,就可以把这些模块在 game.c 文件中连接起来,当然,我们在连接时还可以添加一些函数用来使我们的界面更简洁明了。
连接后的源代码如下:
//test.c文件中的内容
#include"game.h"
void menu()
{
printf("****************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("****************************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 }; //存放雷的信息
char show[ROWS][COLS] = { 0 }; //存放排除雷的信息
//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印棋盘
DisplayBoard(show, ROW, COL);
//埋雷
SetMine(mine, ROW, COL, EASY_MODE);
//DisplayBoard(mine, ROW, COL);
//排雷
MoveMine(mine, show, ROW, COL, EASY_MODE);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu(); //菜单
printf("请选择》");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");
game();
break;
case 0:
system("cls"); //清屏
printf("退出游戏!\n");
break;
default:
printf("选择错误,重新选择!\n");
Sleep(1000);
system("cls");
break;
}
} while (input);
return 0;
}
//game.h中函数声明
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9
#define COL 9
#define EASY_MODE 10
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count);
//排雷
void MoveMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);
//统计周围雷的个数
int TotalMine(char mine[ROWS][COLS], int x, int y);
//game.c中函数的定义
#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = ret;
}
}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("------------------------------------\n"); //分割线
int i = 0;
for (i = 0; i <= row; i++) //列的坐标
{
printf("%d ", i);
}
printf("\n"); //坐标和棋盘之间用换行隔开
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i); //把行的坐标也给打印出来
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("------------------------------------\n"); //分割线
}
//埋雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
while (count)
{
x = rand() % row + 1; //0 - 8 加1就变成了 1 - 9
y = rand() % col + 1;
if (mine[x][y] != '1')
{
mine[x][y] = '1';
count--;
}
}
}
//统计周围雷的个数
int TotalMine(char mine[ROWS][COLS], int x, int y)
{
return ((mine[x - 1][y - 1]) +
(mine[x - 1][y]) +
(mine[x - 1][y + 1]) +
(mine[x][y - 1]) +
(mine[x][y + 1]) +
(mine[x + 1][y - 1]) +
(mine[x + 1][y]) +
(mine[x + 1][y + 1])) - (8 * '0');
}
//排雷
void MoveMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
int ret = 0;
while (1)
{
printf("请输入排雷坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
Sleep(2000);
system("cls");
break;
}
else if (show[x][y] != '*')
{
printf("坐标已被占用,重新输入!\n");
}
else
{
//统计周围雷的个数
show[x][y] = TotalMine(mine, x, y) + '0';
system("cls");
//打印棋盘
DisplayBoard(show, ROW, COL);
ret++;
if (ret == (row * col - count))
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, ROW, COL);
Sleep(5000);
system("cls");
break;
}
}
}
else
{
printf("坐标不合法!\n");
}
}
}