目录
——模块组成
——编写思路
2 游戏框架中game函数
2.1 test.c内
2.2 game.h内
3 game函数的进一步扩充
3.1 定义棋盘
3.1.1 test.c内
3.1.2 game.c内
3.1.3 game.h内
3.2 初始化棋盘
3.2.1 game.h
3.2.2 game.c
3.2.3 test.c内
3.3 打印棋盘
3.3.1 game.h内
3.3.2 game.c内
3.3.3 test.c内
3.3.4 进一步修改完善
4 布置雷
4.1 test.c内
4.2 game.h内
4.3 game.c内
5 排查雷
5.1 test.c内
5.2 game.h内
5.3 game.c内
5.3.1 框架
5.3.2 get_mine_count函数的详细编写设计
5.3.3 游戏结束的编写
6 附完整代码
6.1 test.c内
6.2 game.h内
6.3 game.c内
test.c 扫雷游戏的测试逻辑
game.h 游戏函数的声明
game.c 游戏函数的实现
显而易见,9*9的棋盘——>9*9的二维数组
考虑雷的情况:
如果是右图情况,要考虑最外围的数的周边存在越界判断的情况,为了避免繁复的判断是否越界,我们可以考虑将棋盘改变为11*11的棋盘,这样排除越界的可能,简化代码。
雷的布置,置1,非雷,置0;假如一个位置的周边有一个雷,其也显示为1;为了避免混淆1的概念究竟是置雷还是指周围置一个雷,我们可以弄两个棋盘,一个放置专门布置好的雷的信息(mine),另一个显示排查出的雷的信息(show)。
mine数组初始化为“0”,布置雷时改成“1”;(这样布置的好处在5.3.2处有详细阐述)
show数组初始化为“*”,排查雷后,改为数字,例如“3”
放置好的雷 雷的显示情况
void menu() { printf(" 1.play \n"); printf(" 0.exit \n"); } int main() { 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); return 0; }
#include "game.h" void game() { printf("扫雷\n"); }
#include
运行结果为
游戏框架已经搭建完成
由开始的思路梳理知,我们需要设置两个棋盘,一个放置专门布置好的雷的信息(mine),另一个显示排查出的雷的信息(show)。
mine数组初始化为“0”,布置雷时改成“1”;
show数组初始化为“*”,排查雷后,改为数字,例如“3”
为了便于以后修改棋盘大小(例如高阶棋盘不是 9*9),我们可以使用宏定义
void game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; }
#include"game.h"
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2
//初始化棋盘 void init_board(char board[ROWS][COLS], int rows, int cols,char set);
void init_board(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 game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 //mine初始化为全"0" //show初始化为全"*" init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); }
//打印棋盘 void display_board(char board[ROWS][COLS], int row, int col);
void display_board(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; for (i = 1; i <= row; i++) { for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
void game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 //mine初始化为全"0" //show初始化为全"*" init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 display_board(mine, ROW, COL); display_board(show, ROW, COL); }
以上代码运行结果为此,为了使其更加美观,我们添上行列序号
void display_board(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; //列号 for (j = 1; j <= col; j++) { printf("%d ", j); } 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与列1的打印使棋盘的列与列序不对应,我们可以选择在列1前打印一个空格,使其位置对应,我们也可以打印0的方式,使列序对应。只需将列号j的打印从0开始即可。
void display_board(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; //列号 for (j = 0; j <= col; j++) { printf("%d ", j); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i);//行号 for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
两个棋盘中,show棋盘是我们所能看得到的棋盘,并且是我们要排雷操作后的show棋盘,所以test.c内修改为:
//打印棋盘 //display_board(mine, ROW, COL); //display_board(show, ROW, COL);
void game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 //mine初始化为全"0" //show初始化为全"*" init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 //display_board(mine, ROW, COL); display_board(show, ROW, COL); //布置雷 set_mine(mine,ROW,COL); } int main() { int input=0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏:\n"); break; default: printf("选择错误,重新选择\n"); break; } } while (input); return 0; }
main函数内使用srand函数,并引入相关头文件
#include
#include //布置雷 void set_mine(char mine[ROWS][COLS], int row, int col);
void set_mine(char mine[ROWS][COLS], int row, int col) { //布置10个雷 int count = 10; while (count) { int x = rand() % row + 1;//x是1到9 int y = rand() % col + 1;//y是1到9 if (mine[x][y] == '0')//判断是否已经是雷 { mine[x][y] = '1';//置雷 count--; } } }
void game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 //mine初始化为全"0" //show初始化为全"*" init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 //display_board(mine, ROW, COL); display_board(show, ROW, COL); //布置雷 set_mine(mine, ROW, COL) //排查雷 find_mine(mine,show,ROW, COL); }
对mine数组排查操作,其结果储存显示在show数组内,也要了解row,col的具体数值
//排查雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; printf("请输入要排查雷的坐标:>"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { } else { printf("坐标非法,请重新输入\n"); } }
重新输入的地方再次回到if判断,由此可知为循环结构,修改完善如下:
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; while (1) { printf("请输入要排查雷的坐标:>"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { } else { printf("坐标非法,请重新输入\n"); } } }
判断目标坐标周围的雷的个数,在if内我们可以封装一个实现此功能的函数,对mine数组用此函数排查,并储存在show数组内:
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; while (1) { printf("请输入要排查雷的坐标:>"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; } else { printf("坐标非法,请重新输入\n"); } } }
此处对该行代码作补充说明
show[x][y] = count + '0';
show数组类型为char,而count为int类型,我们需要将其类型转换再储存到show数组内,
通过ASCII表可知,‘2’-‘0’=50-48=2,‘9’-‘0’=57-48=9;
所以此处可知char-‘0’=count,所以show[x][y]=count+'0';
(上图左列为十进制数值,右列为字符)
在mine棋盘内,我们以字符‘1’的方式置雷类比上面单独解释的代码,我们可以通过对目标坐标的周边一圈字符进行减‘0’操作得到数值,将所有数值相加即可得到周边雷的个数,我们对此做进一步解释说明:
对黄色框内的中央位置为我们的目标x,y,对其mine数组内棋盘内雷的分布假设如图所示
以[x-1][y-1]为例,内置‘1’,即有雷分布,对其减‘0’,‘1’-‘0’=1,1可作为雷数直接储存,所以我们可以理解在文章开始时,我们对mine数组内用字符‘1’作为雷的标志的意义所在。
周边8个坐标都作上述的处理,再相加,我们可简化为
mine[x - 1][y] +mine[x - 1][y - 1] +mine[x][y - 1] +mine[x + 1][y - 1] +mine[x + 1][y] +
mine[x + 1][y + 1] +mine[x][y + 1] +mine[x - 1][y + 1]-8*'0';
所以get_mine_count函数的编写如下(game.c内):
int get_mine_count(char mine[ROWS][COLS], int x, int y) { return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1]-8*'0'); }
此函数只对mine数及其【x】【y】处作处理,所以(char mine[ROWS][COLS], int x, int y),
排雷完成后打印show棋盘:
此时若想检验已编写程序的正确性,我们可以将mine数组打印出来看一看
由此检验我们的排雷是正确的。
9*9=81的棋盘,若放置10个雷,找出非雷的71格即可游戏成功;
踩雷炸死也即结束;
对于刚刚我们输入排雷的坐标,我们需判断其是否为雷,才能进一步进行排雷,对其修改如下:
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; while (1) { printf("请输入要排查雷的坐标:>"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1' { printf("很遗憾,你被炸死了\n"); //并且打印mine数组,让玩家死的瞑目 display_board(mine, ROW, COL); //游戏结束 break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; display_board(show, ROW, COL); } } else { printf("坐标非法,请重新输入\n"); } } }
对于另一种游戏结束的方式,我们可以设置一个win变量,当win
|
game.h内
#define DEFAULT_COUNT 10
game.c内
void set_mine(char mine[ROWS][COLS], int row, int col) { //布置10个雷 int count = DEFAULT_COUNT;(修改一下) while (count) { int x = rand() % row + 1;//x是1到9 int y = rand() % col + 1;//y是1到9 if (mine[x][y] == '0')//判断是否已经是雷 { mine[x][y] = '1';//置雷 count--; } } }
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win=0; while (win
"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); //并且打印mine数组,让玩家死的瞑目 display_board(mine, ROW, COL); //游戏结束 break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; display_board(show, ROW, COL); win++; } } else { printf("坐标非法,请重新输入\n"); } if (win == row * col - DEFAULT_COUNT) { printf("恭喜你,排雷成功\n"); //打印雷的真实情况 display_board(mine, ROW, COL); } } }
为了检验上述代码,我们可以先定义DEFAULT_COUNT=80,并且看mine棋盘的情况,进行定点排雷检验代码是否正确:
game.h内
#define DEFAULT_COUNT 80
此处游戏基本完成,但是存在一个bug,如果我们每次排查的都是同一个坐标,达到一定次数之后,win++到某一个数值,也算游戏成功,我们需要再对坐标作合法判断,我们可以通过show棋盘上放置的是'*',来判断该位置未被排查过:
while (win
"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { //坐标是否被排查过 if (show[x][y] == '*') { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); //并且打印mine数组,让玩家死的瞑目 display_board(mine, ROW, COL); //游戏结束 break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; display_board(show, ROW, COL); win++; } } else { printf("该坐标已经被排查过了\n"); } } else { printf("坐标非法,请重新输入\n"); } if (win == row * col - DEFAULT_COUNT) { printf("恭喜你,排雷成功\n"); //打印雷的真实情况 display_board(mine, ROW, COL); } } }
为了符合游戏的基本设定及良好的游戏体验,我们一开始不打印mine棋盘,之前的打印为了方便代码调试,并且我们打印show棋盘,修改如下:
test.c内
//排查雷 //display_board(mine, ROW, COL); display_board(show, ROW, COL); find_mine(mine,show, ROW, COL);
游戏运行如下:
本文结束,感谢阅览。
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void menu() { printf(" 1.play \n"); printf(" 0.exit \n"); } void game() { //设置两个数组 char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 //mine初始化为全"0" //show初始化为全"*" init_board(mine, ROWS, COLS,'0'); init_board(show, ROWS, COLS,'*'); //打印棋盘 //display_board(mine, ROW, COL); //display_board(show, ROW, COL); //布置雷 set_mine(mine, ROW, COL); //排查雷 //display_board(mine, ROW, COL); display_board(show, ROW, COL); find_mine(mine,show, ROW, COL); } int main() { int input=0; srand((unsigned int)time(NULL)); do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏:\n"); break; default: printf("选择错误,重新选择\n"); break; } } while (input); return 0; }
#pragma once #include
#include #include #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define DEFAULT_COUNT 10 //初始化棋盘 void init_board(char board[ROWS][COLS], int rows, int cols,char set); //打印棋盘 void display_board(char board[ROWS][COLS], int row, int col); //布置雷 void set_mine(char mine[ROWS][COLS], int row, int col); //排查雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void init_board(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) { int i = 0; int j = 0; //列号 for (j = 0; j <= col; j++) { printf("%d ", j); } 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 set_mine(char mine[ROWS][COLS], int row, int col) { //布置10个雷 int count = DEFAULT_COUNT; while (count) { int x = rand() % row + 1;//x是1到9 int y = rand() % col + 1;//y是1到9 if (mine[x][y] == '0')//判断是否已经是雷 { mine[x][y] = '1';//置雷 count--; } } } int get_mine_count(char mine[ROWS][COLS], int x, int y) { return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1]-8*'0'); } void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win
"); scanf("%d %d", &x, &y); //排查区域在11*11棋盘中间的9*9范围内 if (x >= 1 && x <= row && y >= 1 && y <= col) { //坐标是否被排查过 if (show[x][y] == '*') { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); //并且打印mine数组,让玩家死的瞑目 display_board(mine, ROW, COL); //游戏结束 break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; display_board(show, ROW, COL); win++; } } else { printf("该坐标已经被排查过了\n"); } } else { printf("坐标非法,请重新输入\n"); } if (win == row * col - DEFAULT_COUNT) { printf("恭喜你,排雷成功\n"); //打印雷的真实情况 display_board(mine, ROW, COL); } } }