文件组成
要写一个扫雷游戏,由于体量较大,我们打算分三个.c 文件(源文件) 来定义函数,实现代码,两个 .h 文件(头文件) 来实现我们.c文件中函数的声明和库函数头文件的引用。
三个 源文件 : test.c , game.c , print.c
两个头文件:game.h , print.h
文件的源码 我会放在结尾以供大家学习
test.c 游戏主题框架
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h" /这里引上我们编写好的头文件
#include "infor.h"/里面有我们所有自定函数的声明和库函数头文件的引用
void menu() {/游戏主界面的菜单
printf("***************************\n");
printf("******* 1. PLAY *******\n");
printf("******* 0. EXIT *******\n");
printf("******* 2. INFOR *******\n");
printf("***************************\n");
}
int main() {
srand((unsigned int)time(NULL));/由于游戏中布置雷需要随机数,我们在这里就将它初始化
int input;/用来接收玩家输入
do {/主题采用循环结构 当玩家结束游戏后可以进行选择进入或者退出
menu();/打印菜单
printf("请选择序号:>");/提示玩家选择
scanf("%d", &input);
switch (input) {
case 1:
game();/进入游戏模块
break;
case 0:
printf("退出游戏\n");/退出游戏
break;
case 2:
INFOR();/进入信息菜单 查看规则以及其他信息
break;
default:
printf("输入错误 重新输入\n");/输入错入提示
}
} while (input);/当玩家输入0 退出程序
return 0;
}
此时我们程序运行起来 就会是这个样子
infor.h 信息模块函数的声明
#pragma once
void INFOR();/信息模块入口
void menu_in();/信息模块的菜单
void game_rules();/游戏规则说明
void infor();/补充信息
infor.c 信息模块的函数定义与功能实现
#include/引上库函数的头文件
void menu_in() {/只是当玩家选择 1 后显示的菜单
printf("1.游戏规则\n");
printf("2.制作信息\n");
printf("3.奖励\n");
printf("0.返回\n");
}
void game_rules() {/游戏规则
printf("\n踩点和标记需输入对应 行和列如 第一行第一列 应该输入 \"1 1\"\n猜到雷会被炸死,恐怖至极,骨灰都找不到!!!\n踩到非雷会显示以它中心3X3矩形范围内雷的个数.\n如果显示0会自动帮你展开\n正确标记出所有雷,或者踩完所有非雷点即可获胜.\nthats it...\n\n");
}
void infor() {/其他信息这里可以看自己喜好添加
printf(".一步一步按提示来 不然要炸\n\n");
}
void reward() {/奖励
printf("完成1级难度即可获得:> ");
printf("游戏作者的大拇指!!!\n");
}
以上是这一模块函数的定义
——————————————————————————————————————————————————————
以下是则以模块的的框架
跟主界面类似 用到循环体 以实现重复选择
void INFOR() {/这一模块的框架
menu_in();/菜单
int input;
do {
printf("请选择序号:>");
scanf("%d", &input);
switch (input) {
case 1:
game_rules();/规则
break;
case 2:
infor();/信息
break;
case 3:
reward();/奖励
break;
case 0:
break;/退出循环,回到主界面
default:
printf("输入错误 重新输入\n");/输入错入提示
}
} while (input);/可以重复选择
}
结束后回到主界面
这时 如果你运行程序 并在主界面输入 2 并按下回车
将会是这样
如继续输入 0 就回到主界面
至此 扫雷游戏除了游戏 就都编写完成了(狗头)
game.h 游戏模块函数的声明
#pragma once
#include
#include
#include
#include
#include
/定义 棋盘行列的全局变量
为了方便 我们定义11X11的棋盘 但只使用 其中9X9的空间
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
/扫雷游戏主体
void game();
/初始化棋盘的函数
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 Board[ROWS][COLS],int row,int col,int nub);
/玩家排雷(一直玩到结束,返回值为输赢结果)
char FindMine(char Board[ROWS][COLS], char show[ROWS][COLS], int row, int col,int nub);
/返回点周围雷的个数
char CountMine(char Board[ROWS][COLS],char[ROWS][COLS] ,int row, int col);
/将点展开
void Extend(char Board[ROWS][COLS], char show[ROWS][COLS], int row, int col);
/判断赢没赢(赢返回 1 ,没赢返回 0 )
int Is_win(char Board[ROWS][COLS],char show[ROWS][COLS], int row, int col,int nub);
/游戏胜利 或 失败后延时打印结果
void SleepPrint(char* p);
game.c 游戏模块的函数定义与功能实现
为了方便 我们定义两个二维数组 来分别代表 用户看到的棋盘数字 ,标记 (排查雷的信息)的棋盘 和 存放雷的棋盘 里面 只有 雷。
用户棋盘show board:
存雷棋盘:
玩家可以选择是要踩 还是 要 标记,把所有非标记的雷周围的点都踩完 就赢了:
当玩家踩到非雷时 会显示 这个点 周围雷的个数:
当这个点周围没有雷时 不会显示数字0,而是自动帮你展开 到周围有雷的位置:
只有胜利 或者 被炸死从而结束游戏时 会 打印一次 存放雷的棋盘:
代码如下 :
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
/初始化棋盘的函数
void InitBoard(char Board[ROWS][COLS],int rows,int cols,char set) {
int i;
int j;
for (i = 0; i < rows; i++) {/将每个元素都设为0
for (j = 0; j < cols; j++) {
Board[i][j] = set;
}
}
}
/打印棋盘的函数
void DisplayBoard(char Board[ROWS][COLS], int row, int col) {
int i;
int j;
printf("————扫雷游戏————\n");
for (j = 0; j < 10; j++) {
if (0 == j) {
printf("%d ", j);
continue;
}
printf("%d ",j);
}
printf("\n\n");
for (i = 1; i <=row; i++) {
printf("%d ", i);
for (j = 1; j <= col; j++) {
printf("%c ", Board[i][j]);
}
printf("\n");
}
printf("————扫雷游戏————\n");
}
会打印如下棋盘
/设置雷的函数
void SetMine(char Board[ROWS][COLS], int row, int col,int nub) {
int i;
int j;
while (nub) { /nub为雷的个数
i = rand() % row + 1;/随机生成行
j = rand() % col + 1;/随机生成列
if (Board[i][j] == '*') {/查重
Board[i][j] = '#';/我们用 字符'#'来表示雷
nub--;/每设置一个雷 nub自减
}
}
}
/数雷的个数的函数
char CountMine(char Board[ROWS][COLS], char show[ROWS][COLS],int row, int col) {
char count = '0';
int i; int j;
for (i = row - 1; i < row + 2; i++) {
for (j = col - 1; j < col + 2; j++) {/排查点 周围 雷的个数
if (Board[i][j] == '#') {
count++;
}
}
}
return count;
}
/当3X3范围内没有雷时 自动展开的函数
这里用到了递归,若周围没有雷就把这个点设成 空白字符 并自动把周围的点都踩了,直到周围有雷为止
void Extend(char Board[ROWS][COLS],char show[ROWS][COLS],int row,int col){
show[row][col] = ' ';
int i, j;
for (i = row - 1; i < row + 2; i++) {
for (j = col - 1; j < col + 2; j++) {
if (show[i][j] == '*') {
show[i][j] = CountMine(Board, show, i, j);
if (show[i][j] == '0') {
Extend(Board, show, i, j);
}
}
}
}
}
/判断 游戏的函状态的函数
int Is_win(char Board[ROWS][COLS],char show[ROWS][COLS], int row, int col,int nub) {
int i,j;
int count = 0;/数正确的标记
int count0 = 0;/数标记的总个数
int COUNT = 0;/数*号后面是雷的个数
int COUNT0 = 0;/数*号的总个数
for (i = 1; i <=row; i++) {
for (j = 1; j <= col; j++) {
if (show[i][j] == '!') {
count0++;
if (Board[i][j] == '#') {
count++;
}
}if (show[i][j] == '*') {
COUNT0++;
if (Board[i][j] == '#') {
COUNT++;
}
}
}
}
if ((nub == count&&nub==count0)|| COUNT == COUNT0) {
/如果标记出了所有雷 或者 正确标记了所有没踩了的雷 就代表胜利
return 1;返回 1
}
else if (nub == count && nub <= count0) {
/如果有错误标记或者标记过多
return 2;返回 2
}
/一切正常 但没赢
return 0; 返回 0
}
/玩家 排雷或者标记的函数!
char FindMine(char Board[ROWS][COLS],char show[ROWS][COLS], int row,int col,int nub) {
int a, b;
int input=1;
while (1) {
printf("请选择是要踩(1)还是要标记(2):>");/标记(插旗)或者 踩点
scanf("%d", &input);
switch (input)/这里用switch语句来对玩家的输入 执行不同的语句
{
case 1:/ 若玩家输入 1 就是踩点 ,则执行一下语句
printf("请输入要走的行列:>");
while (1) { /执行踩
scanf("%d %d", &a, &b);
if (a > 0 && a < 10 && b>0 && b < 10) {
if (show[a][b] != '*' && show[a][b] != '!') {/查重
printf("这个点 (%d行%d列) 已经踩过了,重新输入:>", a, b);
continue;
}
break;
}
else
printf("输入错误,重新输入:>");/输入错误提示
}
if (Board[a][b] == '#') {/如果该点是雷,则失败,结束游戏
return 'F';
}
else {
show[a][b] = CountMine(Board, show, a, b);/如果不是雷,数周围雷的个数
if (show[a][b] == '0') {/如果周围没有雷,调用自动展开函数来自动展开
Extend(Board, show, a, b);
}
}
if (1 == Is_win(Board, show, row, col, nub)) {/每次走完 判断是否已经赢下游戏
DisplayBoard(show, row, col);/如果赢了就结束游戏
return 'W';
}
DisplayBoard(show, row, col);/打印棋盘
break;
case 2://标记
/更踩点思路相似
printf("请输入要标记的行列:>");
while (1) {
scanf("%d %d", &a, &b);/行 列
if (a > 0 && a < 10 && b>0 && b < 10) {/判断坐标的合法性
if (show[a][b] == '!') {/如果这是标记过的点 ,我们就把这个标记取消
show[a][b] = '*';
/判断是否赢得游戏
if (1 == Is_win(Board, show, row, col, nub)) {
DisplayBoard(show, row, col);
return 'W';
}
/如果标记多了就提示
if (2 == Is_win(Board, show, row, col, nub)) {
printf("标记多了...");
}
break;
}
else {/如果是没标记过的点 我们就把这个点 标记上
/随后在检测是否赢得游戏
show[a][b] = '!';
if (1==Is_win(Board, show, row, col,nub)) {
return 'W';
}
if (2 == Is_win(Board, show, row, col, nub)) {
printf("标记多了...");
}
break;
}
}
else {
printf("输入错误,重新输入:>");
}
}
DisplayBoard(show, row, col);
break;
/这里判断 最开始选择要踩还是标记那里 是否 输入错误
default:
printf("输入错误,重新输入:>");
printf("\n");
}
}
}
/这是一个 延时打印 胜利图案的函数 增加游戏的趣味性
void SleepPrint(char* p) {
int i;
int len = strlen(p);
for (i = 0; i < len; i += 2) {
printf("%c%c", *(p + i), *(p + i + 1));
Sleep(600);
}
}
这是游戏模块的主体框架
void game() {
char Board[ROWS][COLS] = { 0 };//布置好雷的信息
char show[ROWS][COLS] = { 0 };//排查出雷的信息
InitBoard(Board, ROWS, COLS, '*');
InitBoard(show, ROWS, COLS, '*');
printf("请选择游戏难度(1~3):>");
int tmpe;
int nub=0;
do {
scanf("%d", &tmpe);
switch (tmpe)
{
case 1:
nub = 10;
break;
case 2:
nub = 15;
break;
case 3:
nub = 20;
break;
default:
printf("输入错误,重新输入:>");
break;
}
} while (tmpe != 1 && tmpe != 3 && tmpe != 2);
SetMine(Board, ROW, COL, nub);
DisplayBoard(show, ROW, COL);
char Result=FindMine(Board, show, ROW, COL,nub);
switch (Result) {
case 'F':/如果输了 就延时打印 “你被炸死了”
SleepPrint("你被炸死了");
for (int i = 0; i < 3; i++) {
printf(".");
Sleep(600);
}
printf("\n");
DisplayBoard(Board, ROW, COL);
break;
case'W':/如果赢了就延时打印 “你赢了”
SleepPrint("你赢了");
for (int i = 0; i < 3; i++) {
printf("!");
Sleep(600);
}
printf("\n");
DisplayBoard(Board, ROW, COL);
}
}
/随后退出到 主界面 让玩家选择是退出程序 或是进入某个模块
这样 ,我们的扫雷游戏的部分就 完成了 .
结尾 源码
希望这篇博客 能对大家有所帮助,祝看到这篇的博客的人都早日成为 大牛!!!
游戏的 源文件 头文件:>
扫雷游戏https://gitee.com/leyi999/project.githttps://gitee.com/leyi999/project.git
.