目录
定义的一些变量
为了代码的可读性和可操作性,开始前要定义一些值,后续有讲解:
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define Set 10
设计思路
我们要创建两个字符串二维数组,一个用来存储雷的位置,一个用来给玩家提示信息的位置,玩家输入一个坐标,如果这个坐标是雷,就提示玩家被炸了,如果这个坐标不是雷,则提示玩家周围雷的数量,供玩家进行排雷。
为什么设计两个数组棋盘?
如果设计一个棋盘,则排雷和提示用户雷容易被干扰,代码可读性不强,而假设选择两个数组,把存储雷和排雷分开放,就可以在完成项目的同时还能使得代码可读性增强。
为什么游戏是9*9但设计数组要设计11*11?
在统计数量时,我们统计的是以该点为中心周围八个点的雷的个数,如果数组设置为9*9,那么在统计周围雷的个数时会导致越界访问,产生不必要的麻烦,因此在设计时选择11*11可以避免这样的问题。
初始化函数
棋盘被创建好了,那么就该进行初始化了,初始化后的内容就是要让用户看到的内容,在本实验的思路中,我们选择的是让用户看到的棋盘被*覆盖,但用来存放的棋盘也需要被初始化,于是初始化函数中可以加入新的参数。
为什么要把雷的棋盘初始化为0?
便于统计个数,后续的代码实现中会发现这样设置的优越性。
函数的实现:
void Initboard(char board[][COLS], int rows, int cols, char c) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) board[i][j] = c; } }
打印函数
棋盘被设置和初始化后,就可以进行打印棋盘的函数实现了,只需要打印出数组的内容即可,要注意的是为了可读性,要打印出行和列对应的数字,代码实现如下:
void Displayboard(char board[][COLS], int row, int col) { for (int i = 0; i <= row; i++) { if (i == 0) printf("%d|",i); else printf("%d ", i); } printf("\n"); for (int i = 0; i < row; i++) { printf("--"); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d|", i); for (int j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("\n"); }
布置雷
开头定义中我们定义了雷的个数,那么在实现函数时,基本思路是运用while循环,如果布置成功了就让雷的个数减一,直到雷的个数为0,此时判断结果为假,则此时就跳出循环,在进行判断时增加一些限制条件,判断坐标合法性等,综合考量就能得出下面的代码实现原理:
void Setmine(char mineboard[][COLS], int row, int col) { int num = Set; while (num) { int x = rand() % row + 1; int y = rand() % col + 1; if (mineboard[x][y] == '0') { mineboard[x][y] = '1'; num--; } } }
排除雷
来看实现的基本思路,玩家输入坐标,如果恰好是雷的位置那么玩家就被淘汰出局,如果不是雷的位置就显示周围雷的个数。
解释为什么先前初始化记数字棋盘时选择0和1:
在统计雷的个数的时候,统计的是周围八个格子的数据,如果先前我们选择的是#等作为雷的标记,那么在统计雷的时候就非常麻烦,但如果选择的是0和1,只需要把这些数据加起来最后减去一个'0'即可,这样就能把char类型的数据转换成int类型的数据,再返回到函数中即可。
那么具体的实现为:
int Count(char board[][COLS], int x, int y) { return (board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x][y - 1] + board[x][y + 1] + board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1]- 8*'0'); } void FindSets(char mineboard[][COLS], char showboard[][COLS], int row, int col) { int x=0, y=0; int nums = row * col - Set; while (nums) { printf("请输入坐标:-->"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (mineboard[x][y] == '1') { printf("被炸死了\n"); break; } else { int ret = Count(mineboard, x, y); showboard[x][y] = ret + '0'; Displayboard(showboard, ROW, COL); nums--; } } else { printf("坐标不合法,重新输入\n"); } } if (nums == 0) { printf("扫雷成功,游戏结束\n"); } }
可以存在的优化
递归函数实现展开
在实际的游戏过程中,在玩家点击一块没有雷的方格时,会将周围的部分都展开,而本文写的这个代码游戏不能达成这个效果,这里可以进行一定程度的优化,可以使用递归函数进行每个九宫格的展开。
借助图形库可视化能力更强
在实际游戏中是用了图形库的,这里只是一个简单的游戏实现,不包括图形库,图形库也可以作为代码的优化。
全部源码
#include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define Set 10 void Initboard(char board[][COLS], int rows, int cols, char c) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) board[i][j] = c; } } void Displayboard(char board[][COLS], int row, int col) { for (int i = 0; i <= row; i++) { if (i == 0) printf("%d|",i); else printf("%d ", i); } printf("\n"); for (int i = 0; i < row; i++) { printf("--"); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d|", i); for (int j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("\n"); } void Setmine(char mineboard[][COLS], int row, int col) { int num = Set; while (num) { int x = rand() % row + 1; int y = rand() % col + 1; if (mineboard[x][y] == '0') { mineboard[x][y] = '1'; num--; } } } int Count(char board[][COLS], int x, int y) { return (board[x - 1][y] + board[x - 1][y - 1] + board[x - 1][y + 1] + board[x][y - 1] + board[x][y + 1] + board[x + 1][y] + board[x + 1][y - 1] + board[x + 1][y + 1]- 8*'0'); } void FindSets(char mineboard[][COLS], char showboard[][COLS], int row, int col) { int x=0, y=0; int nums = row * col - Set; while (nums) { printf("请输入坐标:-->"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (mineboard[x][y] == '1') { printf("被炸死了\n"); break; } else { int ret = Count(mineboard, x, y); showboard[x][y] = ret + '0'; Displayboard(showboard, ROW, COL); nums--; } } else { printf("坐标不合法,重新输入\n"); } } if (nums == 0) { printf("扫雷成功,游戏结束\n"); } } void menu() { printf("*****************************\n"); printf("********* 1.game **********\n"); printf("********* 0.exit **********\n"); printf("*****************************\n"); } void game() { srand((unsigned int)time(NULL)); char mineboard[ROWS][COLS]; char showboard[ROWS][COLS]; Initboard(mineboard, ROWS, COLS, '0'); Initboard(showboard, ROWS, COLS, '*'); Displayboard(showboard, ROW, COL); Setmine(mineboard, ROW, COL); FindSets(mineboard, showboard, ROW, COL); } 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"); } } while (input); return 0; }