许久未见,上次写的博文已经是两天前的,这两天正值端午放假,出去玩了两天,虽然人在外面也在学习,但用笔记本敲代码实在是力不从心,所以想想就也暂停一下博客。
当然玩了两天之后,今天过来也算是一番苦战,将作业题扫雷小游戏算是写出来了,讲师一共给出了可选的拓展增加难度的选项,不过这俩选项对于原版的扫雷来说可太寻常了。一个是空白部分自己爆开,另一个是给雷添加标记,第二个其实我也能写,但是已经想休息了,所以就放弃了,但是第一个拓展功能我是写出来了。接下来附上我的源代码,跟之前的三子棋一样,也是多文件组合的项目,所以这次给出的代码也是拆分的。(感觉也挺倒霉的,就是回到家网络因为运营商那边出问题,所以网络停了,现在是通过自己手机开热点,给自己笔记本开了热点,把这点东西写出来,所以很多东西弄的比之前还差,请见谅)
(一)主程序
/* 扫雷 */
//主程序实现的功能
//1、点开程序弹出菜单图,用户选择1游玩扫雷游戏,选择2退出程序。
//2、用户玩好一把扫雷之后,再次弹出菜单图,用户选择1游玩扫雷游戏,选择2退出程序 、
#include"saolei.h"
int main(void)
{
char input;
do
{
Print_menu();
while(scanf("%c%*c",&input)!=1)getchar();
switch(input)
{
case '1':Game();break;
case '2':input=0;break;
default:printf("您输入的是非法值,请依照菜单重新输入:\n");break;
}
}while(input);
return 0;
}
(二)头文件
#ifndef SAOLEI_H_
#define SAOLEI_H_
//头文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//宏
#define NEED_ROW 10//需要显示的扫雷棋盘行数
#define NEED_COLUMN 10//需要显示的扫雷棋盘列数
#define REAL_ROW 12 //实际建立的扫雷棋盘行数,该值为显示的棋盘行数+2
#define REAL_COLUMN 12//实际建立的扫雷棋盘列数,该值为显示的棋盘列数+2
#define VALUE_LANDMINE 10//需要生成的地雷数量
#endif
//定义了一个枚举类型,用于表示游戏的胜利条件
enum
{
CONTINUE=1,FAILURE,WIN
};
//自定义函数1:打印游戏菜单
void Print_menu(void);
//自定义函数2:扫雷游戏主程序模块
void Game(void);
//自定义函数2.1:创建一个row行column列的矩阵棋盘
char** Creative_chequer(int ,int );
//自定义函数2.2:用于初始化背面棋盘
void Initialize_back_chequer(char** ,int ,int );
//自定义函数2.3:用于初始化正面棋盘
void Initialize_show_chequer(char** ,int ,int );
//自定义函数2.4:在需要展示的棋盘范围内,在背面棋盘随机生成规定数量的地雷
void Generator_landmine(char** ,int ,int ,int );
//自定义函数2.5:用于在已生成雷的背面棋盘,依照雷的布置,对应生成数字
void Generator_number(char** ,int ,int );
//自定义函数2.5.1:用于计算对应元素周围一圈的地雷数量
int Counter_number(char** ,int ,int );
//自定义函数2.6:用于打印扫雷的棋盘
void Print_chequer(char** ,int ,int );
//自定义函数2.7:当选中的是个空白区域时,将空白区域周边8个格子都打开,同时若打开有空白格子,则继续以那个空白格子为中心打开其他格子
void Explode_chequer(char** ,char** ,int ,int ,int ,int );
//自定义函数2.8:用于判断是否胜利
int Analyze_chequer(char** ,char** ,int ,int ,int );//若返回值为1,说明没输没赢,游戏继续,若返回值为2,说明挖到了地雷,玩家输了;若返回值为3,说明已经排雷排干净了,地图上只剩下雷了
//自定义函数2.9:用于释放申请的棋盘动态内存
void Release_chequer(char** ,int );
(三)扫雷自定义函数主模块
//游戏主程序功能
//1、生成两个棋盘,一个是展示给玩家的棋盘,一个是背面实际藏好雷的棋盘,考虑到后续的方便,两个棋盘都比实际大一圈。
//2、初始化正反两个棋盘,正面棋盘全部用#号表示覆盖,还未挖开;背面棋盘用空格初始化所有格子。
//3、背面棋盘先随机生成地雷,然后再根据随机生成的地雷在地雷周边的空格产生数字。
//4、接下来打印一个棋盘,棋盘应该有正交十字坐标,方便用户选出自己要挖的位置
//5、用户输入要挖的位置后,分三种情况先进行判断,若为雷,则将雷赋值到正面棋盘对应位置,然后宣布玩家输了,游戏结束;
// 若为数字,则将数组赋值到正面棋盘,展示给玩家,让玩家继续;若为空,则应进行空爆反应,将相连的所有空格区域赋值
// 给正面棋盘,展示给玩家后,让玩家继续。
//
#include"saolei.h"
//自定义函数1:打印游戏菜单
void Print_menu(void)
{
printf("\n*************************************\n");
printf("*-----------------------------------*\n");
printf("*------------GAME MENU--------------*\n");
printf("*-----------------------------------*\n");
printf("*-------------1. PLAY---------------*\n");
printf("*-------------2. EXIT---------------*\n");
printf("*-----------------------------------*\n");
printf("*************************************\n");
}
//自定义函数2:扫雷游戏主程序模块
void Game(void)
{
char **back_chequer=NULL;
char **show_chequer=NULL;
int irow,icolumn;
int r=1;
srand(time(NULL));
back_chequer=Creative_chequer(REAL_ROW,REAL_COLUMN);
show_chequer=Creative_chequer(REAL_ROW,REAL_COLUMN);
Initialize_back_chequer(back_chequer,REAL_ROW,REAL_COLUMN);
Initialize_show_chequer(show_chequer,REAL_ROW,REAL_COLUMN);
Generator_landmine(back_chequer,NEED_ROW,NEED_COLUMN,VALUE_LANDMINE);
Generator_number(back_chequer,NEED_ROW,NEED_COLUMN);
system("cls");
Print_chequer(show_chequer,NEED_ROW,NEED_COLUMN);
do
{
printf("请输入您想要挖的位置:\n");
while(scanf("%d%d%*c",&irow,&icolumn)!=2)getchar();
if((irow>0&&irow<=NEED_ROW)&&(icolumn>0&&icolumn<=NEED_COLUMN)&&show_chequer[irow][icolumn]=='#')
{
if(back_chequer[irow][icolumn]=='*')
{
show_chequer[irow][icolumn]=back_chequer[irow][icolumn];
}
else if(back_chequer[irow][icolumn]>47&&back_chequer[irow][icolumn]<58)
{
show_chequer[irow][icolumn]=back_chequer[irow][icolumn];
}
else if(back_chequer[irow][icolumn]=' ')
{
Explode_chequer(back_chequer,show_chequer,NEED_ROW,NEED_COLUMN,irow,icolumn);
}
system("cls");
Print_chequer(show_chequer,NEED_ROW,NEED_COLUMN);
r=Analyze_chequer(back_chequer,show_chequer,NEED_ROW,NEED_COLUMN,VALUE_LANDMINE);//若返回值为1,说明没输没赢,游戏继续,若返回值为2,说明挖到了地雷,玩家输了;若返回值为3,说明已经排雷排干净了,地图上只剩下雷了
switch(r)
{
case CONTINUE:break;
case FAILURE:r=0;printf("很遗憾,您挖到了地雷,你输了。\n");break;
case WIN:r=0;printf("恭喜您,你排除了所有地雷!!!\n");break;
}
}
else
{
printf("您输入的位置越界或者已展开,请重新输入想要挖的位置:\n");
}
}while(r);
Release_chequer(back_chequer,REAL_ROW);
Release_chequer(show_chequer,REAL_ROW);
}
//自定义函数2.1:创建一个row行column列的矩阵棋盘
char** Creative_chequer(int row,int column)
{
char **p=NULL;
int i;
p=(char**)malloc(row*sizeof(char*));
for(i=0;i<row;i++)
{
p[i]=(char*)malloc(column*sizeof(char));
}
return p;
}
//自定义函数2.2:用于初始化背面棋盘
void Initialize_back_chequer(char **back_chequer,int real_row,int real_column)
{
int i1,i2;
for(i1=0;i1<real_row;i1++)
{
for(i2=0;i2<real_column;i2++)
back_chequer[i1][i2]=' ';
}
}
//自定义函数2.3:用于初始化正面棋盘
void Initialize_show_chequer(char **show_chequer,int real_row,int real_column)
{
int i1,i2;
for(i1=0;i1<real_row;i1++)
{
for(i2=0;i2<real_column;i2++)
show_chequer[i1][i2]='#';
}
}
//自定义函数2.4:用于在背面棋盘随机生成规定数量的地雷
void Generator_landmine(char **back_chequer,int need_row,int need_column,int value_landmine)
{
int i;
int irow,icolumn;
for(i=0;i<value_landmine;i++)
{
do
{
irow=(rand()%need_row)+1;
icolumn=(rand()%need_column)+1;
}while(back_chequer[irow][icolumn]!=' ');
back_chequer[irow][icolumn]='*';
}
}
//自定义函数2.5:用于在已生成雷的背面棋盘,依照雷的布置,对应生成数字
void Generator_number(char **back_chequer,int need_row,int need_column)
{
int i1,i2;
int counter=0;
for(i1=1;i1<=need_row;i1++)
{
for(i2=1;i2<=need_column;i2++)
{
if(back_chequer[i1][i2]==' ')
{
counter=Counter_number(back_chequer,i1,i2);
if(counter!=0)
back_chequer[i1][i2]='0'+counter;
counter=0;
}
}
}
}
//自定义函数2.5.1:用于计算对应元素周围一圈的地雷数量
int Counter_number(char **back_chequer,int i1,int i2)
{
int counter=0;
if(back_chequer[i1-1][i2-1]=='*')
counter++;
if(back_chequer[i1-1][i2]=='*')
counter++;
if(back_chequer[i1-1][i2+1]=='*')
counter++;
if(back_chequer[i1][i2-1]=='*')
counter++;
if(back_chequer[i1][i2+1]=='*')
counter++;
if(back_chequer[i1+1][i2-1]=='*')
counter++;
if(back_chequer[i1+1][i2]=='*')
counter++;
if(back_chequer[i1+1][i2+1]=='*')
counter++;
return counter;
}
//自定义函数2.6:用于打印扫雷的棋盘
void Print_chequer(char **show_chequer,int need_row,int need_column)
{
int i1,i2;
int c_number=1,r_number=1;
printf(" CHEQUER\n\n");
for(i1=1;i1<=need_row;i1++)
{
if(i1==1)
{
printf(" ");
for(i2=1;i2<=need_column;i2++)
{
printf(" %d ",c_number);
c_number++;
if(i2==need_column)printf(" \n");
}
}
for(i2=1;i2<=need_column;i2++)
{
if(i2==1)
{
printf(" ");
}
printf("----");
if(i2==need_column)
printf("-\n");
}
for(i2=1;i2<=need_column;i2++)
{
if(i2==1)
{
if(r_number!=10)
printf(" %d ",r_number);
else
printf("%d ",r_number);
r_number++;
}
printf("| %c ",show_chequer[i1][i2]);
if(i2==need_column)
printf("|\n");
}
if(i1==need_row)
{
if(i2==1)
{
printf(" ");
}
for(i2=1;i2<=need_column;i2++)
{
if(i2==1)printf(" ");
printf("----");
if(i2==need_column)
printf("-\n");
}
}
}
}
//自定义函数2.7:当选中的是个空白区域时,将空白区域周边8个格子都打开,同时若打开有空白格子,则继续以那个空白格子为中心打开其他格子
void Explode_chequer(char **back_chequer,char **show_chequer,int need_row,int need_column,int irow,int icolumn)
{
show_chequer[irow][icolumn]=back_chequer[irow][icolumn];
if((irow-1>0&&irow-1<=need_row)&&(icolumn-1>0&&icolumn-1<=need_column))
{
if(back_chequer[irow-1][icolumn-1]>47&&back_chequer[irow-1][icolumn-1]<58&&show_chequer[irow-1][icolumn-1]=='#')
show_chequer[irow-1][icolumn-1]=back_chequer[irow-1][icolumn-1];
else if(back_chequer[irow-1][icolumn-1]==' '&&show_chequer[irow-1][icolumn-1]!=back_chequer[irow-1][icolumn-1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow-1,icolumn-1);
}
if((irow-1>0&&irow-1<=need_row)&&(icolumn>0&&icolumn<=need_column))
{
if(back_chequer[irow-1][icolumn]>47&&back_chequer[irow-1][icolumn]<58&&show_chequer[irow-1][icolumn]=='#')
show_chequer[irow-1][icolumn]=back_chequer[irow-1][icolumn];
else if(back_chequer[irow-1][icolumn]==' '&&show_chequer[irow-1][icolumn]!=back_chequer[irow-1][icolumn])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow-1,icolumn);
}
if((irow-1>0&&irow-1<=need_row)&&(icolumn+1>0&&icolumn+1<=need_column))
{
if(back_chequer[irow-1][icolumn+1]>47&&back_chequer[irow-1][icolumn+1]<58&&show_chequer[irow-1][icolumn+1]=='#')
show_chequer[irow-1][icolumn+1]=back_chequer[irow-1][icolumn+1];
else if(back_chequer[irow-1][icolumn+1]==' '&&show_chequer[irow-1][icolumn+1]!=back_chequer[irow-1][icolumn+1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow-1,icolumn+1);
}
if((irow>0&&irow<=need_row)&&(icolumn-1>0&&icolumn-1<=need_column))
{
if(back_chequer[irow][icolumn-1]>47&&back_chequer[irow][icolumn-1]<58&&show_chequer[irow][icolumn-1]=='#')
show_chequer[irow][icolumn-1]=back_chequer[irow][icolumn-1];
else if(back_chequer[irow][icolumn-1]==' '&&show_chequer[irow][icolumn-1]!=back_chequer[irow][icolumn-1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow,icolumn-1);
}
if((irow>0&&irow<=need_row)&&(icolumn+1>0&&icolumn+1<=need_column))
{
if(back_chequer[irow][icolumn+1]>47&&back_chequer[irow][icolumn+1]<58&&show_chequer[irow][icolumn+1]=='#')
show_chequer[irow][icolumn+1]=back_chequer[irow][icolumn+1];
else if(back_chequer[irow][icolumn+1]==' '&&show_chequer[irow][icolumn+1]!=back_chequer[irow][icolumn+1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow,icolumn+1);
}
if((irow+1>0&&irow+1<=need_row)&&(icolumn-1>0&&icolumn-1<=need_column))
{
if(back_chequer[irow+1][icolumn-1]>47&&back_chequer[irow+1][icolumn-1]<58&&show_chequer[irow+1][icolumn-1]=='#')
show_chequer[irow+1][icolumn-1]=back_chequer[irow+1][icolumn-1];
else if(back_chequer[irow+1][icolumn-1]==' '&&show_chequer[irow+1][icolumn-1]!=back_chequer[irow+1][icolumn-1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow+1,icolumn-1);
}
if((irow+1>0&&irow+1<=need_row)&&(icolumn>0&&icolumn<=need_column))
{
if(back_chequer[irow+1][icolumn]>47&&back_chequer[irow+1][icolumn]<58&&show_chequer[irow+1][icolumn]=='#')
show_chequer[irow+1][icolumn]=back_chequer[irow+1][icolumn];
else if(back_chequer[irow+1][icolumn]==' '&&show_chequer[irow+1][icolumn]!=back_chequer[irow+1][icolumn])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow+1,icolumn);
}
if((irow+1>0&&irow+1<=need_row)&&(icolumn+1>0&&icolumn+1<=need_column))
{
if(back_chequer[irow+1][icolumn+1]>47&&back_chequer[irow+1][icolumn+1]<58&&show_chequer[irow+1][icolumn+1]=='#')
show_chequer[irow+1][icolumn+1]=back_chequer[irow+1][icolumn+1];
else if(back_chequer[irow+1][icolumn+1]==' '&&show_chequer[irow+1][icolumn+1]!=back_chequer[irow+1][icolumn+1])
Explode_chequer(back_chequer,show_chequer,need_row,need_column,irow+1,icolumn+1);
}
}
//自定义函数2.8:用于判断是否胜利
int Analyze_chequer(char **back_chequer,char **show_chequer,int need_row,int need_column,int value_landmine)//若返回值为1,说明没输没赢,游戏继续,若返回值为2,说明挖到了地雷,玩家输了;若返回值为3,说明已经排雷排干净了,地图上只剩下雷了
{
int r=-1;
int i1,i2;
int counter=0;
for(i1=1;i1<=need_row;i1++)
{
for(i2=1;i2<=need_column;i2++)
{
if(show_chequer[i1][i2]=='*')
{
r=2;
goto out;
}
else if(show_chequer[i1][i2]=='#')
counter++;
}
}
out:
if(counter==value_landmine)
r=3;
else if(r!=2)
r=1;
return r;
}
//自定义函数2.9:用于释放申请的棋盘动态内存
void Release_chequer(char **chequer,int row)
{
int i;
for(i=0;i<row;i++)
free(chequer[i]);
free(chequer);
}