对于游戏,首先显示在我们面前的是菜单选项,这里我们需要制作一个简单的显示出的菜单界面,还有菜单选项结果。
在源文件创建一个 test.c 文件,作为制作游戏的大框架的文件。
void menu()//这里是设计出显示在玩家视野的菜单栏
{
printf("**********************\n");
printf("****** 1.Play ******\n");
printf("****** 0.Exit ******\n");
printf("**********************\n");
}
int main()
{
int input = 0;//input的值会在循环中使用判断,因此放在循环外
do//我们会进行多次游戏,所以游戏应该放在循环体中,do while语句
{
menu();//这里是显示出的菜单页面
printf("Please select:>\n");
scanf("%d", &input);
switch (input)//switch语句作为游戏初始的开关
{
case 1://input的值为1时就进入游戏
game();//这里是扫雷游戏的主体,在这个函数中设计扫雷游戏
//printf("game start");//这里打印出游戏开始代替游戏本身进行测试该段代码未出错
break;
case 0://input的值为0,则退出游戏
printf("Exit\n");
break;
default://输入除1,0的值,则提示输入错误,循环继续,回到选择输入的代码
printf("Error,please select\n");
break;
}
printf("\n");
} while (input);//循环语句时根据input即我们输入值判断是否进入循环的。
//非0为真,进入循环
return 0;
}
在学习函数时,我们了解到多个文件的使用,为了让代码看起来整齐清晰,我们将接下来写的代码分为三个部分。
首先是main函数及设计游戏的自定义函数在test.c文件中;
其次是库函数和自定义函数的声明在game.h文件中;
最后是自定义函数的定义在game.c文件中。
而在我们创建好这些文件后,会发现在测试上列菜单代码时,程序会报警告,这时我们应在.c 文件中引用我们创建的头文件 game.h ,建立它们之间的联系。
#include "game.h"//在引用自定义头文件时,使用引号
扫雷游戏分为基础,中级,专家,自定义等类型。
难度由它的总格子和雷的数量决定。
以基础的举例,格子是9*9的,左上角是雷数,右上角是秒数(从0计时),中间的笑脸会在触雷和获胜时候变脸。
从上图中我们可以看到显示在玩家面前的界面时逐渐变化的,因此我们这里制作两个界面。
一个是真实界面mine(玩家在游戏结束前不可见),一个是玩家选择界面show(玩家操作界面)。
那么我们就需要使用二维数组来创造需要的游戏界面,在使用数组时,我们需要考虑到数组的访问越界是否会影响程序。
因为在边界处的格子,我们在判断它周边存在的雷时,是以它为中心扫描他所在九宫格的其他格子,在这个扫描中就存在数组的访问越界。因此我们还需要一个比肉眼可见的界面更大的格子。
在我们上面的代码中 game() 是设计游戏主体的函数。接下来结合(一)的分析来设计游戏的界面。
我们假设界面的行为ROW,列为COL。
基础模式中就是设计一个九乘九和十一乘十一的数组。
考虑到后期更有利于改变格子数目,我们可以在game.h中
//game.h
//这里放置的是函数的声明,库函数和自定义函数的
//须注意
#pragma once
#include
#define ROW 9
#define COL 9//显示在玩家面前的界面格子数
#define ROWS ROW+2//为了防止数组越界访问而设计的隐藏界面
#define COLS COL+2
#define EasyCount 10//基础模式下的雷数
//test.c
//这里是game()函数的设计,也是游戏的主体
void game()
{
//创建需要的数组
//扫雷游戏的实现
//mine数组是用来存放布置好的雷的信息,玩家看不到的信息
char mine[ROWS][COLS] = { 0 }; //'0'放置的是字符
//show数组是用来存放排查出的雷的信息,玩家游玩的界面
char show[ROWS][COLS] = { 0 }; //'*'放置的是字符
//函数初始化
InitBoard(mine,ROW,COL,'0');//放置‘0’
InitBoard(show,ROW,COL,'*');//放置‘*’
//放置雷
SetMine(mine,ROW,COL);
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
DisPlayBoard(show,ROW,COL);
//探查雷
FindMine(mine,show,ROW,COL);
}
我们在上列的game()函数中设计好了游戏的主体,现在开始完善函数内部的自定义函数。
在使用我们的自定义函数时,需要注意在头文件game.h中,我们要进行声明:
//game.h
//这里放置的是函数的声明,库函数和自定义函数的
//须注意
#pragma once
#include
#define ROW 9
#define COL 9//显示在玩家面前的界面格子数
#define ROWS ROW+2//为了防止数组越界访问而设计的隐藏界面
#define COLS COL+2
#define EasyCount 10//基础模式下的雷数
//下列是自定义函数的声明
//函数初始化,为了现目,将对应的自定义函数写上作为对比
//InitBoard(mine,ROW,COL,'0');//放置‘0’
//InitBoard(show,ROW,COL,'*');//放置‘*’
void InitBoard(char arr[ROWS][COLS],int row,int col,char set);
//因为数组mine和show都需要进行初始化,且均为char类型,因此都用arr数组来定义
//‘0’,‘*’,是字符,均为初始化,因此使用char set 来概括
//放置雷,是在玩家玩游戏时看不到的界面
//SetMine(mine,ROW,COL);
void SetMine(char mine[ROWS][COLS],int row, int col);
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
//DisPlayBoard(show,ROW,COL);
//我们上列打印的设计也是含有mine和show数组,写作为一个函数即可
void DisPlayBoard(char arr[ROWS][COLS],int row,int col);
//探查雷
//FindMine(mine,show,ROW,COL);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col);
接下来是根据我们game()中的设计来自定义函数的功能
自定义函数的定义放置在game.c的文件中.
//下列是自定义函数的定义
//函数初始化,为了现目,将对应的自定义函数写上作为对比
void InitBoard(char arr[ROWS][COLS],int rows,int cols,char set)
{
int i = 0;
for(i=0; i<=rows; i++)
{
int j = 0;
for(j=0; j<=cols; j++)
{
arr[i][j] = set;//set就代表'0'.'*'
}
}
}
注意rand函数的使用要包括的头文件放置在game.h中,这时我们主要补充完善头文件
#include
#include
//game.c
//放置雷,是在玩家玩游戏时看不到的界面
//SetMine(mine,ROW,COL);
//放置雷就是随机放置,这时就需要我们的rand函数,
//使用rand函数,需要通过srand函数的参数seed来设置rand函数生成随机数的时候的种子
void SetMine(char mine[ROWS][COLS],int row, int col)
{
//雷放置在mine数组中,数量为EasyCount个,
int count = EasyCount;
while(count)//当count为0时为假,循环停止
{
int x = rand()%row + 1;
//注意:取模row,的值为0~row-1,我们设置的row是9,所以是0~8,
//加上一就是1~9,正好对应数组的下标
int y = rand()%col + 1;
if(mine[x][y] == '0') //注意我们的随机值可能出现重复的情况,
{//如果不对数组中的数进行判断可能出现重复设置的情况,导致我们设置的10个雷不准确
mine[x][y] = '1';//数组时char类型的,不要手误,打成了1
count--;//这是判断10个雷的条件,以及循环结束的条件
}
}
}
完善test.c文件,使用rand函数,需要通过srand函数的参数seed来设置rand函数生成随机数的时候的种子
//test.c文件
//srand((unsigned int)time(NULL));
//放在do while循环前
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//注意头文件
do
{
menu();
printf("Please select:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();//游戏入口
//printf("game start");
break;
case 0:
printf("Exit\n");
break;
default:
printf("Error,please select\n");
break;
}
printf("\n");
} while (input);
return 0;
}
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
//DisPlayBoard(show,ROW,COL);
//我们上列打印的设计也是含有mine和show数组,写作为一个函数即可
void DisPlayBoard(char arr[ROWS][COLS],int row,int col)
{
int i = 0;
int j = 0;
printf("-----Game Interface-----\n");
//为了玩家方便找到对应的坐标,将行列标上数字
//列 从0开始是为了对齐
for(i=0; i
玩家输入坐标,判断该坐标是否为雷,如果是雷,则游戏结束,玩家失败;如果不是雷,需要判断这个坐标周围有多少雷,为玩家提供提示。
选择的坐标是九宫格的中心,探查的范围是以这个坐标为中心的九宫格范围,所以处于边界的坐标在探查中会出现访问越界状况,但是我们创建的数组arr[ROWS][COLS]可以防止访问越界。
玩家输入坐标,我们需要进行下列判断:
//使用static修饰,只能在game.c 文件中使用
//放置在需要使用的函数前,即完成了函数的声明和定义,无须在game.h中声明
static int JudgeSteps(char mine[ROWS][COLS],int x,int y)
{
int i = 0;
int j = 0;
int count = 0;
for(i=x-1; i<=x+1; i++)
{
for(j=y-1; j<=y+1; j++)
{
count += (mine[i][j] - '0');
}
}
return count;
}
//探查雷
//FindMine(mine,show,ROW,COL);
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
{
int x = 0;
int y = 0;
int steps = 0;
while(steps < row*col-EasyCount)
{
printf("Please select:>");
scanf("%d %d",&x,&y);
if(x>=1 && x<=row && y>=1 && y<=col)
{
if(show[x][y] == '*')
{
if(mine[x][y] == '1')
{
printf("Unfortunately, you lost\n");
DisPlayBoard(mine, ROW, COL);//打印真实游戏界面
break;//游戏结束,跳出循环
}
else//不是雷,查它的周边有多少雷
{
int count = JudgeSteps(mine,x,y);
show[x][y] = count + '0';
DisPlayBoard(show, ROW, COL);
steps++;//避开了一个雷,++才能达到跳出循环的条件
}
}
else
{
printf("Repeated input.Please reselect\n");
}
}
else
{
printf("Error.Please reselect\n");
}
}
if(steps == row * col - EasyCount)
{
printf("Congratulations.YOU WIN.\n");
DisPlayBoard(mine, ROW, COL);
}
}
//game.h
//这里放置的是函数的声明,库函数和自定义函数的
//须注意
#pragma once
#include
#include //rand函数的使用声明
#include //time函数的使用声明
#define ROW 9
#define COL 9//显示在玩家面前的界面格子数
#define ROWS ROW+2//为了防止数组越界访问而设计的隐藏界面
#define COLS COL+2
#define EasyCount 10//基础模式下的雷数
//下列是自定义函数的声明
//函数初始化,为了现目,将对应的自定义函数写上作为对比
//InitBoard(mine,ROW,COL,'0');//放置‘0’
//InitBoard(show,ROW,COL,'*');//放置‘*’
void InitBoard(char arr[ROWS][COLS],int row,int col,char set);
//因为数组mine和show都需要进行初始化,且均为char类型,因此都用arr数组来定义
//‘0’,‘*’,是字符,均为初始化,因此使用char set 来概括
//放置雷,是在玩家玩游戏时看不到的界面
//SetMine(mine,ROW,COL);
void SetMine(char mine[ROWS][COLS],int row, int col);
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
//DisPlayBoard(show,ROW,COL);
//我们上列打印的设计也是含有mine和show数组,写作为一个函数即可
void DisPlayBoard(char arr[ROWS][COLS],int row,int col);
//探查雷
//FindMine(mine,show,ROW,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("****** 1.Play ******\n");
printf("****** 0.Exit ******\n");
printf("**********************\n");
}
void game()
{
//创建需要的数组
//扫雷游戏的实现
//mine数组是用来存放布置好的雷的信息,玩家看不到的信息
char mine[ROWS][COLS] = { 0 }; //'0'放置的是字符
//show数组是用来存放排查出的雷的信息,玩家游玩的界面
char show[ROWS][COLS] = { 0 }; //'*'放置的是字符
//函数初始化
InitBoard(mine,ROW,COL,'0');//放置‘0’
InitBoard(show,ROW,COL,'*');//放置‘*’
//放置雷
SetMine(mine,ROW,COL);
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
DisPlayBoard(show,ROW,COL);
//探查雷
FindMine(mine,show,ROW,COL);
}
int main()
{
int input = 0;//input的值会在循环中使用判断,因此放在循环外
do//我们会进行多次游戏,所以游戏应该放在循环体中,do while语句
{
menu();//这里是显示出的菜单页面
printf("Please select:>\n");
scanf("%d", &input);
switch (input)//switch语句作为游戏初始的开关
{
case 1://input的值为1时就进入游戏
game();//这里是扫雷游戏的主体,在这个函数中设计扫雷游戏
//printf("game start");//这里打印出游戏开始代替游戏本身进行测试该段代码未出错
break;
case 0://input的值为0,则退出游戏
printf("Exit\n");
break;
default://输入除1,0的值,则提示输入错误,循环继续,回到选择输入的代码
printf("Error,please select\n");
break;
}
printf("\n");
} while (input);//循环语句时根据input即我们输入值判断是否进入循环的。
//非0为真,进入循环
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//下列是自定义函数的定义
//函数初始化,为了现目,将对应的自定义函数写上作为对比
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i <= rows; i++)
{
int j = 0;
for (j = 0; j <= cols; j++)
{
arr[i][j] = set;//set就代表'0'.'*'
}
}
}
//放置雷,是在玩家玩游戏时看不到的界面
//SetMine(mine,ROW,COL);
//放置雷就是随机放置,这时就需要我们的rand函数,
//使用rand函数,需要通过srand函数的参数seed来设置rand函数生成随机数的时候的种子
void SetMine(char mine[ROWS][COLS], int row, int col)
{
//雷放置在mine数组中,数量为EasyCount个,
int count = EasyCount;
while (count)//当count为0时为假,循环停止
{
int x = rand() % row + 1;
//注意:取模row,的值为0~row-1,我们设置的row是9,所以是0~8,
//加上一就是1~9,正好对应数组的下标
int y = rand() % col + 1;
if (mine[x][y] == '0')//注意我们的随机值可能出现重复的情况,
{//如果不对数组中的数进行判断可能出现重复设置的情况,导致我们设置的10个雷不准确
mine[x][y] = '1';//数组时char类型的,不要手误,打成了1
count--;//这是判断10个雷的条件,以及循环结束的条件
}
}
}
//打印界面
//DisPlayBoard(mine,ROW,COL);//这里可以作为测试放置雷是否正确,
//DisPlayBoard(show,ROW,COL);
//我们上列打印的设计也是含有mine和show数组,写作为一个函数即可
void DisPlayBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-----Game Interface-----\n");
//为了玩家方便找到对应的坐标,将行列标上数字
//列 从0开始是为了对齐
for (i = 0; i < col; i++)
{
printf("%d ", i);//i = 0,1,2,3,4,…
}
printf("\n");
for (i = 1; i < row; i++)//注意,这个函数是打印函数,是会出现在玩家视野中的游戏界面
{ //而出现在玩家界面的格子是row*col的
{
printf("%d ", i);//行 搭配后一个for循环后的换行代码,变为每行的标注
for (j = 1; j < col; j++)
{
printf("%c ", arr[i][j]);//数组时char类型的,打印出row*col的格子
}
printf("\n");//换行
}
}
}
//使用static修饰,只能在game.c 文件中使用
//放置在需要使用的函数前,即完成了函数的声明和定义,无须在game.h中声明
static int JudgeSteps(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
count += (mine[i][j] - '0');
}
}
return count;
}
//探查雷
//FindMine(mine,show,ROW,COL);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int steps = 0;
while (steps < row * col - EasyCount)
{
printf("Please select:>");
scanf("%d %d", &x, &y);
//判断坐标的有效性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("Unfortunately, you lost\n");
DisPlayBoard(mine, ROW, COL);//打印真实游戏界面
break;//游戏结束,跳出循环
}
else//不是雷,查它的周边有多少雷
{
int count = JudgeSteps(mine, x, y);
show[x][y] = count + '0';
DisPlayBoard(show, ROW, COL);
steps++;//避开了一个雷,++才能达到跳出循环的条件
}
}
else
{
printf("Repeated input.Please reselect\n");
}
}
else
{
printf("Error.Please reselect\n");
}
}
if (steps == row * col - EasyCount)
{
printf("Congratulations.YOU WIN.\n");
DisPlayBoard(mine, ROW, COL);
}
}
我们可以将雷的值设置大一些,接近80个来检查代码的可用性。
将mine数组的界面打印出来,以便我们避开雷。否则检查太过复杂。