游戏整体思路
编写前的准备
游戏规则
菜单的打印
扫雷棋盘的显示
扫雷地雷的布置
扫雷地雷的排除以及输赢判断
代码分享
编写前的准备
在编写前同样要准备三个不同的文件
负责程序逻辑测试的 test.c源文件
负责头文件的包含以及函数声明的 game.h头文件
负责实现游戏函数实现的 game.c源文件
游戏规则
玩家在一定的方格内排雷,当排到的一颗不为雷时标出周围八个坐标的总雷数,当排到坐标为雷时,游戏失败;排除所有的非雷时,游戏胜利。
游戏菜单的打印
游戏菜单是任何游戏的需要,玩家可以在菜单中选择开始游戏或者退出游戏。
void menu() { printf("****************************\n"); printf("******** 1.play **********\n"); printf("****************************\n"); printf("******** 0.exit **********\n"); printf("****************************\n"); } int main() { int chose = 0; do { menu(); printf("请选择:>"); scanf("%d",&chose); switch (chose) { case 1: printf("扫雷\n"); break; case 0: printf("退出游戏\n"); break; default: printf("输入错误,请重新输入\n"); break; } } while (chose); return 0; }
在 test.c(负责测试游戏逻辑的.c文件)中创建一个 menu();函数,用来实现游戏菜单的显现,并在main()函数中的do{ } while()语句中调用,每当打开游戏或者是结束一局游戏后,都会调用该函数,打印一遍菜单;
将游戏菜单的打印实现后,应该实现的就是开始游戏,退出游戏的选择;
从菜单可知,键盘输入1为开始游戏,0为退出游戏,既然是键盘输入,我们选择的则为getchar( ) \ scanf( ); 在使用getchar()函数时,可能会使下一次输入时读取到 ‘ \n ’ ,所以此处选择用scanf();,并将scanf()读取到的数据值赋给 int类型变量chose内 ;并用 开关语句(多分支语句)swithc( ) case: 语句,并将变量chose作为switch()语句的判断条件进行数据的判断:若键盘输入1,则开始三子棋游戏;若键盘输入0,则提示退出游戏并退出;若键盘输入其他数,则提示输入错误,并提示重新输入选项。
同时将变量chose作为该处do{ } while( );语句的循环条件,若输入1,则恒为真,并进入游戏;输入0,则为假,跳出循环;输入其他时也恒为真,并提示重新输入。
扫雷棋盘的显示
扫雷游戏是玩家在一个棋盘中不停的排雷,在创建完棋盘后才能在棋盘中进行排雷以及显示,扫雷的棋盘也是为n*n的方格,在此先创建两个char类型的二维数组 mine[ ][ ] 和 show[ ][ ] 作为棋盘,一个数组用来显示剩余所需排雷的位置,一个数组用来布雷。
void game() { //布雷 初始化为0 char mine[ROWS][COLS]; //显示棋盘 初始化为* char show[ROWS][COLS]; //初始化棋盘 Intboard(mine, ROWS, COLS,'0'); Intboard(show, ROWS, COLS, '*'); //打印棋盘 Displayboard(show,ROW,COL); Displayboard(mine, ROW, COL); }
在game.c文件中创建一个名为game()的函数并在主函数内调用,用来测试程序的逻辑性是否正确,在该函数内创建两个二维数组,一个二维数组用来存放扫雷中的雷,一个数组用来打印该游戏的棋盘,为了代码的健壮性,此处在game.h文件中创建2#define定义的标识符常量用来定义棋盘的大小,此处演示为9*9的棋盘大小(可根据需要进行修改);游戏的规则为每找到一个非雷的坐标后,在该坐标处显示周围八个坐标的雷数量,故要检测周围八个坐标,但若是找到四边处时,会因为访问越界而报错;为了防止查找四边的坐标而导致的越界,在创建二维数组时数组的行列都应该+2,所以该处还需要再创建两个#define定义的标识符常量。
#define ROW 9//行 #define COL 9//列 #define ROWS ROW+2 #define COLS COL+2
在创建数组后,首先要进行的就是数组的初始化,为了更好的分辨雷与非雷,在mine数组中将用 ' 0 ' 来表示非雷 ' 1 ' 表示雷,为了防止数组在访问边上坐标的周围坐标中出现随机数,应将mine数组内11*11的元素都初始化为' 0 ';
void Intboard(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; } } }
在该函数内调用自定义函数Intboard( )函数并在game.c文件中实现该函数,遍历该数组的每个元素并将该数组内每个元素都初始化为相应的字符;
在初始化数组后可以试着将棋盘打印出来;
void Displayboard(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("-------扫雷-------\n"); for (j = 0; j <= col; j++) { printf("%d ",j); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ",i); for (j = 0; j < col; j++) { printf("%c ",board[i][j]); } printf("\n"); } }
用for循环遍历该数组,并将该数组的每一行进行打印,每打印一行打印一个 ‘ \n ’ 用来进行分层,为了使玩家能更好的看到游戏中每一行每一列,可以将每一行列的行列数打印出来(虽然初始化了11*11个元素,但在使用中只需要用中间的9*9个元素);
(该处将两个数组的内容都打印了出来)
扫雷地雷的布置
在布雷中,每次的雷的排布点都应不同,所以在该处应该选择使用随机数;
在主函数中调用srand()函数,并返回一个unsigned int 类型的时间戳;(需要包含<stdlib.h> 和 <time.h>)
srand((unsigned int) time(NULL));
创建一个函数用来布雷,并在game.c文件中实现该函数;为了方便改动游戏中雷的数量,可以在game.h文件中定义一个#define定义的标识符常量作为雷的数量,此处用EASY_COUNT作为雷的数量。
void Setmine(char board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } }
定义一个变量count,初始化值为EASY_COUNT,并将该变量作为布置雷的循环条件,每当布下一颗雷时count--;定义两个变量x,y作为rand()随机值产生的坐标,rand()函数所生成的随机数的范围为0~32767,应%行列并+1(行列数为0~8,当+1时,该范围变为1~9)。
每当该坐标处为' 0 '时,在该坐标处布下一颗雷。
地雷的排除以及输赢判断
在扫雷中,玩家以输入坐标来确定该坐标是否为雷,每找到一个非雷则访问附近坐标有几颗雷,创建一个变量初始化为非雷的坐标,玩家每找到一个非雷的坐标,该坐标-1;
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("请输入坐标进行排雷:>\n"); scanf("%d%d",&x,&y); //判断坐标的合法性 if (x > 0 && x <= row && y > 0 && 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"); break; } } }
在game()函数调用一个自定义函数Findmine ( )函数并在game.c文件中实现该函数。
创建两个变量来接收键盘上输入的坐标,当输入坐标时应先判断输入坐标的合法性,坐标输入1-9为合法,限制玩家输入的非法坐标,当玩家输入非法坐标时,则提示该坐标非法,并提示玩家重新输入。
若是玩家输入的坐标合法,应判断输入的坐标是否为雷,若该坐标为雷( '1' ),则提示 “此处为雷,游戏结束” 。
若玩家输入的坐标为非雷时,应排查周围其余八个坐标的总雷数量;
若输入的坐标为 x , y 时,其余的八个坐标分别为[x-1][y-1] , [x-1][y] , [x-1][y+1] , [x][y-1] , [x][y+1] , [x+1][y-1] , [x+1][y] , [x+1][y+1] ,该处若是有雷,则每个元素为字符 ' 1 ' , 对应ASCII表可以得知,数字0的ASCII值为0,字符' 0 '的ASCII值为48,0 +' 0 ' = ' 0 ',由此可推处数字1 + ' 0 '的值为49等于 ' 1 ' ;
在该处创建一个int返回类型的函数GetMineCount(),并返回八个坐标之和减去 8 * ' 0 ',得出的结果即为周围雷的数量;
并在show数组内将该值赋给该数组的所在坐标,在赋值时,show数组为char类型,故应在赋值时加上' 0 ' ,将所赋的值变为char类型;
再创建一个变量为win,每当找到一个非雷坐标时,win++,当win变量等于除了非雷坐标的其余坐标数时,即游戏胜利。
代码分享
game.c
#include"game.h" //初始化数组 void Intboard(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; } } /*memset(board, set, row * col * sizeof(board[5][5]));*/ } void Displayboard(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("-----扫雷-----"); printf("\n"); printf(" "); 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"); } } void Setmine(char board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } } 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("请输入坐标进行排雷:>\n"); scanf("%d%d",&x,&y); //判断坐标的合法性 if (x > 0 && x <= row && y > 0 && 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"); break; } } } int GetMineCount(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'; }
game.h
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 80 #include<stdlib.h> #include<stdio.h> #include<string.h> //初始化数组 void Intboard(char board[ROWS][COLS],int row,int col,char set); //打印棋盘 void Displayboard(char board[ROWS][COLS], int row, int col); //布雷 void Setmine(char board[ROWS][COLS],int row,int col); //排雷 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 }; //数组初始化 //初始化为0 Intboard(mine, ROWS, COLS,'0'); //初始化为* Intboard(show, ROWS, COLS,'*'); //打印棋盘 //Displayboard(mine,ROW,COL); Displayboard(show, ROW, COL); //布雷 Setmine(mine,ROW,COL); Displayboard(mine, ROW, COL); //扫雷 Findmine(mine, show, ROW, COL); } void menu() { printf("***********************\n"); printf("******* 1.play ******\n"); printf("***********************\n"); printf("******* 0.exit ******\n"); printf("***********************\n"); } int main() { srand((unsigned int)time(NULL)); int chose = 0; do { menu(); printf("请选择:>"); scanf("%d",&chose); switch (chose) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("输入错误,重新输入\n"); break; } } while (chose); return 0; }