1.游戏概述
该游戏有9*9个格子(在头文件game.h中玩家可通过改变ROW COL的值来改变棋盘行列数),格子中已随机布置生成10颗雷(玩家也可通过改变EASY_COUNT的值来自行改变雷的数目),test.c中的文件实时记录已经排查过的格子个数,当玩家把雷全部找出且没有选中雷时,游戏胜利,否则,游戏失败!
2.游戏设计
本游戏代码设计通过两个棋盘来完成,在mine棋盘中存储雷的信息,0表示非雷,1表示为类;在show棋盘中初始状态全为’*‘,当玩家开始游戏后,排查出的不是雷的位置有两种情况,1是该位置为数字,即表示该位置周围的雷的数量,2是空格,即表示该位置周围没有雷
3.代码实现
请读者先按照test.c文件来理清游戏逻辑,再去game.c文件中进一步探究步骤的具体实现(game.h只是头文件及函数的声明)
test.c
该文件的阅读请从main函数开始梳理游戏逻辑
#include"game.h" void menu() { printf("****************\n"); printf("*****1.play*****\n"); printf("*****0.exit*****\n"); printf("****************\n"); } void game() { //定义两个棋盘 char mine[ROWS][COLS] = { 0 };//mine棋盘存放雷的信息 char show[ROWS][COLS] = { 0 };//show棋盘存放排查情况 //以下五个函数的具体实现在game.c文件中 //初始化棋盘 initBoard(mine, ROWS, COLS, '0');//0为非雷,1为雷 initBoard(show, ROWS, COLS, '*');//‘*’表示未知,‘ ’或‘数字’均表示周围8个格子雷的数量 displayBoard(show, ROW, COL);//打印棋盘 setMine(mine, ROW, COL);//随机设置雷 fineBoard(mine, show, ROW, COL);//玩家扫雷 } int main() { system("color f4");//实现输出框背景及字体的颜色改变,具体实现见博主博客:https://editor.csdn.net/md/?articleId=121968165 srand((unsigned int)time(NULL));//此处不做解释,后边解释 int input = 0; do//设置循环,玩家选择是否游戏 { menu();//菜单打印 printf("请选择>\n"); scanf("%d", &input); switch (input) { case 1: printf("游戏开始\n"); game();//玩家选择1则进入游戏 break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,请重新选择\n"); break; } } while (input); }
game.h(该文件只是头文件引用及函数的声明,无需过多阅读)
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<windows.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 displayBoard(char board[ROWS][COLS], int row, int col); void setMine(char mine[ROWS][COLS], int row, int col); void fineBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
initBoard(初始化棋盘)
两个棋盘传过来棋盘数组及行列数进行初始化,还有一个字符参数用set变量接受,mine棋盘全部初始化为‘0’,show棋盘全部初始化为‘*’
void initBoard(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = set; } } }
displayBoard(棋盘打印)
该部分就是通过循环将棋盘打印,以下为样图
void displayBoard(char board[ROWS][COLS], int row, int col) { printf("---------------------------\n"); printf("---------------------------\n"); int i, j; for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); for (i = 0; i <= COL; i++) { printf("| %d ", i); } printf("|\n"); for (i = 0; i < ROW; i++) { for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); printf("| %d ", i + 1); for (j = 0; j < COL; j++) { printf("| %c ", board[i+1][j+1]); } printf("|\n"); } for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); }
setMine(随机安放雷在mine棋盘上)
count为雷的数量,玩家可通过更改game.h文件中EASY_COUNT的值来自行改变雷的数目
void setMine(char mine[ROWS][COLS], int row, int col) { int x = 0, y = 0, count = EASY_COUNT; while (count) {//此处解释srand((unsigned int)time(NULL)),是为了返回时间戳以随机生成坐标,将该位置设置为雷 x = rand() % row + 1; y = rand() % col + 1; if (mine[x][y] == '0') {//如果该位置为0,将该位置置为1,count-1,因为可能随机生成同样的数字,所以,循环次数>=count mine[x][y] = '1'; count--; } } }
fineBoard(玩家扫雷)
实现这一步需要三个函数fineBoard,expandBoard,getCount来共同完成
fineBoard
void fineBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { printf("请输入你要排查的位置>\n"); int x, y; int win = 0;//win的值实时记录已知非雷格子数,当win的值与row*col-EASY_COUNT值相等时,雷被排完,游戏结束 while (win<row*col-EASY_COUNT)//设置循环让玩家进行排雷,不断增加win的值 { scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col)//判断输入坐标合法性 { if (mine[x][y] == '1')//若为1,即被炸 { printf("很遗憾,你被炸死了!\n"); displayBoard(mine, ROW, COL); break; } else { expandBoard(mine, show, x, y,&win);//该函数下边解释 displayBoard(show, ROW, COL); } } else { printf("你输入的坐标非法,请重新输入\n"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你获得胜利!\n"); displayBoard(mine, ROW, COL); } }
expandBoard(设置循环递归展开)
进入该函数是因为该位置不是雷才会进入,如果该位置周围有雷则该位置对应show棋盘变为数字,不会进入递归,若周围无雷则才会经过判断后进行递归展开
static void expandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int*win) {//因为需要通过递归展开,所以递归的位置每次进入时先判断位置的合法性 if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { if (show[x][y] == ' ' || show[x][y] != '*') return;//排除已经检查过的点,避免形成死递归 else if (getCount(mine, x, y) != 0)//getCount()函数下边解释 {//若该位置周围有雷也不会进行递归,该位置对应show棋盘变为数字,win的值+1(也是排查了一个位置的情况) show[x][y] = getCount(mine, x, y) + '0'; (*win)++; return; } else//该位置周围雷数量为0时才会进入这一步 { show[x][y] = ' ';//将该位置置为‘ ’,并让win+1 (*win)++; for (int i = -1; i <= 1; i++)//设置循环将该位置所在九宫格的所有格子进行递归 { for (int j = -1; j <= 1; j++) {//当i=0,j=0时不会进入递归,因为此时该位置在循环上边已经将其置为‘ ’,避免形成死递归 expandBoard(mine, show, x + i, y + j, win); } } } } }
getCount(计算当前位置周围雷的数量)
通过设置循环将该位置的周围八个位置计算,因为棋盘数组为字符,则需要通过-‘0’将其转变为int型
static int getCount(char mine[ROWS][COLS], int x, int y) { int i, j; int count = 0; for (i = -1; i <= 1; i++)//该循环将该位置所在九宫格进行排查 { for (j = -1; j <= 1; j++) { count = count+mine[x + i][y + j]-'0'; } } return count; }
以下为game.c文件全部源码
#include"game.h" void initBoard(char board[ROWS][COLS], int rows, int cols, char set) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = set; } } } void displayBoard(char board[ROWS][COLS], int row, int col) { printf("---------------------------\n"); printf("---------------------------\n"); int i, j; for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); for (i = 0; i <= COL; i++) { printf("| %d ", i); } printf("|\n"); for (i = 0; i < ROW; i++) { for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); printf("| %d ", i + 1); for (j = 0; j < COL; j++) { printf("| %c ", board[i+1][j+1]); } printf("|\n"); } for (j = 0; j <= COL; j++) { printf("|---"); } printf("|\n"); } void setMine(char mine[ROWS][COLS], int row, int col) { int x = 0, y = 0, count = EASY_COUNT; while (count) { x = rand() % row + 1; y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } static int getCount(char mine[ROWS][COLS], int x, int y) { int i, j; int count = 0; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { count = count+mine[x + i][y + j]-'0'; } } return count; } static void expandBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int*win) { if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { if (show[x][y] == ' ' || show[x][y] != '*')//排除已经检查过的点 return; else if (getCount(mine, x, y) != 0) { show[x][y] = getCount(mine, x, y) + '0'; (*win)++; return; } else { show[x][y] = ' '; (*win)++; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { expandBoard(mine, show, x + i, y + j, win); } } } } } void fineBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { printf("请输入你要排查的位置>\n"); int x, y; int win = 0; while (win<row*col-EASY_COUNT) { 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 { expandBoard(mine, show, x, y,&win); displayBoard(show, ROW, COL); } } else { printf("你输入的坐标非法,请重新输入\n"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你获得胜利!\n"); displayBoard(mine, ROW, COL); } }