消消乐游戏结构
创作背景
最近又放寒假了。真的是作业多多,寒假少少啊……不过在赶作业之余,我也是有时间编程的,只不过没有之前那么"放肆"了。消消乐游戏(简单)是我挤出大量零零散散的时间编出来的(亲测Dev-c++无报错,可以运行):
思路
消消乐游戏的思路真的是很简单易懂了:
1.获取玩家输入(游戏地图边框大小:n),作不合法处理
2.初始化:随机生成地图界面
do{
3.输出当前地图界面
4*.获取玩家鼠标左键点击位置(位于控制台窗口内),作不合法处理
5.深搜(dfs),搜索四连通(四个方向连通且同色)块
6.统计此次消除的得分
7.随机生成数(代表颜色)填补上次消除空白处
}while(地图界面未形成无法消除情况);
总体形成玩家消除,机器填补的循环过程
思路就是上方的执行过程
代码
头文件:
#include<bits/stdc++.h>
#include<windows.h>
宏定义:
#define dn() ((GetAsyncKeyState(VK_LBUTTON)&0x8000)?true:false)//检测鼠标左键是否按下
变量和数组:
POINT p;
CONSOLE_FONT_INFO font;
int a[11][11],n,x,y,v,sc;
int dx[]={0,-1,0,0,1},dy[]={0,0,-1,1,0};//方向数组(上下左右)
bool g[11][11];//dfs时用作标记
初始化函数:
void init(){//获取玩家输入(n)并随机生成地图界面
memset(a,0,sizeof(a));
do{
cout<<"Please enter the size of interface(2<=n<=10):";
cin>>n;
}while(n<2 || n>10);
srand(int(time(NULL)));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]=rand()%6+1;
}
输出地图界面函数:
void put(){//输出当前地图界面
memset(g,false,sizeof(g));
system("cls");
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
switch(a[i][j]){
case 0:cout<<" ";continue;
case 1:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);break;
case 2:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN);break;
case 3:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN);break;
case 4:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_BLUE);break;
case 5:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_BLUE);break;
case 6:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
cout<<"□";
}
cout<<endl;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<"Score:";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<sc<<endl<<endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
对玩家获取的鼠标左键点击坐标(列)转换:
int c(int y){//列转换函数
return (y%2?(y+1)/2:y/2);
}
深搜函数(此情境下无需回溯):
void dfs(int x,int y){//搜索四连通且同色块
if(a[x][y]!=v)
return;
for(int d=1;d<5;d++){
int xx=x+dx[d],yy=y+dy[d];
if(xx>0 && xx<=n && yy>0 && yy<=n && !g[xx][yy] && a[xx][yy]==v){
g[xx][yy]=true;
dfs(xx,yy);
}
}
}
判断当前地图界面是否成无法消除情况:
bool lose(){//判断可消除性
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int d=1;d<5;d++){
int xx=i+dx[d],yy=j+dy[d];
if(xx>0 && xx<=n && yy>0 && yy<=n && a[i][j]==a[xx][yy])
return false;
}
return true;
}
主函数:
int main(){
init();
do{
Sleep(1250);
put();
DWORD mode;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),&mode);
mode&=~ENABLE_QUICK_EDIT_MODE;
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),mode);
do{//循环检测鼠标左键点击位置的合法性
while(!dn());
POINT p;
GetCursorPos(&p);
ScreenToClient(GetForegroundWindow(),&p);
GetCurrentConsoleFont(GetStdHandle(STD_OUTPUT_HANDLE),FALSE,&font);
x=(p.y/=font.dwFontSize.Y)+1;
y=(p.x/=font.dwFontSize.X)+1;
Sleep(100);
if(x>n || y>n*2)
continue;
break;
}while(1);
v=a[x][c(y)];
dfs(x,c(y));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(g[i][j]){
sc++;//统计此次消除得分
a[i][j]=0;
}
put();
srand(int(time(NULL)));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!a[i][j])
a[i][j]=rand()%6+1;//填补上次消除空白处
}while((n<7?(!lose()):1));//地图越大,那无法消除的情况就越无法达到,所以我控制范围,争取较少的时间复杂度(觉得不稳妥的可以改成"while(!lose())")
Sleep(1875);
put();
Sleep(1875);
system("cls");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED);
cout<<"You lost!";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<"\n\nScore:";
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN | FOREGROUND_BLUE);
cout<<sc;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
return 0;
}
总结
这次的消消乐游戏的"精华"在于获取玩家的鼠标左键的点击位置。其实真的不算难,主要是获取点击位置时花的时间太多。不过最后还是起作用了。这就与平常的游戏结构差不多。
若有任何错误、问题或需求,各位大神请在评论区发表评论。