一.你知道什么是模块化程序设计吗~
1.概述:
模块化程序设计(多文件编程)是指在进行程序设计时,将一个大程序按照功能划分为若干小程序模块,每个小程序模块完成一个特定的功能,然后在这些小程序模块之间建立必要的联系,通过这些小程序模块的互相协作,完成整个大程序功能的程序设计方法。
这里需要说明的是,模块化程序设计(开发)并非专指多文件编程,模块化程序设计(开发)的重点在于小功能模块的设计和协作,多文件编程只是将(一个或多个)“小程序模块”打包在单独的文件中,以文件的形式进行呈现,形成了多文件编程形式。
2.传统的编程方式
所有的代码都放在.c文件里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的的组织和管理,而且很影响编程者的思路,更甚,别人是很不容易看懂这样的代码的,与其说不愿意,倒不如是不愿意~
这里是我写扫雷小游戏时的代码,我先把它们放在一块看看是什么效果~(大概看一下就行,不用理解,只是做一个示范~)
#pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10 //初始化棋盘函数声明 void init_board(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘函数声明 void display_board(char board[ROWS][COLS], int row, int col); //布置雷函数声明 void set_mine(char board[ROWS][COLS], int row, int col); //找雷函数的声明 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); void menu() { printf("欢迎来到 ^_^ 扫雷小游戏 \n"); printf(" 1.play \n"); printf(" 0.exit \n"); printf("请选择:"); } void game() { //定义俩个数组 char mine[ROWS][COLS];//布置雷 char show[ROWS][COLS];//显示排雷信息 //初始化俩个棋盘 init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*'); //布置雷 set_mine(mine, ROW, COL); //打印棋盘 display_board(mine, ROW, COL); display_board(show, ROW, COL); //找雷 find_mine(mine, show, ROW, COL); } int main() { int input = 0; do { srand((unsigned int)time(NULL)); menu(); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default: printf("输入错误,请重新输入!\n"); } } while (input); return 0; } //初始化棋盘 void init_board(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; } } } //布置雷 void set_mine(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 display_board(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("-------欢迎来到扫雷小游戏-------\n"); printf(" ");//让棋盘居中,更加美观 for (i = 0; i <= row; i++) { printf("%d ", i);//打印横坐标 } printf("\n"); for (i = 1; i <= row; i++) { printf(" %d ", i);//打印纵坐标 for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } } //判断该坐标附近的雷的个数 int get_sum(char mine[ROWS][COLS], int x, int y) { int sum = 0; for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { sum += (mine[i][j] - '0');//记录雷的个数 } }//判断该坐标附近的雷的个数 return sum; } //找雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int win = 0; while (row * col - EASY_COUNT > win) { printf("请输入你要查找的坐标:"); int x = 0; int y = 0; scanf("%d%d", &x, &y); if (x > 0 && x < row && y>0 && y < col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了!\n"); display_board(mine, ROW, COL); break; } else { int count = get_sum(mine, x, y); show[x][y] = count + '0'; display_board(show, ROW, COL); win++; } } else { printf("输入错误,请重新输入\n"); } } if (row * col - EASY_COUNT == win) { printf("恭喜你,成功了!\n"); } }
我们可以看到,这么长的一段代码(对我们这些初学者来说)是很难让人愿意去看的,不过这里写了比较详细的注释,如果我们认真去看还是能比较轻松看懂的啦~
3.注释的重要性
讲到这里,我就不得不说明一下注释的重要性了(真的很重要!)
1.帮助自己理解代码
当我们在学新知识,写一些我们陌生的代码的时候,恰当的注释可以让我们更好的理解自己写的代码(我们都是先模仿他人优质的代码开始学习的),当我们抛开他人的代码凭借自己的理解去复现代码的时候难免会卡壳(在这里适当的做注释,形成自己的理解)。当然,未来我们回顾这段代码的时候,也会更加注重自己的注释(自己薄弱的地方)
2.方便他人阅读自己的代码
方便他人阅读自己的代码不仅要有良好的代码习惯,也要有适当的注释(尤其是对于一些大型工程来说),在我们未来实际的工作中,往往是多人协同合作,当别人拿到你的代码(你自己框框写了一大堆,或许你可以看懂,但别人心里已经来了不知道多少句:你真是个小宝贝~)
4模块化程序设计的方法
把各个模块的代码分别放在各个新建的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include \"XXX.h\"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。
举个栗子~
teat.c
#include"file.h" int main() { ...... fun(); return 0; }
file.c
#include"file.h" void fun() { ...... }
file.h
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> void fun();
二.实现扫雷小游戏的思路
1.创建一个菜单
2.游戏的进入与退出
3.扫雷游戏的逻辑实现
(
1.布置棋盘
2.初始化棋盘
3.在棋盘中布雷
4.打印棋盘
5.找雷
6.在屏幕中显示排雷情况
)
三.代码实现
1.将代码分为3个模块(test.c、game.c、game.h)
1.test.c用来实现游戏的测试逻辑
2.game.c用来实现游戏的运行逻辑
3.game.h用来包含所有头文件和和函数的声明
2.菜单的创建
我们用一个简单的函数来实现在屏幕上打印一个简单的菜单~
在test.c文件中
3.游戏的进入与退出
4.游戏逻辑的实现
1.布置棋盘
在这里我们要如何布置棋盘呢?
1.我们知道简单的扫雷是一个9*9的宫格,因此我们是不是定义一个行列大小为9的二维数组就可以了呢?貌似可以,但我们仔细想一下就不难发现当我们要判断棋盘边缘元素周围是否有雷时我们要对其周围的8个元素进行判断(这样是不是会导致数组越界访问呢~)
因此我们要定义一个11*11的二维数组来解决这个问题
2.这里要注意我们要定义2个这样的数组
一个用来布置雷,另一个用来在屏幕上显示排雷的信息
这里我们将数组的行和列用#define宏来定义
好处如下:
1.方便程序的修改,不用对整个程序进行修改,只需要对宏定义上进行修改
2.提高程序的运行效率,更加方便模块化
3.在9*9扫雷打基础上,只需要改变宏定义的值就可以实现N*N扫雷的效果
2.初始化棋盘
我们在布置雷的棋盘中将元素初始化字符‘0’(这里我们为什么要定义字符数组,而不是整形数组呢?因为当我们要在显示棋盘中打印排雷信息时,我们用的元素都是字符。具体来说,就是我们在布置雷数组中找到该元素周围有多少雷时,我们要以字符的形式赋值给显示排雷信息的数组)
这里我们用字符0表示非雷,用字符1表示雷
在显示排雷信息的棋盘中将元素初始化字符‘*’
3在棋盘中布雷
我们定义一个函数来实现这个效果
这里涉及了随机数的生成(在我前面的文章中也讲到过)
还没有看的一定要去看哦~
4.打印棋盘
注意:这里我们只希望将显示排雷信息的棋盘打印出来(这里实现的每个功能都是以函数的形式)相信宝子们都能看懂代码的~(这里就不进行过多的解释了)
5.找雷
这里我们要插入一个判断雷个数的函数来辅助主函数的实现
这里插入一个小知识点字符1的ASCLL值是49,字符0的ASCLL值是48,‘1’-‘0’的结果就是整形1
EASY_COUNT是用来设置雷的个数的
//找雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int win = 0;//用来记录我们排雷的次数 while (row*col-EASY_COUNT>win) //当我们排雷的次数符合条件时就赢了,跳出循环 { printf("请输入你要查找的坐标:"); int x = 0; int y = 0; scanf("%d%d", &x, &y); if (x > 0 && x < row && y>0 && y < col) { if (show[x][y] == '*')//避免重复输入同一坐标 { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了!\n"); display_board(mine, ROW, COL); break; } else { int count = get_sum(mine, x, y); show[x][y] = count + '0';//将整形转化成字符型 display_board(show, ROW, COL); win++; } } else { printf("改坐标已经被排查,请重新输入\n"); } } else { printf("输入错误,请重新输入\n"); } } if (row * col - EASY_COUNT == win) { printf("恭喜你,成功了!\n"); } }
四.完整代码与模块
1.test.c
#include"game.h" void menu() { printf("欢迎来到 ^_^ 扫雷小游戏 \n"); printf(" 1.play \n"); printf(" 0.exit \n"); printf("请选择:"); } void game() { //定义俩个数组 char mine[ROWS][COLS];//布置雷 char show[ROWS][COLS];//显示排雷信息 //初始化俩个棋盘 init_board(mine, ROWS, COLS, '0'); init_board(show, ROWS, COLS, '*'); //布置雷 set_mine(mine, ROW, COL); //打印棋盘 //display_board(mine, ROW, COL); display_board(show, ROW, COL); //找雷 find_mine(mine, show, ROW, COL); } int main() { int input = 0; do { srand((unsigned int)time(NULL)); menu(); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏!\n"); break; default: printf("输入错误,请重新输入!\n"); } } while (input); return 0; }
2.game.c
#include"game.h" //初始化棋盘 void init_board(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; } } } //布置雷 void set_mine(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 display_board(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("-------欢迎来到扫雷小游戏-------\n"); printf(" ");//让棋盘居中,更加美观 for (i = 0; i <= row; i++) { printf("%d ",i);//打印横坐标 } printf("\n"); for (i = 1; i <=row; i++) { printf(" %d ", i);//打印纵坐标 for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } } //判断该坐标附近的雷的个数 int get_sum(char mine[ROWS][COLS],int x,int y) { int sum = 0; for (int i = x - 1; i <= x + 1; i++) { for (int j = y - 1; j <= y + 1; j++) { sum += (mine[i][j] - '0');//记录雷的个数 } }//判断该坐标附近的雷的个数 return sum; } //找雷 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int win = 0;//用来记录我们排雷的次数 while (row*col-EASY_COUNT>win) //当我们排雷的次数符合条件时就赢了,跳出循环 { printf("请输入你要查找的坐标:"); int x = 0; int y = 0; scanf("%d%d", &x, &y); if (x > 0 && x < row && y>0 && y < col) { if (show[x][y] == '*')//避免重复输入同一坐标 { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了!\n"); display_board(mine, ROW, COL); break; } else { int count = get_sum(mine, x, y); show[x][y] = count + '0';//将整形转化成字符型 display_board(show, ROW, COL); win++; } } else { printf("改坐标已经被排查,请重新输入\n"); } } else { printf("输入错误,请重新输入\n"); } } if (row * col - EASY_COUNT == win) { printf("恭喜你,成功了!\n"); } }
3.game.h
#define _CRT_SECURE_NO_WARNINGS #pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10 //初始化棋盘函数声明 void init_board(char board[ROWS][COLS], int rows, int cols, char set); //打印棋盘函数声明 void display_board(char board[ROWS][COLS], int row, int col); //布置雷函数声明 void set_mine(char board[ROWS][COLS], int row, int col); //找雷函数的声明 void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
五.完结散花
这只是简易版的扫雷小游戏,我们其实还可以扩展很多。篇幅有限,这次的分享到这里就结束了。后续一定会对扫雷小游戏进行扩展的,敬请期待!