目录
🤖前言
🤖正文
🤖主函数部分(test.c)
👾main函数
👾menu函数
👾game函数
👾主函数部分所需要的引用
🤖自定义头文件部分(game.h)
👾自定义头文件部分所需引用
🤖功能实现函数部分(game.c)--- 基础版
👾功能实现函数部分所需引用
👾1.初始化函数
👾 2.打印函数
👾3.布置雷函数
👾4.排查雷函数
👾 5.统计雷函数-排查雷函数的辅助函数
👾6.效果展示
🤖主函数部分代码(test.c)
🤖自定义头文件部分代码(game.h)
🤖功能实现函数部分(game.c)
🌌进阶版(屏幕清理、爆炸展开、雷点标记)
🌏屏幕清理
🌏爆炸展开
🌏雷点标记
🌏效果
🌌功能实现函数部分(game.c)--- 进阶版代码
🤖总结
🤖前言
💣扫雷,一款微软在上世纪九十年内置于windows的小游戏,玩法很简单,根据提示避开所有雷区即可获胜,原理也很简单:通过两个数组和功能实现函数的配合,实现踩雷结束,否则提示的基本逻辑。如今在win11上已经看不见这款小游戏了,根据第一性原则,没有我们就去创造,下面让我们一起看看扫雷是怎样诞生的😀。
🤖正文
同样的我们会有三个文件:
test.c主函数源文件、game.h自定义头文件、game.c功能实现源文件
本文将会介绍两个版本:基础版🛩️和进阶版(屏幕清理+爆炸展开+雷点标记)✈️,建议选择根据自身能力食用更佳🌼。
因为进阶版是由基础版改变化而来,所以我们先系统性的介绍基础版。🎆
🤖主函数部分(test.c)
主函数部分是我们程序的入口,里面包含了main主函数、game游戏函数和菜单函数🏄♂️
👾main函数
不管什么程序,总是从main函数开始,再从main函数结束,所以我们先来看看main函数部分
👾menu函数
上面的main函数中用到了这个函数,这个函数及其简单,就是负责打印出提示信息
—
👾game函数
game函数是功能实现函数控制中心,负责各项功能的调用,同时负责两个数组的创建
主函数部分就介绍完毕了,下面让我们来看看自定义头文件部分:
注意:为了保证文章的可阅读性,所涉及代码将会在文章后半部分统一展出。
👾主函数部分所需要的引用
好的功能少不了全的引用,因此头文件的引用也是很讲究的。🎟️🎟️🎟️🎟️🎟️🎟️🎟️🎟️🎟️
🤖自定义头文件部分(game.h)
这个文件存在的意义就是架起一座主函数与功能实现函数沟通的桥梁,因此里面主要是各种常量的定义、头文件的引用及功能实现函数的声明。✨✨✨
👾自定义头文件部分所需引用
🤖功能实现函数部分(game.c)--- 基础版
这个部分是整个程序的灵魂所在,比较难理解的也是这个部分,里面包含了之前头文件中声明的所有函数,还有部分辅助函数,我们的进阶版主要就是对此部分进行改动,实现更复杂的功能。没关系,我们可以慢慢学🌠。
👾功能实现函数部分所需引用
跟主函数部分一模一样,甚至可以直接搬用上面的图😂
👾1.初始化函数
👾 2.打印函数
👾3.布置雷函数
👾4.排查雷函数
👾 5.统计雷函数-排查雷函数的辅助函数
👾6.效果展示
🤖主函数部分代码(test.c)
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //扫雷 void menu() { printf("------------------------\n"); printf(" 扫雷游戏 \n"); printf("************************\n"); printf("******* 1.Play *******\n"); printf("******* 0.Exit *******\n"); printf("************************\n"); } void game() { //真实雷区-mine //镜像雷区-show char mine[ROWS][COLS] = { 0 };//真实布置雷的数组 char show[ROWS][COLS] = { 0 };//展示给玩家看的数组 init_board(mine, ROWS, COLS, '0');//初始化真实雷区 init_board(show, ROWS, COLS, '*');//初始化展示雷区 //display_board(mine, ROW, COL);//打印真实雷区,大小变为9 display_board(show, ROW, COL);//打印镜像雷区,大小变为9 put_boom(mine, ROW, COL);//布置雷置真实雷区,大小为9 //display_board(mine, ROW, COL);//打印真实雷区,大小变为9 find_boom(mine, show, ROW, COL);//排查雷,两个数组都要用到 } 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.h)
#pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #include<windows.h> #include<assert.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define Boom 10 void init_board(char board[][COLS], int rows, int cols, char ret);//初始化函数 void display_board(char board[][COLS], int row, int col);//打印函数 void put_boom(char mine[][COLS], int row, int col);//布置雷置真实雷区,大小为9 void find_boom(char mine[][ROWS],char show[][COLS], int row, int col);//排查雷,两个数组都要用到
🤖功能实现函数部分(game.c)
//基础版本 void init_board(char board[][COLS], int rows, int cols, char ret)//初始化函数 { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = ret; //因情况而定,初始为'0'或'*' } } } void display_board(char board[][COLS], int row, int col)//打印函数 { int i = 0; int j = 0; for (j = 0; j <= row; 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 put_boom(char mine[][COLS], int row, int col)//布置雷置真实雷区,大小为9 { int count = Boom; while (count) { int x = rand() % row + 1;//模9不超过9,+1确保能在正确位置 int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } int sta_boom(char mine[][COLS], int x, int y)//统计雷 { int i = 0; int j = 0; int count = 0; for (i = -1; i < 2; i++) { for (j = -1; j < 2; j++) { if (mine[x + i][y + j] == '1') count++; } } return count; } void find_boom(char mine[][ROWS], char show[][COLS], int row, int col)//排查雷,两个数组都要用到 { int x = 0; int y = 0; int count = row*col-Boom; while (count) { printf("请开始排查雷:>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] == '*') { if (mine[x][y] == '1') { printf("很遗憾你被炸死了\n"); display_board(mine, ROW, COL); //打印真实雷区,大小变为9 printf("游戏结束!\n"); break; } show[x][y]=sta_boom(mine, x, y) + '0'; //传过来的数需要转成ASCII display_board(show, ROW, COL); //打印镜像雷区,大小变为9 count--; } else printf("坐标已被排查过了\n"); } else printf("坐标非法\n"); } if (!count) { printf("恭喜,所有雷已被排除\n"); display_board(mine, ROW, COL); //打印真实雷区,大小变为9 } }
🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱
接下来有请进阶版登场!🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊🎊
🌌进阶版(屏幕清理、爆炸展开、雷点标记)
进阶版比多了几个功能,而这几个功能都是在功能实现函数部分(game.c)中实现的,其他部分与基础版一模一样,因此我们不做过多赘述,挑出有改变的部分讲,当然结尾我也会把功能实现函数部分(game.c)--- 进阶版的代码展示出来。
🌏屏幕清理
这个可不是说从物理成面把我们的电脑屏幕清理干净😂,而是从软件层面,也就是我们的控制台,将不需要的部分清理干净,这样会给我们一种动态的感觉,界面也更舒服。
这里利用的是windows指令-cls,清空屏幕中的一切东西,在使用时只需要把windows.h这个头文件引好
🌏爆炸展开
正宗的扫雷都是点一个位置,从这个位置出发,展开一大片,我们的程序是否可以实现这一效果呢?当然可以,这里需要用到递归,我称这个过程为碰壁(碰雷)。就行了。
🌏雷点标记
雷点标记就是把自己认为有雷的坐标输入,然后将其镜像雷区的对应值赋为 '#' ,和雷 '*' 区分开来,是否进行标记的决定权在于自己。
🌏效果
下面让我们来看看效果如何!
—很不错,可以愉快的玩耍了,对于基础版的提示还是很大的,尤其是游玩体验上,至少不需要像基础版一样输入几十次坐标排查。,一次爆炸展开,就可以节省很多步骤🚀🚀🚀!
🌌功能实现函数部分(game.c)--- 进阶版代码
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //进阶版本 void init_board(char board[][COLS], int rows, int cols, char ret)//初始化函数 { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = ret; //因情况而定,初始为'0'或'*' } } } void display_board(char board[][COLS], int row, int col)//打印函数 { int i = 0; int j = 0; for (j = 0; j <= row; 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 put_boom(char mine[][COLS], int row, int col)//布置雷置真实雷区,大小为9 { int count = Boom; while (count) { int x = rand() % row + 1;//模9不超过9,+1确保能在正确位置 int y = rand() % col + 1; if (mine[x][y] == '0') { mine[x][y] = '1'; count--; } } } int sta_boom(char mine[][COLS], int x, int y)//统计雷 { int i = 0; int j = 0; int count = 0; for (i = -1; i < 2; i++) { for (j = -1; j < 2; j++) { if (mine[x + i][y + j] == '1') count++; } } return count; } void exp_spr(char mine[][COLS], char show[][COLS], int row, int col, int x, int y,int* pc)//爆炸展开 { if (x >= 1 && x <= row && y >= 1 && y <= col) { int num = sta_boom(mine, x, y); if (num == 0) { (*pc)--; show[x][y] = ' '; int i = 0; int j = 0; for (i = -1; i < 2; i++) { for (j = -1; j < 2; j++) { if (show[x + i][y + j] == '*') { exp_spr(mine, show, ROW, COL, x + i, y + j, pc);//递归 } } } } else { (*pc)--; show[x][y] = num + '0'; } } //鸣谢CSDN博主 野猪佩奇` 的代码分享 } void boom_mark(char show[][ROWS], int row, int col)//雷点标记 { int input = 0; while (1) { printf("是否需要进行雷点标记?(1/0)"); scanf("%d", &input); if (1 == input) { int x = 0; int y = 0; printf("请输入想标记的坐标:>"); scanf("%d %d", &x, &y); show[x][y] = '#'; break; } else if (0 == input) { printf("不做标记\n"); break; } else printf("选择错误,请重新选择\n"); } } void find_boom(char mine[][ROWS], char show[][COLS], int row, int col)//排查雷,两个数组都要用到 { int x = 0; int y = 0; int count = row*col-Boom; int ret = 0; int input = 0; while (count) { printf("请开始排查雷:>"); scanf("%d %d", &x , &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (show[x][y] == '*') { if (mine[x][y] == '1') { printf("很遗憾你被炸死了\n"); display_board(mine, ROW, COL);//打印真实雷区,大小变为9 printf("游戏结束!\n"); break; } exp_spr(mine, show, ROW, COL, x, y,&count);//爆炸展开 display_board(show, ROW, COL);//打印镜像雷区,大小变为9 boom_mark(show, ROW, COL);//雷点标记 system("cls");//屏幕清除 display_board(show, ROW, COL);//打印镜像雷区,大小变为9 } else printf("坐标已被排查过了\n"); } else printf("坐标非法\n"); } if (!count) { printf("恭喜,所有雷已被排除\n"); display_board(mine, ROW, COL);//打印真实雷区,大小变为9 } }
🤖总结
从学习分支到学习数组,再到扫雷小游戏的实现,中间经历漫长的时间,最后我花了一下午的时间,将扫雷双版本写成博客分享给大家。现在回头来看这个小游戏,无非就是分支与循环、函数、数组这三个章节的内容,连指针都很少出现,关于cls、rand、time这样的知识,只需要打开软件,搜索关键字就可以轻松学会,因此再有限的知识也有它的适用范围,比如我们的扫雷以及前面的三子棋,多练习、多理解,我们都可以写出这样的游戏!所以赶快去学习吧,C语言的世界正等着我们去探索!🛬🛬🛬🛫🛫🛫✈️✈️✈️🚀
如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正!
如果觉得作者写的还不错的话,可以点一个小小的赞,你们的支持将会是我更新的最大动力!