一个简单的扫雷小游戏,可以用键盘控制光标来对格子进行操作。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#include<conio.h>
#define KONG 0 //方块被消除后不显示数字的格子
#define KONG_WITH_NUMBER 3 //方块被消除后显示数字的格子
#define WALL 2 //围住方块的四周格子,不可消除
#define BOX 1 //待操作的方块
#define LEFT 75
#define RIGHT 77
#define UP 72
#define DOWN 80
#define ESC 27
struct Face
{
int data[18][18];
}face;
struct Mode
{
int FACE_Y;
int FACE_X;
int BOMBNUMBER; //炸弹数量
}mode;
struct BOOM
{
int data[18][18];//为1时该位置是炸弹,为0时不是炸弹
}boom;
struct MARK
{
int data[18][18];//为1时该位置标记为炸弹,为0时没有标记
int number;//记录标记的数量
}mark;
struct DISPLAY
{
int data[18][18];//为1时该位置显示数字,为0时不显示
}display;
int menu();
void Start_Game();
void Inter_Face();
void Cursor(int c); //改光标可见状态
void gotoxy(int x,int y); //坐标跳转
void display_number(int x,int y);
void markbox(int x,int y);
int bomb_number(int x,int y);
void setbomb(int x,int y,int z);
void win();
void gameover();
void setzero();
int main()
{
int gamemode;
system("title 扫雷"); //设置cmd窗口标题
system("mode con lines=16 cols=35"); //设置cmd窗口高度、宽度
system("color f0");
gamemode=menu();
if(gamemode==1)
{
mode.BOMBNUMBER=12;
mode.FACE_Y=9;
mode.FACE_X=9;
}
else
{
mode.BOMBNUMBER=42;
mode.FACE_Y=16;
mode.FACE_X=16;
}
Cursor(1);
Inter_Face();
srand(time(NULL)); //main函数中设置srand((unsigned)time(NULL));时,影响的将是所有的rand
while (1)
{
Start_Game();
}
return 0;
}
int menu() //生成开始菜单。
{
char a;
int b;
while(1)
{
system("cls");
Cursor(0);
gotoxy(0,0);
printf("+++++++++++++++++++++++++++++++++++\n");
printf("+ +\n");
printf("+ +\n");
printf("+ 扫雷小游戏 +\n");
printf("+ +\n");
printf("+ +\n");
printf("+ 请选择难度 +\n");
printf("+ +\n");
printf("+ 1、简单模式 +\n");
printf("+ +\n");
printf("+ 2、困难模式 +\n");
printf("+ +\n");
printf("+ 请输入序号(1/2)来开始游戏 +\n");
printf("+ +\n");
printf("+++++++++++++++++++++++++++++++++++\n");
a=getch();
if(a=='1')
{
b=1;
break;
}
else if(a=='2')
{
b=2;
break;
}
}
return b;
}
void Start_Game()
{
int m,n,numb=0;
char ch;
setzero();
setbomb(0,0,0); //总体设置炸弹,不需用到这些参数,随便写三个数,只要第三个不是1就行。
m=(mode.FACE_X%2==0?mode.FACE_X:mode.FACE_X+1); //要使m为偶数,否则方块设置会有问题。
n=mode.FACE_X/2;
gotoxy(m,n);
while(1)
{
Cursor(1); //使光标一直可见,不会因为不恰当的输入而突然消失。
fflush(stdin);
ch=getch();
switch(ch) //对光标进行移动
{
case LEFT:
m-=2;
gotoxy(m,n);
break;
case RIGHT:
m+=2;
gotoxy(m,n);
break;
case UP:
n-=1;
gotoxy(m,n);
break;
case DOWN:
n+=1;
gotoxy(m,n);
break;
case 'd':
case 'D':
numb++;
if(numb==1 && boom.data[m/2][n]==1)
{
setbomb(m,n,1);
display_number(m,n);
}
else if(boom.data[m/2][n]==1)
gameover();
else if(face.data[m/2][n]==BOX)
display_number(m,n);
break;
case 'a':
case 'A':
markbox(m,n);
break;
case ESC:
system("cls");
gotoxy(0,0);
printf("---游戏结束!---\n");
exit(0);
break;
default:
Cursor(1);
break;
}
win();
}
}
void setzero() //清空信息。
{
int m,n;
for(m=1;m<mode.FACE_Y+1;m++) //再次开始游戏前,先把原本的标记信息清空。
for(n=1;n<mode.FACE_X+1;n++)
mark.data[m][n]=0;
mark.number=0;//标记数量置零。
for(m=1;m<mode.FACE_Y+1;m++) //再次开始游戏前,先把原本的炸弹清空。
for(n=1;n<mode.FACE_X+1;n++)
boom.data[m][n]=0;
}
void win() //判断是否胜利。
{
int m,n,numb1=0,numb2=0,flag=0;
char ch;
for(m=1;m<mode.FACE_Y+1;m++) //判断是否标记出所有的炸弹。
{ for(n=1;n<mode.FACE_X+1;n++)
{
if(boom.data[m][n]==1)
if(mark.data[m][n]==1)
numb1++;
}
}
for(m=1;m<mode.FACE_Y+1;m++) //判断是否排除出所有不是炸弹的格子。
{ for(n=1;n<mode.FACE_X+1;n++)
{
if(face.data[m][n]!=KONG_WITH_NUMBER)
numb2++;
}
}
if(numb1==mode.BOMBNUMBER || numb2==mode.BOMBNUMBER)
{
gotoxy(0,mode.FACE_Y+4);
printf("\033[30m------成功啦!游戏结束------");
while(flag==0)
{
gotoxy(0,mode.FACE_Y+5);
printf("是否开启新游戏?(y/n): \b");
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
flag=1;
system("cls"); // 重新开始游戏前,清屏
main();
}
else if (ch == 'N' || ch == 'n')
{
flag=1;
exit(0);
}
else
{
gotoxy(0,mode.FACE_Y+6);
printf("输入错误,请重新输入!");
}
}
}
}
void setbomb(int x,int y,int z) //设置炸弹。
{
int i,m,n,a,b;
if(z==1) //第一次就碰到炸弹的话,为了游戏体验,贴心地把第一个炸弹移走。
{
a=x/2; b=y;
m=rand()%mode.FACE_Y; n=rand()%mode.FACE_X; //随机产生炸弹所在的位置。
while(boom.data[m+1][n+1]==1) //如果该位置已有炸弹,则寻找其他位置。
{
m=rand()%mode.FACE_Y;
n=rand()%mode.FACE_X;
}
boom.data[m+1][n+1]=1;
boom.data[a][b]=0; //第一次的位置变成没有炸弹的状态。
}
else
{
for(i=0;i<mode.BOMBNUMBER;i++) //设置炸弹
{
m=rand()%mode.FACE_Y; n=rand()%mode.FACE_X; //随机产生炸弹所在的位置。
while(boom.data[m+1][n+1]==1) //如果该位置已有炸弹,则寻找其他位置。
{
m=rand()%mode.FACE_Y;
n=rand()%mode.FACE_X;
}
boom.data[m+1][n+1]=1;
}
}
}
void markbox(int x,int y) //标记格子。
{
int m=x/2,n=y;
if(face.data[m][n]==BOX)
{
if(mark.data[m][n]==0)
{
mark.data[m][n]=1;
mark.number++; //使显示的标记数量加1。
gotoxy(2*mode.FACE_X+16,2); //改变显示的标记数量。
printf("\033[1;35m""%2d",mark.number); //%2d的使用,这样从两位数变一位数时才不会乱。
gotoxy(x+2,y);
printf("\b\b");
printf("\033[1;34m""■"); //"\033[1;34m"让其之后的全部字符显示为浅蓝色。"\033[m"让颜色的效果消去。
gotoxy(x,y);
}
else if(mark.data[m][n]==1)
{
mark.data[m][n]=0;
mark.number--;
gotoxy(2*mode.FACE_X+16,2); //改变显示的标记数量。
printf("\033[1;35m""%2d",mark.number);
gotoxy(x+2,y);
printf("\b\b");
printf("\033[1;33m""■");
gotoxy(x,y);
}
}
}
void gameover() //失败,游戏结束。
{
int m,n,flag=0;
char ch;
for(m=1;m<mode.FACE_Y+1;m++) //让所有炸弹显示出来,代表结束。
{ for(n=1;n<mode.FACE_X+1;n++)
{
if(boom.data[m][n]==1)
{
gotoxy(2*(m+1),n);
printf("\b\b");
printf("\033[31m●\033[37m"); //"\033[31m●\033[37m"让其之后的全部字符显示为红色。
}
}
}
gotoxy(0,mode.FACE_Y+4);
printf("\033[30m------失败啦!游戏结束------");
while(flag==0)
{
gotoxy(0,mode.FACE_Y+5);
printf("是否开启新游戏?(y/n): \b");
scanf("%c", &ch);
if (ch == 'Y' || ch == 'y')
{
flag=1;
system("cls"); // 重新开始游戏前,清屏
main();
}
else if (ch == 'N' || ch == 'n')
{
flag=1;
exit(0);
}
else
{
gotoxy(0,mode.FACE_Y+6);
printf("输入错误,请重新输入!");
}
}
}
int bomb_number(int x,int y) //判断中心格周围8个格中炸弹的数目。
{
int m=x/2,n=y,i,j,count=0;
for(i=m-1;i<m+2;i++)
{ for(j=n-1;j<n+2;j++)
{ if(boom.data[i][j]==1)
count++;
}
}
return count;
}
void display_number(int x,int y) //消除方块后非炸弹格显示数字。
{
int m=x/2,n=y,i,j,count=0;
if(mark.data[m][n]==1) //显示数字后的格子当成失去标记。
{
mark.data[m/2][n]=0;
mark.number--;
}
gotoxy(2*mode.FACE_X+16,2); //改变显示的标记数量。
printf("\033[1;35m""%2d",mark.number);
gotoxy(x,y);
if(face.data[m][n]==BOX && boom.data[m][n]==0) //递归时判断是否为没扫过的格或炸弹位置。
{
count=bomb_number(x,y);
if(count==0)
{
face.data[m][n]=KONG;
gotoxy(x+2,y);
printf("\033[1;35m""\b\b");
printf(" ");
gotoxy(x,y);
for(i=m-1;i<m+2;i++)
{
for(j=n-1;j<n+2;j++)
{
if(i>0 && i<=mode.FACE_Y && j>0 && j<=mode.FACE_X)
{ if(i!=m ||j!=n)
display_number(2*i,j);}
}
}
}
else
{
face.data[m][n]=KONG_WITH_NUMBER;
gotoxy(x+2,y);
printf("\033[1;35m""\b\b");
printf(" %d",count);
gotoxy(x,y);
}
}
}
void gotoxy(int x,int y) //坐标跳转
{
COORD coord; //COORD是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标。
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); //SetConsoleCursorPosition是一个API(应用程序编程接口),可以设置控制台窗口中光标的位置。
}
void Inter_Face() //初始化界面
{
int i,j;
system("cls");
if(mode.FACE_X==16)
{
system("mode con lines=24 cols=60"); //设置cmd窗口高度、宽度
gotoxy(2*mode.FACE_X+6,1);
printf("\033[1;35m""炸弹数量:%d",mode.BOMBNUMBER);
gotoxy(2*mode.FACE_X+6,2);
printf("标记数量:%d",mark.number); //但是这里的标记数量不会一直改变。
gotoxy(2*mode.FACE_X+6,4);
printf("ESC键:退出游戏");
gotoxy(2*mode.FACE_X+6,6);
printf("键盘↑键:光标上移");
gotoxy(2*mode.FACE_X+6,7);
printf("键盘↓键:光标下移");
gotoxy(2*mode.FACE_X+6,8);
printf("键盘←键:光标左移");
gotoxy(2*mode.FACE_X+6,9);
printf("键盘→键:光标右移");
gotoxy(2*mode.FACE_X+6,11);
printf("键盘a键:标记炸弹");
gotoxy(2*mode.FACE_X+15,12);
printf("再按取消");
gotoxy(2*mode.FACE_X+6,14);
printf("键盘d键:消除方块");
}
else if(mode.FACE_X==9)
{
system("mode con lines=17 cols=43"); //设置cmd窗口高度、宽度
gotoxy(2*mode.FACE_X+6,1);
printf("\033[1;35m""炸弹数量:%d",mode.BOMBNUMBER);
gotoxy(2*mode.FACE_X+6,2);
printf("标记数量:%d",mark.number);
gotoxy(2*mode.FACE_X+6,4);
printf("ESC键:退出游戏");
gotoxy(2*mode.FACE_X+6,6);
printf("方向键:光标移动");
gotoxy(2*mode.FACE_X+6,8);
printf("键盘a键:标记炸弹");
gotoxy(2*mode.FACE_X+15,9);
printf("再按取消");
gotoxy(2*mode.FACE_X+6,10);
printf("键盘d键:消除方块");
}
gotoxy(0,0);
for(i=0;i<mode.FACE_Y+2;i++)
{
for(j=0;j<mode.FACE_X+2;j++)
{
if(i==0 || i==mode.FACE_Y+1 || j==0 || j==mode.FACE_X+1)
{
face.data[i][j]=WALL;
printf("\033[1;35m""■");
}
else
{
face.data[i][j]=BOX;
printf("\033[1;33m""■");
}
}
gotoxy(0,1+i);//让光标跳到每列的最左边
}
}
void Cursor(int c) //改变光标可见状态。
{
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); //GetStdHandle是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值)。
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(hOut, &cci);
cci.bVisible = c; //赋值0为隐藏,赋值1为可见
SetConsoleCursorInfo(hOut, &cci);
}
?
?
|