void DisplayBoard(char arr[ROWS][COLS], int row, int col)//在展示棋盘中只需要打印九行九列内容 { int i = 0; printf("%d ",i);//在每行打印之前打印行数 for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%c",arr[i][j]); } printf("\n");//每行打印完成后换行 } }
,这可是我们可怜的高or初中生唯一可以光明正大接触电脑的荣光时刻,所以没有扫雷小程序,就自己编写一个吧hhhh。
在线扫雷游戏网址:扫雷游戏网页版 - Minesweeper
至于前面的大体框架编写部分,我已经在猜数字小游戏中写出,这里就不做过多阐述。直接1 2 3,上代码!
test.c中创立
#include"game.h"//需要包含自己设置的头文件 void menu()//打印菜单 { printf("**********************\n"); printf("**** 1.play ******\n"); printf("**** 0.exit ******\n"); printf("**********************\n"); } void game() {//完成扫雷游戏 InitBoard(mine,ROWS,COLS); ShowBoard(show,ROWS,COLS);//在头文件中进行声明 } void test()//打印测试界面 { int input = 0; do { menu(); printf("请选择>"); scanf("%d", &input);//输入1,0或其他值 switch (input) {case 1: game(); break; case 2: printf("退出游戏"); break; default: printf("选择错误,重新选择"); break; } while (input);//选择1开始游戏,选择0退出游戏 } } }
布置游戏界面
我们选择用9*9的页面来布置扫雷游戏,如果位置上有雷,布置1,无雷,布置0。此时存在一个问题,如果点开一个不是雷的数字,我们需要展现周围有几个雷,如果它的周围有一个雷,那么它应该显示1,但是此时的1是说明其有雷,还是说明其周围有一个雷呢,这会产生歧义,所以我们选择用两个棋盘放置生成的结果:一个棋盘展示游戏(给gamer)show数组('0'),另一个用来储存炸弹的个数 mine数组('*')。
并且,当我们在点击一个坐标时,它如果不是雷,会扫描周围一圈的雷的数目,如果其所排雷为如图所示的绿色图标,那么就会越界。所以我们选择增大数组,来防止越界。
如图所示,增大排雷数目。布置一个11*11的棋盘。这样就不会越界。
首先对这两个数组进行初始化,我们希望mine数组中的元素全部初始化为字符0,show数组中的元素全部初始化为*。.如果像这样初始化:char min[11][11]={'0'};就会得到第一个字符为数字0,其余全为\0的结果。
char mine[11][11]={0}; char show[11][11]={0};
文件结构设计
game.h //头文件的说明
game.c//游戏的实现
test.c//游戏的测试
扫雷游戏的代码实现
在game.h中
为头文件的创立与声明
#include<stdio.h> #include<time.h> #include<stdlib.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2//既有9又有11 void InitBoard(char board[ROWS][COLS], int rows, int cols);//初始化棋盘,传二维数组,同时还得知道行和列 void ShowBoard(char board[ROWS][COLS], int rows, int cols);//打印棋盘,在本题中只需要打印出9*9的棋盘即可 void SetMine(char board[ROWS][COLS], int rows, int cols); void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
如果需要设置不同的棋盘,且使得棋盘看上去较为美观,我们需要在占位符中操作,例如%-2c,%-2d (使得打印出的字符左对齐)
在game.c中实现创立的函数
初始化棋盘
创立void InitBoard()函数,在此函数中对每一个数字进行初始化,将其初始化为0
include "game.h"//包含自定义头文件 void InitBoard(char arr[ROWS][COLS], int rows, int cols) { int i = 0; for (i = 0; i < cows; i++) { int j = 0; for (j = 0; j < cols; j++) { arr[i][j] = '0';//将i行j列的元素初始化为0 } } }
我们将mine数组中的所有元素都初始化为0,同时,我们也希望能够将show数组中的所有元素都初始化为“*”,这就需要我们对数组再进行一次初始化,但这样显然太麻烦了。我们希望找到一种方式,使得其能够同时将两个棋盘完成初始化。这就需要我们在原先的头文件中多传入一个参数
void game() {//完成扫雷游戏 InitBoard(mine,ROWS,COLS,'0'); ShowBoard(show,ROWS,COLS,'*');//在头文件中进行声明 }
同时在game.c中对InitBoard的头文件声明中,我们需要多加入一个参数
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
并将初始化的参数进行改变:arr[i][j] = set;
打印棋盘
在对棋盘初始化完成后,我们需要对棋盘进行打印。
打印棋盘时,我们只需要打印9*9的区域即可,若打印11*11,可能玩家会误解最外围区域也是能下棋的,所以我们只打印9*9的格子区域。
void game() {//完成扫雷游戏 InitBoard(mine,ROWS,COLS,'0'); ShowBoard(show,ROWS,COLS,'*');//在头文件中进行声明 Display(mine,ROW,COL); }
include "game.h"//包含自定义头文件 void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set) { int i = 0; for (i = 0; i < cows; i++) { int j = 0; for (j = 0; j < cols; j++) { arr[i][j] = set; } } } void DisplayBoard(char arr[ROWS][COLS], int row, int col)//在展示棋盘中只需要打印九行九列内容 { int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%c",arr[i][j]); } printf("\n");//每行打印完成后换行 } }
在这些代码输入完成之后,我们可以对其进行打印,查看效果。
此时打印出的只有棋盘,对于下棋的游戏者来说体验感并不好,因为他不知道任意一个棋子的坐标,这意味着,它在每次下棋的时候,都需要自己查找棋子的坐标,因此,我们可以在每行每列前打印该行数或者列数。
include "game.h"//包含自定义头文件 void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set) { int i = 0; printf("%d ",i);//在每行打印之前打印改组的行号 for (i = 0; i < cows; i++) { int j = 0; for (j = 0; j < cols; j++) { arr[i][j] = set; } } } void DisplayBoard(char arr[ROWS][COLS], int row, int col)//在展示棋盘中只需要打印九行九列内容 {//打印列号需要在打印数组开始之前打印列号 int i=0; for(i=1;i<col;i++){ printf("%d",i); } printf("\n");//在列数打印完成之后换行 int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%c",arr[i][j]); } printf("\n");//每行打印完成后换行 } }
打印查看效果
我们可以观察到,在棋盘的最上行出现了列标与棋盘不对应的情况,我们可以在该行的前面加上一个0,来使棋盘变得美观。
我们修改代码
for(i=0;i<col;i++)
这样就能使坐标数与棋盘对应。整合我们目前设计出的代码:
include "game.h"//包含自定义头文件 void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set) { int i = 0; printf("%d ",i);//在每行打印之前打印改组的行号 for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { arr[i][j] = set; } } } void DisplayBoard(char arr[ROWS][COLS], int row, int col)//在展示棋盘中只需要打印九行九列内容 {//打印列号需要在打印数组开始之前打印列号 printf("---------扫雷游戏---------\n"); int i=0; for(i=0;i<col;i++){ printf("%d",i); } printf("\n");//在列数打印完成之后换行 int i = 0; for (i = 0; i < row; i++) { int j = 0; for (j = 0; j < col; j++) { printf("%c",arr[i][j]); } printf("\n");//每行打印完成后换行 } }
布置雷
布置雷就是在9*9的棋盘中布置雷。我们要求随机布置10个雷。
void game() {//完成扫雷游戏 InitBoard(mine,ROWS,COLS,'0'); ShowBoard(show,ROWS,COLS,'*');//在头文件中进行声明 Display(mine,ROW,COL); SetMine(mine,ROW,COL); }
void test()//打印测试界面 { int input = 0; srand((unsigned)int(NULL));//调用rand函数,参数不需要,为NULL,返回类型强制转化为unsigned类型 do { menu(); printf("请选择>"); scanf("%d", &input);//输入1,0或其他值 switch (input) {case 1: game(); break; case 2: printf("退出游戏"); break; default: printf("选择错误,重新选择"); break; } while (input);//选择1开始游戏,选择0退出游戏 } } }
void SetMine(char mine[ROWS][COLS], int row, int col)//传过来的数组为11*11 { int count = EASY_COUNT;//设置不同的游戏模式供玩家进行选择,当布置成功一个雷时,就减减一次 while (count) {//我们需要在棋盘上随机布置十个雷,横坐标范围为1到9,纵坐标范围为1到9 int x = rand() % row + 1//我们可以从数学的角度理解这个式子,13%8的结果永远不可能比8大,最大值为7,最小值为1,要让生成随机数的范围在1到8之间,我们呢还需要加上 int y = rand() % col + 1; //我们在布置雷之前需要判断,该位置是否布置过雷 if (arr[x][y] == '0') { arr[x][y] = '1'; count--; } } }
排查雷
FindMine(show,mine,ROW,COL);//在排查雷的过程中,show数组和mine数组都要传入
排查周围是否为雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col) {//传入的数组仍为11*11,但我们只需要对9*9的区域进行操作,在mine数组中排查,传入show数组中展示 printf("请输入需要排查的坐标:\n"); //排查的坐标范围必须在1到9之间,所以我们要判断坐标的有效性 while (1) { if (x >= 1 && x <= 10 && y >= 1 && y <= 10) { if ([x][y] = '1') { printf("很遗憾,你被炸死了\n"); DisplayBoard(mine, ROW, COL); break; } else { //如果该坐标不是雷,统计该坐标周围有几个雷 int count=GetMineCount=(mine,x,y); show[x][y]=count+'0';//加上‘0’才能转化为数字 Display(show,ROW,COL);//展示周围有几个雷 } } else printf("坐标非法,请重新输入!\n"); } }
展示周围雷的个数
我们如何查看周围雷的个数。假设我们排查雷的横坐标为x,纵坐标为y,那么周围九个数的横纵坐标都可以得知,便于查找。我们只要把周围九个的数字全部加起来就可以知道周围一圈雷的个数,但是,我们初始化时将周围的字符初始化为的是字符0,而不是数字0,这就使得我们无法通过相加直接得到结果。字符0的ASC码值是48,字符1的ASC码值是49,所以我们考虑到可以让两个字符相减,用得到的结果代表周围雷的个数。
void GetMineCount(char mine[ROWS][COLS], int col, int row) { 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'; }
如果我们不希望将此函数在别的函数内应用,我们还可以在它之前加上static修饰。
int GetMineCount(char mine[ROWS][COLS], int col, int row)
我们还可以采用循环的方式排查周围的雷:
static int GetMineCount(char mine[ROWS][COLS], int col, int row) //加上static只能在这个文件内部使用 { int i = 0; int count = 0; for (i = x - 1; i <= x + 1; i++) { int j = 0; for (j = y - 1; j <= y + 1; j++) { count += (mine[x][y] - '0'); } } return count; }
设置使结束游戏的代码
原先我们在while循环中的循环条件是while(1),游戏无法结束。我们产生一个想法:是否可以检测排查的次数,若排查的次数小于不是雷的个数,则算游戏成功。由此,我们设置一个变量win。
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col) {//传入的数组仍为11*11,但我们只需要对9*9的区域进行操作,在mine数组中排查,传入show数组中展示 printf("请输入需要排查的坐标:\n"); //排查的坐标范围必须在1到9之间,所以我们要判断坐标的有效性 while (win<=row*col-EASY_COUNT) { if (x >= 1 && x <= 10 && y >= 1 && y <= 10) { if ([x][y] = '1') { printf("很遗憾,你被炸死了\n"); DisplayBoard(mine, ROW, COL); break; } else { //如果该坐标不是雷,统计该坐标周围有几个雷 int count=GetMineCount=(mine,x,y); show[x][y]=count+'0';//加上‘0’才能转化为数字 Display(show,ROW,COL);//展示周围有几个雷 win++;//当排查出一个不是雷的数目win就++ } } else printf("坐标非法,请重新输入!\n"); } }
但是我们还存在一个疑问,我们在排查雷时可能多次排查同一个雷,所以我们要检查即将排查的雷是否排查过。在排查之前加上代码:
while(mine[x][y]=='*') else printf("该坐标已经被排查过,请重新输入坐标!\n);
我们还可以设置不同的游戏难度(下次再战qaq)
整合代码并运行
最后整合我们的代码,并试运行!
game.h
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<time.h> #include<stdlib.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10 void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);//初始化棋盘,传二维数组,同时还得知道行和列 void ShowBoard(char board[ROWS][COLS], int rows, int cols,char set);//打印棋盘,在本题中只需要打印出9*9的棋盘即可 void DisplayBoard(char arr[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 menu() { printf("*******************\n"); printf("***** 1. play *****\n"); printf("***** 0. exit *****\n"); printf("*******************\n"); } void game()//game.c中对于头文件的声明 { char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //初始化棋盘 //1. mine数组最开始是全'0' //2. show数组最开始是全'*' InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 //DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL);//只需要打印给玩家的棋盘即可,展示棋盘也只需要打印9*9的棋盘供玩家游戏 //1. 布置雷 SetMine(mine, ROW, COL); //2. 排查雷 FindMine(mine, show, ROW, COL);//排查雷时需要排查mine数组中的雷,同时把mine数组中排查的结果传给show数组 } 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; }
game.c
#include "game.h" void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { arr[i][j] = set;//初始化为设定的字符 } } } void DisplayBoard(char arr[ROWS][COLS], int row, int col) { int i = 0; printf("--------扫雷游戏-------\n"); for (i = 0; i <= col; i++) { printf("%d ", i); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); int j = 0; for (j = 1; j <= col; j++) { printf("%c ", arr[i][j]); } printf("\n"); } } void SetMine(char arr[ROWS][COLS], int row, int col) { //布置10个雷 //⽣成随机的坐标,布置雷 int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (arr[x][y] == '0') { arr[x][y] = '1'; count--; } } }int GetMineCount(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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win < row * col - EASY_COUNT) { printf("请输⼊要排查的坐标:>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); DisplayBoard(mine, ROW, COL); break; } else { //该位置不是雷,就统计这个坐标周围有⼏个雷 int count = GetMineCount(mine, x, y); show[x][y] = count + '0'; DisplayBoard(show, ROW, COL); win++; } } else { printf("坐标⾮法,重新输⼊\n"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你,排雷成功\n"); DisplayBoard(mine, ROW, COL); } }
运行一下: