前言
扫雷游戏项目将预设建立一个头文件和两个源文件,如:game.c test.c game.h
test.c ——扫雷游戏的测试
game.c ——游戏函数的实现
game.h ——游戏函数的声明
下面先实现扫雷基本的功能,再尝试完整实现扫雷的功能,可根据目录跳转。
基本实现
基本还原扫雷的思路,排查一个坐标后,扫描周围八个方格的雷数
无自动排查无雷的其他方格、无标记雷的功能
代码(game.h)
#include <stdio.h> #include <windows.h> #define EASY_count 10 //简易难度下的雷的个数 设为10 #define ROW 9 //雷盘的行数 #define COL 9 //雷盘的列数 #define ROWS 11 //实际定义雷盘数组的行数和列数 #define COLS 11 //往大了定义是为了排查雷时不出现数组越界的情况 int win; //用于判断游戏是否胜利 //即当win == row * col - EASY_count时 void game(); //初始化棋盘 void Intboard(char board[ROWS][COLS], int rows, int cols, char aim); //打印棋盘 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 menu() { printf(" \n"); printf(" \n"); printf(" \n"); printf(" 扫雷游戏 \n"); printf(" 1.开始 \n"); printf(" 0.退出 \n"); printf(" \n"); printf(" \n"); printf(" \n"); } void game() { Sleep(1000); system("cls"); char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息 char show[ROWS][COLS] = { 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); } int main() { srand((unsigned int)time(NULL)); 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; }
代码(game.c)
#include "game.h" //棋盘初始化函数的定义 void Intboard(char board[ROWS][COLS],int rows, int cols, char aim) { int a, b; for (a = 0; a < rows; a++) { for (b = 0; b < cols; b++) { board[a][b] = aim; } } } //打印棋盘函数的定义 void Displayboard(char board[ROWS][COLS], int row, int col) { int a, b; for (a = 0; a <= col; a++) { printf("%d ", a); } printf("\n"); for (a = 1; a <= row; a++) { printf("%d ", a); for (b = 1; b <= col; b++) { printf("%c ", board[a][b]); } 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] != '1') { board[x][y] = '1'; count--; } } } //检测排查坐标周围的雷,加static修饰,使得其只能在当前源文件中使用 static int get_mine_count(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'; } //排查雷的函数的定义 void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { //一、输入要排查的坐标 //二、判断输入的坐标是否合理 //三、记录win值,全部排查完则显示游戏胜利 int x, y,flag = 0,win = 0; while (1) { printf("请输入要排查的雷\n坐标(行号 列号):"); scanf("%d %d", &x, &y); system("cls"); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); Displayboard(mine, row, col); do { printf("输入0进行下一步操作:"); scanf("%d", &flag); if (flag == 0) continue; else printf("输入错误,请重新输入\n"); Sleep(2500); } while (flag); break; } else { int count = get_mine_count(mine, x, y); show[x][y] = count + '0'; //显示排查的信息 Displayboard(show, row, col); win++; } } else { printf("坐标不合法,请重新输入\n"); } if (win == row * col - EASY_count) { printf("恭喜你,排雷成功\n"); Sleep(5000); break; } } }
游戏测试
修改代码:
一、
二、
运行并测试:
完整实现(递归展开,标记雷)
代码(game.h)
#include <stdio.h> #include <windows.h> #define EASY_count 10 //简易难度下的雷的个数 设为10 #define ROW 9 //雷盘的行数 #define COL 9 //雷盘的列数 #define ROWS 11 //实际定义雷盘数组的行数和列数 #define COLS 11 //往大了定义是为了排查雷时不出现数组越界的情况 //与之前相比,多了一种判定胜利的方法 int win_s; //win_s用于记录标记了多少正确的的雷 //当win_s == EASY_count时,则游戏胜利 void game(); //初始化棋盘 void Intboard(char board[ROWS][COLS], int rows, int cols, char aim); //打印棋盘 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); //优化排查雷 void get_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col,int* p); //标记雷 void Setnote(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col);
代码(test.c)
#include "game.h" void Description() { int n; printf("欢迎来到C语言扫雷游戏,此处为游戏说明\n在游戏中,‘0’表示无雷,‘1’表示有雷\n"); printf("'*'表示未被排查的区域,‘@’表示自己所标记的雷的位置\n"); printf("如果您已悉知,请按‘任意键+回车键’确定正式进入游戏选择界面"); scanf("%d", &n); system("cls"); } void menu() { printf(" \n"); printf(" \n"); printf(" \n"); printf(" 扫雷游戏 \n"); printf(" 1.开始 \n"); printf(" 0.退出 \n"); printf(" \n"); printf(" \n"); printf(" \n"); } void game() { Sleep(1000); system("cls"); char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息 char show[ROWS][COLS] = { 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); } int main() { srand((unsigned int)time(NULL)); int input = 0; Description(); 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 Intboard(char board[ROWS][COLS],int rows, int cols, char aim) { int a, b; for (a = 0; a < rows; a++) { for (b = 0; b < cols; b++) { board[a][b] = aim; } } } //打印棋盘 void Displayboard(char board[ROWS][COLS], int row, int col) { int a, b; for (a = 0; a <= col; a++) { if (a == 0) printf(" "); else printf("%d ", a); } printf("\n"); for (a = 0; a <= col; a++) { if (a == 0) printf(" "); else printf("%c ", 'v'); } printf("\n"); for (a = 1; a <= row; a++) { printf("%d > ", a); for (b = 1; b <= col; b++) { printf("%c ", board[a][b]); } 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] != '1') { board[x][y] = '1'; count--; } } } //排查周围八格总计的雷数 static int get_mine_count(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'; } //游戏排查雷的主函数 void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { //选择要进行排查雷或标记雷 //输入要排查的坐标或要标记的坐标 //判断输入的坐标是否合理 int x = 0, y = 0,flag = 0,note = 0,win = 0; int* p = &win; while (1) { printf("请选择要:0.排查雷 1.标记雷\n"); scanf("%d", ¬e); switch (note) { case 0: opt: printf("请输入要排查的雷\n坐标(行号 列号):"); scanf("%d %d", &x, &y); system("cls"); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了\n"); Displayboard(mine, row, col); do { printf("输入0进行下一步操作:"); scanf("%d", &flag); if (flag == 0) goto exit; else { printf("输入错误,请重新输入\n"); Displayboard(show, row, col); } Sleep(2500); } while (flag); break; } else { //用递归实现自动排查周围无雷的格子 get_mine(mine, show, x, y,row,col,p); //显示排查的信息 Displayboard(show, row, col); } } else { printf("坐标不合法,请重新输入\n"); Displayboard(show, row, col); goto opt; } break; case 1: Setnote(mine, show, x, y,row,col); break; } if (win == row * col - EASY_count || win_s == EASY_count) { printf("恭喜你,排雷成功\n"); win = 0; win_s = 0; Sleep(5000); exit: system("cls"); break; } } } //定义递归函数 void get_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col,int* p) { if (x > 0 && x <= row && y > 0 && y <= col) { int count = get_mine_count(mine, x, y); if (count == 0) { (*p)++; int a, b; show[x][y] = '0'; for (a = -1; a <= 1; a++) { for (b = -1; b <= 1; b++) { if (show[x + a][y + b] == '*') get_mine(mine, show, x + a, y + b, row,col,p); } } } else { (*p)++; show[x][y] = count + '0'; } } } //标记雷的函数定义 void Setnote(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int row,int col) { while (1) { printf("请输入要标记的雷的坐标:"); scanf("%d %d", &x, &y); system("cls"); if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != '0' && show[x][y] != '1') { show[x][y] = '@'; if ( mine[x][y] == '1') win_s++; break; } else { printf("输入坐标不合法,请重新输入\n"); Displayboard(show, row, col); } } Displayboard(show, row, col); }
游戏测试 *
一、测试递归展开
首先将雷数改为3个
再把雷的位置展示一下
然后输入坐标排查一次,看棋盘的情况
运行正常
二、测试雷的标记功能
我们保持雷数为3个不变
将雷盘展示出来,把所有雷都标记出来
正常运行
小结
- 对于要基本实现扫雷的简单玩法,我们要对之前学过的知识点掌握得相对熟悉并且综合运用。
- 大致掌握循环语句,选择语句等、基本的嵌套循环、宏定义、二维数组的应用、函数的声明及调用
- 而对于要实现扫雷的大部分玩法,我们则需要运用到函数递归,指针的简单运用,以及适应更加复杂的逻辑。
值得一提的是:一个小项目分模块分别实现其功能的尝试,所练习到的思路,我想未来在大项目中也是同样有用的。