電腦如何自動玩popstar(BFS,剪枝)


非正經向更博~~~

只是一個無聊的想法,然而百度以后發現解決這個問題的方案很少,也少有實現。於是又來瞎寫一篇文章。

游戲規則這么簡單就不介紹了吧

步驟

自動截屏並識別方塊顏色

不會,等上大學再學圖像處理

不對呀,蒟蒻沒有大學上呀嚶嚶嚶

計算最優解

過程中最核心的部分。

其實算出最優解是幾乎不可能做到的,因為只能暴搜,而狀態數太大,交給家用電腦絕對TLE(如果需要記憶化去重還MLE)

交給超算還是有希望很快出解

那只好用一些剪枝算法,能在可接受的時間內算出可接受的高分解了

popstar游戲每過一關需要的累計分數比上一關增加最多4000

所以如果能穩定算出4000以上的解就可以無限過關啦

然而這又是不可能的,因為隨機一個初始狀態甚至都有可能一步也動不了=直接爆0

。。。。。。

想得高分,就要防止有機會得高分的狀態被剪掉。

目前看來,積累高分的途徑主要有2個

一個是通過聚聚聚聚聚起一個很大的同色塊消除得高分,每個小方塊的得分與消除時的同色塊大小成正比

網上有一個想法,以當前得分+當前所有未被消除色塊的分值作為估價函數進行搜索

比起單純以當前得分為依據進行剪枝,這種方法對於這種情況的處理要好很多。

畢竟在聚起大塊爆分之前,總要消掉一些別的顏色的小塊,經歷一段時間的得分低谷寫出了一些哲理的味道

另一個是通過最后剩的單塊少於10得分,這個沒有規律可循,只能在接近終盤的時候少剪枝吧


已實現,目前對於隨機的開局,在\(1s\)內完成搜索,平均分可達\(5500\),分數不到\(4000\)的概率小於\(5\%\),無限過關很穩了。

\(100\)個數組,下標為\(i\)的數組存所有剩余塊數為\(i\)的狀態。

對於每個數組,去重,按上述估價函數排序,只對前LIM個狀態進行bfs擴展。

附帶結果展示功能,#define SHOW即可

#include<bits/stdc++.h>
#include<windef.h>
#include<winbase.h>
#include<wincon.h>
#include<conio.h>
#define LL long long
#define R register int
#define Handle GetStdHandle(STD_OUTPUT_HANDLE)
#define SetPos(Y,X) SetConsoleCursorPosition(Handle,(COORD){(short)(X),(short)(Y)})
#define cprintf(c,a...)	SetConsoleTextAttribute(Handle,c),printf(a)
//#define SHOW
using namespace std;
const int N=10,LIM=1e3,ColorID[]={0,11,12,10,14,13};
struct Data{//狀態,一列方塊用二進制壓進一個int里
	int a[N],sc,ex;Data*pre;
	inline bool operator<(const Data&x)const{
		if(ex!=x.ex)return ex>x.ex;
		for(R i=0;i<N;++i)
			if(a[i]!=x.a[i])return a[i]<x.a[i];
		return 0;
	}
	inline bool operator==(const Data&x)const{
		for(R i=0;i<N;++i)
			if(a[i]!=x.a[i])return 0;
		return 1;
	}
	inline int operator()(R x,R y)const{
		return a[y]>>3*x&7;
	}
	inline void operator()(R x,R y,R z){
		a[y]^=((a[y]>>3*x&7)^z)<<3*x;
	}
	inline void pop(R x,R y){//x行y列消除
		a[y]=(a[y]>>3*(x+1)<<3*x)|(a[y]&((1<<3*x)-1));
		if(a[y])return;
		for(++y;y<N;++y)a[y-1]=a[y];
		a[N-1]=0;
	}
};
vector<Data>v[N*N+1];
Data mp,*ansp,a[N*N];
bool fl,vis[N][N];
int x,y,c,ans,ps,pt,px[N*N],py[N*N];
inline void swap(R&x,R&y){
	R z=x;x=y;y=z;
}
void Print(Data mp){//展示局面
	SetPos(0,0);
	for(R i=N-1;~i;--i){
		printf(" ");
		for(R j=0;j<N;++j)
			cprintf(ColorID[mp(i,j)],"┌───┐");
		cprintf(15,"\n%d",i);
		for(R j=0;j<N;++j)
			cprintf(ColorID[mp(i,j)],"│ ★│");
		printf("\n ");
		for(R j=0;j<N;++j)
			cprintf(ColorID[mp(i,j)],"└───┘");
		printf("\n");
	}
	cprintf(15," ");
	for(R j=0;j<N;++j)
		printf("  %d  ",j);
	printf("\nscore=%d\n",mp.sc);
}
void Flash(Data mp){//展示用,使下一步消除塊閃爍
	for(R i=0;i<=pt;++i)
		px[i]=N-1-px[i];
	do{
		Print(mp);
		Sleep(200);
		for(R i=0;i<=pt;++i){
			SetPos(3*px[i]  ,1+5*py[i]),printf("     ");
			SetPos(3*px[i]+1,1+5*py[i]),printf("     ");
			SetPos(3*px[i]+2,1+5*py[i]),printf("     ");
		}
		SetPos(3*N+2,0);
		Sleep(300);
	}while(!kbhit());
	getch();
}
int main(){
	freopen("save.dat","r",stdin);
	scanf("%d,%d,%d",&x,&x,&x);
	for(R i=N-1;~i;--i)//開局讀入
		for(R j=0;j<N;++j){
			x=rand()%6;
			scanf("%d",&x);
			if(x<0||x>5)return 0;
			if(x)++c;
			mp(i,j,x);
		}
	v[c].push_back(mp);
	for(R l=N*N;~l;--l){//搜索
		sort(v[l].begin(),v[l].end());//狀態去重
		v[l].resize(unique(v[l].begin(),v[l].end())-v[l].begin());
		for(Data&now:v[l]){//計算估價函數
			fl=0;memset(vis,0,sizeof(vis));
			for(R i=N-1;~i;--i)
				for(R j=N-1;~j;--j){
					if(vis[i][j]||(c=now(i,j))==0)continue;
					px[0]=i;py[0]=j;vis[i][j]=1;
					for(ps=pt=0;ps<=pt;++ps){
						x=px[ps];y=py[ps];
#define GO(X,Y,A) if(A&&!vis[X][Y]&&c==now(X,Y))px[++pt]=X,py[pt]=Y,vis[X][Y]=1
						GO(x-1,y,x);
						GO(x,y-1,y);
						GO(x+1,y,x+1<N);
						GO(x,y+1,y+1<N);
					}
					if(pt)now.ex+=(pt+1)*(pt+1)*5;
				}
		}
		sort(v[l].begin(),v[l].end());
		if(v[l].size()>LIM)v[l].resize(LIM);
		for(Data&now:v[l]){//狀態擴展
			fl=0;memset(vis,0,sizeof(vis));
			for(R i=N-1;~i;--i)
				for(R j=N-1;~j;--j){
					if(vis[i][j]||(c=now(i,j))==0)continue;
					px[0]=i;py[0]=j;vis[i][j]=1;
					for(ps=pt=0;ps<=pt;++ps){
						x=px[ps];y=py[ps];
						GO(x-1,y,x);
						GO(x,y-1,y);
						GO(x+1,y,x+1<N);
						GO(x,y+1,y+1<N);
					}
					if(!pt)continue;
					fl=1;
					for(ps=0;ps<pt;++ps)
						for(R k=pt;k>ps;--k)
							if(px[k-1]<px[k]||(px[k-1]==px[k]&&py[k-1]<py[k]))
								swap(px[k-1],px[k]),swap(py[k-1],py[k]);
					mp=now;mp.pre=&now;mp.ex=mp.sc=mp.sc+(pt+1)*(pt+1)*5;
					for(ps=0;ps<=pt;++ps)
						mp.pop(px[ps],py[ps]);
					v[l-pt-1].push_back(mp);
				}
			if(!fl){//算分
				if(l<10)now.sc+=2000-20*l*l;
				if(ans<now.sc)ans=now.sc,ansp=&now;
			}
		}
	}
	for(pt=0;ansp;ansp=ansp->pre)
		a[pt++]=*ansp;
	for(R l=N*N;~l;--l)v[l].resize(0);
#ifdef SHOW//結果展示
	for(R i=pt-1;i;--i){
		memset(vis,0,sizeof(vis));
		Data now=a[i];
		for(y=0;now.a[y]==a[i-1].a[y];++y);
		for(x=0;(now.a[y]<<(29-3*x))==(a[i-1].a[y]<<(29-3*x));++x);
		c=now(x,y);px[0]=x;py[0]=y;vis[x][y]=1;
		for(ps=pt=0;ps<=pt;++ps){
			x=px[ps];y=py[ps];
			GO(x-1,y,x);
			GO(x,y-1,y);
			GO(x+1,y,x+1<N);
			GO(x,y+1,y+1<N);
		}
		Flash(a[i]);
	}
	Print(a[0]);
#endif
	return a[0].sc;
}

save.dat示例

0,0,0
4 5 4 3 1 3 1 1 1 3 
4 1 3 2 3 2 1 1 3 1 
2 2 1 2 3 4 2 1 1 4 
2 1 1 3 3 3 2 3 2 3 
4 4 4 3 3 5 3 2 4 2 
5 5 5 1 4 5 5 2 2 5 
5 2 4 3 3 5 3 2 4 3 
3 4 4 4 1 3 3 1 2 5 
4 3 5 5 5 2 5 3 4 1 
4 4 1 5 4 2 5 1 5 1 

展示效果

模擬鼠標點擊

百度發現好像C++有函數茲瓷

待更


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM