if ((ii!=fi)&&(jj!=fj)) ++t2;
					}
				}
			}
			if (t1>2) TSp=true;
			if ((t1>2)&&(t2>1)) TFull=true;
			if (_p.GetName()!="T") TFull=TSp=Spin;
		}
		if ((SpinRule==SR_Guildline)&&(!Mod8)&&(!IsSpinBonus(_p.GetName()))&&(Lines<4)) Spin=false;
		if ((SpinRule==SR_Guildline)&&(!Mod8)) {
			if ((!TSp)&&(Lines<4)) Spin=false;
		}
		bool CalcAllMini = ((SpinRule==SR_Default)&&(Gen.Pool.size()<=10)&&(!Mod8)&&((BotType==1)||(BotType==3)));
		bool AllMini=false, ex1=false, ex2=false, sw=false, ew=false; int eu=0;
		for (int i=0; i<PLX; ++i) {
			for (int j=0; j<PLX; ++j) {
				int x=o.x+i, y=o.y+j;
				if ((x<1)||(y<1)||(x>LX)||(y>LY)) continue;
				if (!p.GetGrid(i,j)) {
					if (!CalcAllMini) continue;
					int ad = 0;
					if ((i>0)&&(p.GetGrid(i-1,j))) ++ad;
					if ((i+1<PLX)&&(p.GetGrid(i+1,j))) ++ad;
					if ((j>0)&&(p.GetGrid(i,j-1))) ++ad;
					if ((j+1<PLX)&&(p.GetGrid(i,j+1))) ++ad;
					if (ad>=2) {
						ew=true; if (!D[x][y]) sw=true;
					}
					continue;
				}
				++Fill[x];
				PieceHeight = max(PieceHeight,x);
				LeftMost = min(LeftMost,y);
				RightMost = max(RightMost,y);
				Edge = min(Edge,min(y-1,LY-y));
				if (!CalcAllMini) continue;
				int ex = 0;
				if ((x==1)||(D[x-1][y])) ++ex;
				if ((x==LX)||(D[x+1][y])) {
					++ex;
					if ((i+1==PLX)||(!p.GetGrid(i+1,j))) ++eu;
				}
				if ((y==1)||(D[x][y-1])) ++ex;
				if ((y==LY)||(D[x][y+1])) ++ex;
				if (ex<=1) ex1=true;
				if (ex<=2) ex2=true;
			}
		}
		if (CalcAllMini) {
			if (IsOType(_p.GetName())) AllMini=(eu<2);
			else AllMini=((!Use90)||(ex1)||((ex2)&&((sw)||(!ew))));
		}
		int FillCnt=0, FillCol=0;
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (D[i][j]) {
					if (!Upper[j]) {
						Upper[j]=true; ++FillCol;
					}
				}
			}
			if ((Fill[i])&&(FillCol==LY)) ++FillCnt;
		}
		/*int LQH=0, RQH=0;
		if ((BotType==6)||(BotType==7)) {
			int acc = 0;
			for (int i=1; i<=LX; ++i) {
				int cur = 0;
				for (int j=2; j<=LY; ++j) {
					if (D[i][j]) break;
					else ++cur;
				}
				if (cur>=LY-1) break;
				if (cur) {
					acc+=cur; if (acc>3) break;
				}
				else {
					cur=0; LQH=i;
				}
			}
			for (int i=1; i<=LX; ++i) {
				int cur = 0;
				for (int j=LY-1; j; --j) {
					if (D[i][j]) break;
					else ++cur;
				}
				if (cur>=LY-1) break;
				if (cur) {
					acc+=cur; if (acc>3) break;
				}
				else {
					cur=0; RQH=i;
				}
			}
		}*/
		int Ero = 0;
		for (int i=1; i<=LX; ++i) {
			bool f = true;
			for (int j=1; j<=LY; ++j) {
				if (!D[i][j]) {
					f=false; break;
				}
			}
			for (int x : StaticLines) {
				if (i==x) f=false;
			}
			if (f) {
				if (Fill[i]) ++Lines;
				Ero += Fill[i];
				if (GravityType==GV_None) {
					++pp; for (int j=1; j<=LY; ++j) D[pp][j]=false;
				}
			}
			else {
				++pp;
				if (i!=pp) {
					for (int j=1; j<=LY; ++j) {
						D[pp][j]=D[i][j]; D[i][j]=false;
					}
				}
			}
		}
		Sta->Clutch = false;
		if ((ClutchRule==CLR_Default)&&(Lines)) Sta->Clutch=true;
		if (ClutchRule==CLR_Always) Sta->Clutch=true;
		if (Lines) ++Sta->Combo;
		else Sta->Combo=0;
		ll res = 0LL;
		ll ares = 0LL;
		bool SetSpin = (((BotType==1)||(BotType==3)||(BotType==6)||(BotType==7)||(BotType==8))&&(SpinMode));
		for (int i=0; i<=LX+1; ++i) D[i][0]=D[i][LY+1]=true;
		for (int i=0; i<=LY+1; ++i) D[0][i]=D[LX+1][i]=true;
		int BegHeight=Height; Height=0;
		bool qd = ((SetSpin)&&((BotType==6)||(BotType==7)));
		int WellCol = -1;
		if ((Sta->LastClearType.second>0)&&(Sta->LastClearType.second<4)&&(Height>6)) {
			if (BotType==9) qd=false;
			else if (qd) WellCol=-1;
		}
		int _wc = -1;
		int _wh = 0;
		if ((qd)||((SpinRule==SR_Guildline)&&(SetSpin)&&(!Mod8)&&((BotType==1)||(BotType==3)))) {
			int mn = LX+1;
			if (qd) {
				for (int j=LY; j; --j) {
					if ((j>=2)&&(j<=LY-1)) continue;
					int p = 0;
					for (int i=LX; i; --i) {
						if (D[i][j]) {
							p=i; break;
						}
					}
					if (p<mn) {
						mn=p; WellCol=j;
					}
				}
				for (int j=LY; j; --j) {
					if ((j==1)||(j==LY)) continue;
					int p = 0;
					for (int i=LX; i; --i) {
						if (D[i][j]) {
							p=i; break;
						}
					}
					if (p<mn) {
						mn=p; WellCol=j;
					}
				}
			}
			else {
				for (int j=LY-3; j>=3; --j) {
					int p = 0;
					for (int i=LX; i; --i) {
						if (D[i][j]) {
							p=i; break;
						}
					}
					if (p<mn) {
						mn=p; WellCol=j;
					}
				}
				for (int j=LY; j>=1; --j) {
					if ((j!=1)&&(j!=LY)) continue;
					int p = 0;
					for (int i=LX; i; --i) {
						if (D[i][j]) {
							p=i; break;
						}
					}
					if (p<mn) {
						mn=p; WellCol=j;
					}
				}
				for (int j=LY; j>=1; --j) {
					if ((j!=2)&&(j!=LY-1)) continue;
					int p = 0;
					for (int i=LX; i; --i) {
						if (D[i][j]) {
							p=i; break;
						}
					}
					if (p<mn) {
						mn=p; WellCol=j;
					}
				}
				_wc=WellCol; _wh=mn;
			}
			if (mn+10>SX) WellCol=-1;
		}
		if ((BotType==6)||(BotType==7)) {
			if ((WellCol>=2)&&(WellCol<=LY-1)) WellCol=-1;
		}
		if ((Sta->LastClearType.second>0)&&(Sta->LastClearType.second<4)&&(Height>10)) {
			if ((BotType==6)||(BotType==7)) WellCol=-1;
		}
		if ((BotType==9)&&(SpinMode)) {
			int mn = LX+1;
			for (int j=LY; j; --j) {
				if ((j>=2)&&(j<=LY-1)) continue;
				int p = 0;
				for (int i=LX; i; --i) {
					if (D[i][j]) {
						p=i; break;
					}
				}
				if (p<mn) {
					mn=p; WellCol=j;
				}
			}
			for (int j=LY; j; --j) {
				if ((j==1)||(j==LY)) continue;
				int p = 0;
				for (int i=LX; i; --i) {
					if (D[i][j]) {
						p=i; break;
					}
				}
				if (p<mn) {
					mn=p; WellCol=j;
				}
			}
			if (mn>=SX/2) WellCol=-1;
			if ((WellCol>=2)&&(WellCol<=LY-1)) WellCol=-1;
			if (WellCol==-1) res-=30000;
		}
		if ((qd)&&(WellCol==-1)) res-=30000;
		/*if ((BotType==6)||(BotType==7)) {
			if (SpinMode) res+=(LX-Height)*10;
		}*/
		if ((SpinRule==SR_Guildline)&&(!Mod8)&&((BotType==1)||(BotType==3))) {
			WellCol = _wc;
			if (SetSpin) {
				if ((_wc==2)||(_wc==LY-1)) res-=200000;
				if ((_wc==1)||(_wc==LY)) {
					res-=50000; _wc=-1;
				}
			}
			else WellCol=_wc=-1;
		}
		else _wc=-1;
		//if (WellCol!=-1) printf("!%d\n",WellCol);
		for (int i=1; i<=LX; ++i) {
			int fc = 0;
			for (int j=1; j<=LY; ++j) {
				if (D[i][j]) ++fc;
			}
			int WellCnt = 0;
			for (int j=1; j<=LY; ++j) {
				if (D[i][j]) {
					Height=max(Height,i); continue;
				}
				if (D[i+1][j]) {
					if (GravityType==GV_None) ;
					else if (BotType==5) {
						if (((j<=2)||(D[i][j-1])||(D[i+1][j-1]))&&((j>=LY-1)||(D[i][j+1])||(D[i+1][j+1])))
							res-=5LL;
						else --res;
					}
					else if (BotType==9) res-=5LL;
					else if (qd) res-=20LL;
					else if ((!SetSpin)||(qd)) res-=5LL;
					else if ((SpinRule==SR_Guildline)&&(!Mod8)) {
						if ((((j<=2)||(D[i][j-1])||(D[i+1][j-1]))&&((j>=LY-1)||(D[i][j+1])||(D[i+1][j+1])))||((j==1)||(j==LY))) res-=50LL;
						else res-=10LL;
					}
					else if ((((j<=2)||(D[i][j-1])||(D[i+1][j-1]))&&((j>=LY-1)||(D[i][j+1])||(D[i+1][j+1])))||((j==1)||(j==LY)))
						res-=10LL;
					else res+=0;
				}
				if ((D[i-1][j])||(i==1)) --res;
				if ((D[i][j-1])||(j==1)||(j-1==WellCol)) --res;
				if ((D[i][j+1])||(j==LY)||(j+1==WellCol)) --res;
				if (((D[i][j-1])||(j==1)||(j-1==WellCol))&&((D[i][j+1])||(j==LY)||(j+1==WellCol))) {
					if (_wc>0) {
						if ((j!=_wc)&&(i>1)&&(!D[i-1][j])) res-=10;
						if (j==_wc) ;
						else if (!D[i+1][j]) ;
						else if (abs(j-_wc)>1) res-=15;
						else if (D[i][max(1,min(LY,_wc+_wc-j))]) res-=30;
						else if ((!D[i-1][j])||(!D[i-1][max(1,min(LY,_wc+_wc-j))])) res-=30;
						else if ((i+2<=LX)&&((D[i+2][j])&&(D[i+2][max(1,min(LY,_wc+_wc-j))]))) res-=30;
					}
					if (BotType==9) {
						if (j!=WellCol) --res;
					}
					else if (((!qd)&&(_wc<0))||(j!=WellCol)) {
						--res;
					}
					else if (fc>=LY-1) res+=2;
				}
				if ((_wc>0)&&((D[i+1][j])&&(D[i][j-1])&&(D[i][j+1]))&&(abs(i-_wc)>1)) res-=10;
			}
		}
		if ((_wc>1)&&(_wc<LY)) {
			int Base = 0;
			for (int i=1; i<=LX; ++i) {
				if ((D[i][_wc-1])&&(D[i][_wc+1])) Base=i;
			}
			int hl=0, hr=0;
			while ((Base+hl+1<=LX)&&(D[Base+hl+1][_wc-1])) ++hl;
			while ((Base+hr+1<=LX)&&(D[Base+hr+1][_wc+1])) ++hr;
			int del = abs(hl-hr);
			res -= ((del==1)?3:min(5,del))*2;
			res -= Height*10;
		} 
		if ((!SetSpin)&&((BotType==1)||(BotType==2)||(BotType==3)||(BotType==4))&&(PlayerCount>1)) ares-=Height*500;
		if ((GetT)&&(SetSpin)&&((BotType==1)||(BotType==3))) {
			int c1=0, c0=0;
			int mul = 1;
			int pp=0, ph=0;
			for (int j=2; j<LY; ++j) {
				for (int i=LX-1; i>=2; --i) {
					if (D[i][j]) break;
					if ((D[i][j])||(D[i-1][j])||(D[i+1][j])||(D[i][j-1])||(D[i][j+1])) continue;
					if ((!D[i-1][j-1])||(!D[i-1][j+1])) continue;
					int k = int(D[i+1][j-1])+int(D[i+1][j+1]);
					if (j==_wc) mul=6;
					if ((j!=_wc)&&(_wc>0)) continue;
					if (k==1) {
						++c1;
						if (!pp) {
							if (D[i+1][j-1]) pp=j-1;
							if (D[i+1][j-1]) pp=j+1;
							ph = i+1;
						}
					}
					if (!k) {
						if (((D[i][j-2])||(j==2))&&((D[i][j+2])||(j==LY-1))) ++c0;
					}
				}
			}
			if ((pp)&&(_wc>0)) {
				int d = 1;
				while ((ph+d<=LX)&&(D[ph+d][pp])) ++d;
				if (d==1) ++res;
				if (d>2) res-=2*(d-2);
			}
			if (c1) res+=5*mul;
			else if (c0) res+=1*mul;
			/*int c1=0, c0=0;
			for (int j=2; j<=LY; ++j) {
				for (int i=LX-1; i>=2; --i) {
					if (D[i][j]) continue;
					if ((D[i-1][j])||(D[i][j-1])||(D[i-1][j-1])) continue;
					if ((j>2)&&((!D[i][j-2])&&(!D[i-1][j-2]))) continue;
					if ((j<LY)&&((!D[i][j+1])&&(!D[i-1][j+1]))) continue;
					if ((i>2)&&((!D[i-2][j])&&(!D[i-2][j-1]))) continue;
					if (((j>2)&&(!D[i][j-2]))&&((j<LY)&&(!D[i-1][j+1]))) continue;
					if (((j>2)&&(!D[i-1][j-2]))&&((j<LY)&&(!D[i][j+1]))) continue;
					int k = int(D[i+1][j])+int(D[i+1][j-1]);
					if (k) ++c1;
					if (!k) {
						if (((D[i][j-2])||(j==2))&&((D[i][j+1])||(j==LY))) ++c0;
					}
				}
			}
			if (c1) res+=5;
			else if (c0) res+=1;*/
		}
		bool AllClear = (Height<=SolidHeight);
		if ((BotType==6)||(BotType==7)) AllClear=false;
		if (SetSpin) {
			if ((AllClearRule!=AR_IO)&&(AllClearRule!=AR_Default)&&(AllClearRule!=AR_QP)&&(AllClearRule!=AR_Techmino)
			&&(B2BRule!=BR_None)&&(B2BRule!=BR_Guildline)&&(B2BRule!=BR_Techmino)) AllClear=false;
		}
		Sta->LineAfterPC += Lines;
		if (AllClear) Sta->LineAfterPC=0;
		if ((Mod8)&&((Lines)||(Spin))) {
			pair<string,int> NewClearType = make_pair("",Lines);
			if (Spin) NewClearType.first=_p.GetRealName();
			if (Mod8==2) {
				if (Spin) NewClearType.first="X";
				else NewClearType.second=0;
			}
			if (!Lines) NewClearType.second=-1;
			if (NewClearType==Sta->LastClearType) ares-=10000000000LL;
			Sta->LastClearType = NewClearType;
		}
		bool sth = false;
		if ((Lines)&&(sth)) {
			if (((Lines!=1)||((!Spin)&&((!Spinable)||(!TSp))))) ares-=1000000000LL;
			else if ((_f)&&(Lines<4)&&(!Spin)&&(!TSp)) ares-=1000000000LL;
			else ares+=10000LL;
		}
		if (BotType==5) {
			int Parity=0, CParity=0;
			for (int i=1; i<=LX; ++i) {
				for (int j=1; j<=LY; ++j) {
					if (D[i][j]) {
						if (j&1) ++CParity;
						else --CParity;
						if ((i+j)&1) ++Parity;
						else --Parity;
					}
				}
			}
			Parity=abs(Parity); CParity=abs(CParity);
			//res -= CParity;
			int Goal = 4;
			if (Height+Sta->LineAfterPC>Goal) ares-=1000000000LL;
			else {
				int h = min(LX,Goal-Sta->LineAfterPC);
				for (int i=1; i<=LY; ++i) {
					if (!D[h][i]) --res;
				}
				vector<vector<char> > G(h+2,vector<char>(LY+2,0));
				for (int i=0; i<=h+1; ++i) G[i][0]=G[i][LY+1]=1;
				for (int i=0; i<=LY+1; ++i) G[0][i]=G[h+1][i]=1;
				for (int i=1; i<=h; ++i) {
					for (int j=1; j<=LY; ++j) G[i][j]=D[i][j];
				}
				for (int i=1; i<=h; ++i) {
					for (int j=1; j<=LY; ++j) {
						if (!G[i][j]) {
							int val = VecDfs(G,i,j);
							if (val&1) {
								if ((i>=h-1)||(val==1)) res-=5000LL;
								else res-=50LL;
							}
							else if (val&3) {
								if ((i>=h-1)||(val==2)) res-=10LL;
								else res-=5LL;
							}
						}
					}
				}
			}
		}
		res -= Height;
		int RotDis=((DefaultRot-o.d+4)&3); if (RotDis==3) RotDis=1;
		ares += Lines*Ero*2000LL;
		//if (!IsSpinBonus(p.GetName())) Spin=0; Spin|=TSp;
		int _Spin = Spin;
		if ((!Mod8)&&((SpinRule==SR_AllMini)||(BotType==8)||(Mod2==2))) Spin|=TSp;
		if ((!Mod8)&&((CenterY==1)||(CenterY==LY))) {
			if ((!Spin)&&(TSp)) Spin=AllMini=true;
		}
		if (qd) Spin=0;
		if ((_f)&&(TQPScore<=1100000)&&(TFull)) Spin=0;
		if ((_f)&&(AllClear)) Spin=0;
		if ((Lines)&&((BotType==1)||(BotType==3)||(BotType==6)||(BotType==7)||(BotType==8))) {
			int SpinBonus = ((!AllMini)?5000:2500);
			if (GetT) SpinBonus=(TFull)?10000:(((TSp)&&(_Spin))?5000:500);
			if (Lines>3) SpinBonus=max(SpinBonus,5000);
			if (AllClear) SpinBonus=max(SpinBonus,10000);
			if ((_f)&&(TFull)) SpinBonus=0;
			if ((_f)&&(Lines==1)) SpinBonus*=5;
			if ((_f)&&(TQPScore<=1100000)&&(Lines>1)&&(Lines<4)) SpinBonus=-5000;
			if (SetSpin) {
				bool CurB2B = (((Lines>3)&&(Mod8<2))||(Spin));
				if ((AllClear)&&(!_f)&&((AllClearRule==AR_Default)||(AllClearRule==AR_IO)
				||(AllClearRule==AR_QP)||(AllClearRule==AR_Techmino))) CurB2B=true;
				if ((!QPBlighted)&&(Mod6==2)) CurB2B=true;
				if ((Spinable)&&(TSp)&&(sth)) CurB2B=true;
				if (CurB2B) {
					//if ((BotType==1)&&(TFull)) SpinBonus*=10;
					int l = Lines+2;
					//if (GetT) l=((TFull)||(Lines>=4))?Lines+2:2;
					if (_f) l=2;
					if ((sth)||(_f)||(Gen.Pool.size()>=10)||(ModM)||((GetT)&&(!TSp))) ares+=ll(SpinBonus)*l;
					else ares+=ll(SpinBonus)*(4+max(0,Sta->Combo-1))/4*l;
				}
				else ares-=max(0LL,((Mod8==2)?2000000LL:1000000LL)*(SX-PreHeight)/SX);
			}
			else if (((Lines>3)||(Spin))&&(Height+8<=SX)) {
				SpinBonus /= 2;
				if ((sth)||(_f)||(Gen.Pool.size()>=10)||(ModM)||((GetT)&&(!TSp))) ares+=ll(SpinBonus)*Lines;
				else ares+=ll(SpinBonus)*(4+max(0,Sta->Combo-1))/4*Lines;
			}
		}
		if ((SpinMode)&&(GetT)&&(IsSpinBonus(_p.GetName()))&&((!Lines)||(!TFull))) {
			ares -= 300LL;
			if (_wc>0) {
				if (_wc-1>LY-_wc) {
					if (y>_wc) ares-=100LL;
				}
				if (LY-_wc>_wc-1) {
					if (y<_wc) ares-=100LL;
				}
			}
		}
		if (((BotType==1)||(BotType==3))&&((!ModM)&&(Gen.Pool.size()<10))) ares+=3000LL*ll(max(0,min(4,Sta->Combo-1)));
		//if ((_wc>0)&&((!Lines)||(!TSp))&&(IsSpinBonus(_p.GetName()))) ares-=500LL;
		//if ((GetT)&&((!Lines)||(!TFull))&&(IsSpinBonus(p.GetName()))&&(SetSpin)) ares-=100LL;
		if ((Mod8==2)&&((BotType==1)||(BotType==3)||(BotType==8))&&(1||!ModP)) {
			if ((SetSpin)&&(B2B>=4)) {
				if ((Spin)&&(!Lines)) ares+=(ModP)?2000LL:5000LL;
			}
		}
		if ((BotType==9)&&(Lines)&&(SpinMode)&&(WellCol>0)) {
			if ((Combo)&&(SX-PreHeight<10)) ;
			else if (FillCnt<4) res-=50;
		}
		if ((Lines)&&(!_f)) {
			if (AllClear) ares+=(SetSpin)?5000LL:100000LL;
		}
		res = res*2000LL;
		if ((SetSpin)&&(!qd)&&(BotType!=6)&&(BotType!=7)&&((Height+10<=SX)||(PlayerCount<2))) {
			int hd=2, tot=0, tot4=0;
			for (Piece &p : Gen.Pool) {
				++tot; if (p.GetLX()>=4) ++tot4;
			}
			if (tot4*3>=tot) hd=4;
			if (tot4*3>=tot*2) hd=8;
			for (int i=Height; i>=1; --i) {
				if (D[i][1]) {
					if ((i>=Height-hd)&&((i==1)||(D[i-1][1]))) {
						res += 2000LL;
						if (D[i][2]) res+=1000L;
					}
				}
				//else if (D[i+1][1]) res-=2000LL;
			}
			for (int i=Height; i>=1; --i) {
				if (D[i][LY]) {
					if ((i>=Height-hd)&&((i==1)||(D[i-1][LY]))) {
						res += 2000LL;
						if (D[i][LY-1]) res+=1000L;
					}
				}
				//else if (D[i+1][LY]) res-=2000LL;
			}
			/*for (int i=1; i<=LX; ++i) {
				for (int j=1; j<=LY; ++j) {
					int ed = (LY>>1)-min(j-1,LY-j-1);
					if (D[i][j]) res-=((LY>>1)*(LY>>1)-ed*ed)*10LL;
				}
			}*/
		}
		if ((SetSpin)&&(!qd)&&(BotType!=6)&&(BotType!=7)&&(LY>=6)) {
			int cnt = 0;
			for (int i=1; i+2<=LX; ++i) {
				for (int j=1; j<LY; ++j) {
					if ((!D[i][j])&&(!D[i][j+1])
					&&(!D[i+1][j])&&(!D[i+1][j+1])&&(D[i+1][j-1])&&(D[i+1][j+2])
					&&((j==1)||(j==LY-1)||((!D[i+2][j])&&(!D[i+2][j+1])&&(D[i+2][j-1]&&D[i+2][j+2])))) ++cnt;
				}
			}
			if (cnt) res-=2000LL*cnt;
		}
		if (!BotType) res-=Edge*100LL+10LL*ll(LeftMost+RightMost<=LY+LY)+ll(RotDis);
		Sta->AccEval += ares;
		for (int i=0; i<=LX+1; ++i) D[i][0]=D[i][LY+1]=false;
		for (int i=0; i<=LY+1; ++i) D[0][i]=D[LX+1][i]=false;
		return res;
	}
	static vector<pair<int,int> > TmpKick[4][4];
	vector<Action> FinalSearch(AccessMap *Map, AccessMap *Tmp, int d, int x, int y, int gd, int gx, int gy, bool TryMini=false, bool HalfSame=false, bool Clutch=false) {
		int LX=Map->LX, LY=Map->LY;
		if ((BotType!=1)&&(BotType!=3)&&(BotType!=8)) TryMini=false;
		if (!Map->Query(d,x,y)) {
			if (!Clutch) return {A_HardDrop};
			bool f = false;
			for (int i=1; i<=LX-SX+6; ++i) {
				++x;
				if (Map->Query(d,x,y)) {
					f=true; break;
				}
			}
			if (!f) return {A_HardDrop};
		}
		Tmp->B[d][x][y] = false;
		queue<PosTuple> qu;
		queue<vector<Action> > ql;
		qu.push((PosTuple){d,x,y}); ql.push(vector<Action>());
		vector<Action> TmpRes = {A_HardDrop};
		while (!qu.empty()) {
			PosTuple o=qu.front(); qu.pop();
			vector<Action> lst=ql.front(); ql.pop();
			d=o.d; x=o.x; y=o.y;
			if ((d==gd)&&(x==gx)&&(y==gy)) {
				if (!TryMini) {
					lst.push_back(A_HardDrop); return lst;
				}
				TmpRes=lst; TmpRes.push_back(A_HardDrop);
			}
			if ((SideGravity)&&(DropDelay)) {
				if (Map->Query(d,x,y-1)) {
					if (Tmp->Query(d,x,y-1)) {
						lst.push_back(A_Left);
						Tmp->B[d][x][y-1]=false; qu.push((PosTuple){d,x,y-1});
						ql.push(lst); lst.pop_back();
					}
					continue;
				}
			}
			if (Tmp->Query(d,x-1,y)) {
				lst.push_back(A_SoftDrop);
				Tmp->B[d][x-1][y]=false; qu.push((PosTuple){d,x-1,y});
				ql.push(lst); lst.pop_back();
			}
			if ((!DropDelay)&&(Map->Query(d,x-1,y))) continue;
			for (int nd=0; nd<4; ++nd) {
				if (nd==d) continue;
				bool f = true;
				int dd = ((nd-d+4)&3);
				if (dd==2) f=UseHalf;
				else f=((Use2Rot)||((UseRotate)&&(dd==DefaultRot)));
				if (!f) continue;
				int nx=-1, ny=-1;
				for (pair<int,int> o : TmpKick[d][nd]) {
					if (Map->Query(nd,x+o.first,y+o.second)) {
						nx=x+o.first; ny=y+o.second; break;
					}
				}
				if (nx>=0) {
					Action Act = A_Half;
					if (((d+1)&3)==nd) Act=A_CW;
					if (((d+3)&3)==nd) Act=A_CCW;
					if ((TryMini)&&(Act!=A_Half)&&(Map->Query(nd,nx,ny))) {
						if (((nd==gd)||((HalfSame)&&(nd==(gd^2))))&&(nx==gx)&&(ny==gy)) {
							lst.push_back(Act); lst.push_back(A_HardDrop); return lst;
						}
					}
					if (Tmp->Query(nd,nx,ny)) {
						lst.push_back(Act);
						Tmp->B[nd][nx][ny]=false; qu.push((PosTuple){nd,nx,ny});
						ql.push(lst); lst.pop_back();
					}
				}
			}
			if ((UseMove)&&(Tmp->Query(d,x,y+1))) {
				lst.push_back(A_Right);
				Tmp->B[d][x][y+1]=false; qu.push((PosTuple){d,x,y+1});
				ql.push(lst); lst.pop_back();
			}
			if ((UseMove)&&(Tmp->Query(d,x,y-1))) {
				lst.push_back(A_Left);
				Tmp->B[d][x][y-1]=false; qu.push((PosTuple){d,x,y-1});
				ql.push(lst); lst.pop_back();
			}
			/*int gg = 1;
			while (Map->Query(d,x-gg,y)) ++gg;
			--gg;
			if ((gg)&&(Tmp->Query(d,x-gg,y))) {
				lst.push_back(A_SonicDrop);
				Tmp->B[d][x-1][y]=false; qu.push((PosTuple){d,x-gg,y});
				ql.push(lst); lst.pop_back();
			}*/
		}
		return TmpRes;
	}
	//#warning Board::SearchNext
	vector<Status*> CurLayer, Recycle;
	int SearchDepth=1, Keep=1;
	bool IgnoreAfterHold=true, GetT;
	AccessMap *Map, *Tmp; Status *Current;
	Instruction FinalInst, FinalStepInst;
	int BeginInc;
	vector<int> StaticLines; int SolidHeight=0;
	int GetSearchSeg() {
		if ((!BotType)||(BotType==8)||(BotType==9)) return 1;
		if ((BotType==1)||(BotType==2)||(BotType==6)) return 9;
		if ((BotType==3)||(BotType==4)||(BotType==7)) return 14;
		if (BotType==5) return 12;
		return 1;
	}
	bool _f = false;
	int SameInstLim = 0;
	bool SearchBegin() {
		BotType = ActType;
		TgD=-1; TgX=TgY=0;
		if (ActType==5) {
			BotType = 0;
			if (AliveCount<=5) BotType=2;
		}
		if (PlayerCount>1) {
			if (ActType==8) {
				if (AliveCount>=30) BotType=9;
				if (AliveCount<=5) BotType=1;
			}
			if (ActType==9) {
				if (AliveCount<=5) BotType=1;
			}
		}
		StaticLines.clear(); SolidHeight=0;
		for (int i=1; i<=LX; ++i) {
			bool f = true;
			for (int j=1; j<=LY; ++j) {
				if (!B[i][j]) {
					f=false; break;
				}
			}
			for (int j=1; j<=LY; ++j) {
				if ((B[i][j])&&(IsUnclearable(B[i][j]))) {
					if (f) ++SolidHeight;
					StaticLines.push_back(i); break;
				}
			}
		}
		BeginInc=HoldPosInc; HoldPosInc=0;
		bool HaveT=GetT=false;
		if (((BotType==1)||(BotType==3))&&(!Mod8)&&(Mod2<2)&&((SpinRule==SR_Guildline)||(SpinRule==SR_AllMini))) GetT=true;
		if (!DropDelay) HaveT=false;
		for (Piece o : Pool) {
			if (IsSpinBonus(o.GetName())) HaveT=true;
		}
		if (!HaveT) GetT=false;
		if ((Mod9==2&&0)&&(TQPScore<300000)) _f=true;
		else _f=false;
		if (QPLevTime>=30000LL&&TQPScore>=150000) _f=false;
		if (QPLevTime>=45000LL&&TQPScore>=50000) _f=false;
		if (_f) GetT=false;
		if (Gen.Pool.size()>=20) GetT=false;
		SearchDepth=1, Keep=1;
		IgnoreAfterHold = true;
		CurLayer.clear(); SameInstLim=0;
		if (!BotType) {
			SearchDepth=1; Keep=1;
		}
		else if (BotType==1) {
			SearchDepth=7; Keep=((HoldType)&&(HoldLen))?15:20;
			if (ModP) Keep=20;
			if ((BoardID<2)&&(SpinRule==SR_Guildline)) Keep=20;
			//if ((ModP)&&(!BoardID)) Keep=100;
			IgnoreAfterHold = false;
		}
		else if (BotType==2) {
			SearchDepth=7; Keep=((HoldType)&&(HoldLen))?15:20;
			if (ModP) Keep=20;
			if ((PlayerCount<2)&&(StaticGarbHeight)) Keep=50;
			IgnoreAfterHold = false;
		}
		else if (BotType==3) {
			SearchDepth=12; Keep=30;
			if (ModP==2) {
				SearchDepth=12; Keep=50;
			}
			IgnoreAfterHold = false;
		}
		else if (BotType==4) {
			SearchDepth=12; Keep=30;
			if (ModP==2) {
				SearchDepth=12; Keep=50;
			}
			IgnoreAfterHold = false;
		}
		else if (BotType==5) {
			SearchDepth=10; Keep=200;
			IgnoreAfterHold = false;
		}
		else if (BotType==6) {
			SearchDepth=7; Keep=20;
			if (ModP==2) {
				SearchDepth=12; Keep=20;
			}
			IgnoreAfterHold = false;
		}
		else if (BotType==7) {
			SearchDepth=12; Keep=50;
			IgnoreAfterHold = false;
		}
		else if (BotType==8) {
			SearchDepth=3; Keep=10;
			IgnoreAfterHold = false;
		}
		else if (BotType==9) {
			SearchDepth=1; Keep=1;
			if ((!HoldLen)||(!HoldType)) {
				SearchDepth=2; Keep=4;
			}
			BotType = 6;
		}
		if ((Mod2==2)&&(SearchDepth>2)) {
			Keep=max(Keep,min(200,SearchDepth*Keep)); SearchDepth=2;
		}
		if ((Mod2==2)&&(SearchDepth>3)) {
			SearchDepth=2; Keep=max(Keep,100);
		}
		int Height=0, Bub=0, BubH=0, HoleHeight=0;
		bool bb = ((BotType==6)||(BotType==7));
		for (int i=LX; i; --i) {
			int ls=0, lu=0;
			for (int j=1; j<=LY; ++j) {
				if (CheckGrid(i,j)) Height=max(Height,i);
				bool dn = CheckGrid(i-1,j);
				if ((!CheckGrid(i+1,j))||(i==LX)) lu=j;
				if ((!CheckGrid(i,j))&&
				((CheckGrid(i,j-1))||((dn)&&(bb)&&(j==2)))&&
				((CheckGrid(i,j+1))||((dn)&&(bb)&&(j==LY-1)))&&
				(CheckGrid(i+1,j))&&((CheckGrid(i-1,j))||((PlayerCount>1)&&(Height+10>SX)))) {
					++Bub; BubH=max(BubH,i);
				}
				else if ((!CheckGrid(i,j))&&(CheckGrid(i,j+1))&&(ls>=lu)) {
					++Bub; BubH=max(BubH,i);
				}
				if (CheckGrid(i,j)) ls=j;
				if ((!CheckGrid(i,j))&&(CheckGrid(i+1,j))) HoleHeight=max(i,HoleHeight);
			}
		}
		PreHeight = Height;
		int TotHeight = Height+min((((BotType==1)||(BotType==3))&&(QPMode)&&((WindUpCD)||(TQPScore<=1300000LL)))?5:((Mod8==2)?10:999),int(AttackIn.size()));
		if ((TotHeight<=6)&&(Bub<2)&&((Mod8<2)||(TDig>=10))&&((Combo<3)||(B2B>=3))) SpinMode=true;
		//if ((!ModM)&&(Gen.Pool.size()<15)&&(PlayerCount>1)&&(TotHeight+10<SX)&&(Bub<2)&&((!Combo)||(B2B>=5))) SpinMode=true;
		if (TotHeight+(((PlayerCount<2)||(Gen.Pool.size()<18)||((B2B>=10)&&(TQPScore<2000000LL)))?5:10)>SX) SpinMode=false;
		if ((Height<=SX-5)&&(CurZone)) SpinMode=true;
		if ((BotType==6)||(BotType==7)) {
			if (Mod2==2) {
				if (TotHeight+9>SX) SpinMode=false;
			}
			int mn=LX+1, p=0;
			for (int i=1; i<=LY; ++i) {
				if ((i>=2)&&(i<=LY-1)) continue;
				int cur = 0;
				for (int j=LX; j; --j) {
					if (CheckGrid(j,i)) {
						cur=j; break;
					}
				}
				if (cur<mn) {
					mn=cur; p=i;
				}
			}
			for (int i=1; i<=LY; ++i) {
				if ((i==1)||(i==LY)) continue;
				int cur = 0;
				for (int j=LX; j; --j) {
					if (CheckGrid(j,i)) {
						cur=j; break;
					}
				}
				if (cur<mn) {
					mn=cur; p=i;
				}
			}
			if ((((p!=1)&&(p!=LY)))||(BubH>mn)) BotType=2;
			else if (!SpinMode) BotType=2;
			else {
				int d=(p==1)?1:-1, acc=0;
				for (int i=mn+1; i<=LX; ++i) {
					int j=p, cnt=0;
					for (int jj=1; jj<LY; ++jj) {
						j += d;
						if (CheckGrid(i,j)) {
							if (jj>1) break;
						}
						else ++cnt;
					}
					if (cnt>=LY-1) break;
					if (cnt) acc+=cnt;
					else if ((acc)&&(acc<3)) {
						if ((mn)||(PlayerCount>1)) BotType=2;
						BotType=2; break;
					}
					else acc=0;
				}
			}
		}
		while (Hold.size()>HoldLen) Hold.pop_back();
		FillNext(SearchDepth+HoldLen+1);
		if (!CheckActive()) return false;
		Current = new Status(LX, LY);
		Current->Cur=Cur; Current->Hold=Hold; Current->Next=Next;
		Current->LineAfterPC = LineAfterPC;
		Current->LastClearType = LastClearType;
		Current->Combo = Combo;
		Current->Clutch = CurClutch;
		/*if ((GetT)&&(SpinMode)) {
			bool f = (Current->Cur.GetName()=="T");
			for (Piece &o : Current->Hold) f|=(o.GetName()=="T");
			for (int i=0; i<SearchDepth-1; ++i) {
				if (f) break;
				if (Current->Next[i].GetName()=="T") f=true;
			}
			if (!f) Current->Next[max(0,SearchDepth-1)]=P_T;
		}*/
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) Current->B[i][j]=((B[i][j])&&(!C[i][j]));
		}
		CurLayer = {Current};
		Map = new AccessMap(LX,LY);
		Tmp = new AccessMap(LX,LY);
		FinalInst = (Instruction){-1,0,0,0,false};
		FinalStepInst = (Instruction){-1,0,0,0,false};
		return true;
	}
	bool SearchLayer(int i) {
		vector<pair<Status*,ll> > NextLayer;
		vector<llu> NextHash;
		int LayerKeep = Keep;
		//if ((BotType==1)&&(i>1)) Keep=10;
		bool Final=(i+1==SearchDepth); ll FinalEval=-4000000000000000000LL;
		for (Status *Sta : CurLayer) {
			vector<pair<int,Piece> > PieceOpt = {make_pair(0,Sta->Cur)};
			vector<llu> PieceHash = {Sta->Cur.GetHash()};
			int Holds = 0;
			if (HoldType==1) Holds=min(HoldLen,HoldRem);
			if (HoldType==2) Holds=HoldLen;
			int hp = (BotType==5)?3:1;
			if ((i>=hp)&&(IgnoreAfterHold)) Holds=0;
			int id = 0;
			int StaHeight = 0;
			for (int ii=1; ii<=LX; ++ii) {
				for (int jj=1; jj<=LY; ++jj) {
					if (Sta->B[ii][jj]) StaHeight=max(StaHeight,ii);
				}
			}
			for (int j=0; j<int(HoldLen-Sta->Hold.size()); ++j) {
				if (++id>Holds) break;
				llu hsh=Sta->Next[j].GetHash(); bool f=true;
				for (llu k : PieceHash) {
					if (hsh==k) {
						f=false; break;
					}
				}
				if (f) {
					PieceOpt.push_back(make_pair(id,Sta->Next[j])); PieceHash.push_back(hsh);
				}
			}
			for (Piece &p : Sta->Hold) {
				if (++id>Holds) break;
				llu hsh=p.GetHash(); bool f=true;
				for (llu k : PieceHash) {
					if (hsh==k) {
						f=false; break;
					}
				}
				if (f) {
					PieceOpt.push_back(make_pair(id,p)); PieceHash.push_back(hsh);
				}
			}
			for (pair<int,Piece> &o : PieceOpt) {
				vector<Piece> NewNext=Sta->Next, NewHold=Sta->Hold; Piece NewCur=Sta->Cur;
				for (int j=0; j<o.first; ++j) {
					if (NewHold.size()==HoldLen) {
						Piece tmp = NewHold[0];
						for (int i=0; i+1<HoldLen; ++i) NewHold[i]=NewHold[i+1];
						NewHold[HoldLen-1] = NewCur;
						NewCur = tmp;
					}
					else {
						NewHold.push_back(NewCur);
						NewCur = NewNext[0];
						for (int k=0; k+1<NewNext.size(); ++k) NewNext[k]=NewNext[k+1];
						NewNext.pop_back();
					}
				}
				NewCur=NewNext[0]; for (int j=0; j+1<NewNext.size(); ++j) NewNext[j]=NewNext[j+1];
				NewNext.pop_back();
				GetAccessMap(Map, Sta, o.second);
				Piece p = o.second;
				vector<pair<PosTuple,bool> > Locations = GetLocations(Map, Tmp, p, StaHeight, ((i)||(o.first))?0:BeginInc,
				(IsSpinBonus(p.GetName()))||(((BotType==1)||(BotType==3))&&(SpinRule==SR_Default)), Sta->Clutch);
				if (Locations.empty()) break;
				for (pair<PosTuple,bool> pr : Locations) {
					PosTuple oo = pr.first;
					Status *NewSta = nullptr;
					if (Recycle.empty()) NewSta=new Status(*Sta);
					else {
						NewSta=Recycle.back(); Recycle.pop_back();
						*NewSta = *Sta;
					}
					int PLX = p.GetLX();
					int Unmoveable = 0;
					for (int ii=0; ii<PLX; ++ii) {
						for (int jj=0; jj<PLX; ++jj) {
							if (!p.GetGrid(ii,jj,oo.d)) continue;
							int x=oo.x+ii, y=oo.y+jj;
							if (NewSta->CheckGrid(x+1,y)) Unmoveable|=1;
							if (NewSta->CheckGrid(x-1,y)) Unmoveable|=2;
							if (NewSta->CheckGrid(x,y+1)) Unmoveable|=4;
							if (NewSta->CheckGrid(x,y-1)) Unmoveable|=8;
						}
					}
					if (!i) NewSta->Inst=(Instruction){o.first,oo.d,oo.x,oo.y,((pr.second)||(Unmoveable==15))};
					for (int ii=0; ii<PLX; ++ii) {
						for (int jj=0; jj<PLX; ++jj) {
							if (p.GetGrid(ii,jj,oo.d)) NewSta->B[oo.x+ii][oo.y+jj]=true;
						}
					}
					NewSta->Next=NewNext; NewSta->Hold=NewHold; NewSta->Cur=NewCur;
					int pp=-1; ll TmpEval=Eval(NewSta,p,oo,Unmoveable==15,oo.d,oo.x,oo.y,pr.second);
					ll hsh = NewSta->GetHash((HoldType)?HoldLen:0);
					ll NewEval = TmpEval+NewSta->AccEval;
					if (Final) {
						if (NewEval>FinalEval) {
							FinalEval=NewEval; FinalStepInst=NewSta->Inst;
						}
						Recycle.push_back(NewSta); continue;
					}
					int sc=0, sp=-1;
					for (int i=0; i<NextHash.size(); ++i) {
						if (hsh==NextHash[i]) pp=i;
						/*if (NextLayer[i].first->Inst==NewSta->Inst) {
							++sc;
							if (sp<0) sp=i;
							else if (NextLayer[sp].second>NextLayer[i].second) sp=i;
						}*/
					}
					if (pp>=0) {
						if (NewEval>NextLayer[pp].second) {
							NextLayer[pp].second = NewEval;
							Recycle.push_back(NextLayer[pp].first);
							NextLayer[pp].first = NewSta;
						}
						else Recycle.push_back(NewSta);
					}
					/*else if ((sp>=0)&&(SameInstLim>0)&&(sc>=SameInstLim)) {
						if (NewEval>NextLayer[sp].second) {
							Recycle.push_back(NextLayer[sp].first);
							NextLayer[sp].first=NewSta; NextLayer[sp].second=NewEval;
							NextHash[sp] = hsh;
						}
						else Recycle.push_back(NewSta);
					}*/
					else {
						if (NextLayer.size()<LayerKeep) {
							NextLayer.push_back(make_pair(NewSta, NewEval));
							NextHash.push_back(hsh);
						}
						else {
							int p = 0;
							for (int i=1; i<NextLayer.size(); ++i) {
								if (NextLayer[i].second<NextLayer[p].second) p=i;
							}
							if (NewEval>NextLayer[p].second) {
								Recycle.push_back(NextLayer[p].first);
								NextLayer[p].first=NewSta; NextLayer[p].second=NewEval;
								NextHash[p] = hsh;
							}
							else Recycle.push_back(NewSta);
						}
					}
				}
			}
			if (i) Recycle.push_back(Sta);
		}
		CurLayer.clear();
		if (Final) {
			if (FinalStepInst.ord>=0) FinalInst=FinalStepInst;
			return true;
		}
		bool AllSame = false;
		if (NextLayer.size()) {
			int pos = 0;
			for (int i=1; i<NextLayer.size(); ++i) {
				if (NextLayer[i].second>NextLayer[pos].second) pos=i;
			}
			FinalInst = NextLayer[pos].first->Inst;
			AllSame = true;
			for (pair<Status*,ll> o : NextLayer) {
				if (o.first->Inst!=FinalInst) {
					AllSame=false; break;
				}
			}
		}
		for (pair<Status*,ll> o : NextLayer) CurLayer.push_back(o.first);
		NextLayer.clear();
		if (AllSame) return true;
		return false;
	}
	vector<Action> SearchEnd() {
		for (Status *p : CurLayer) delete p;
		CurLayer.clear();
		Piece p=P_Empty; int k=FinalInst.ord;
		if (k<0) {
			delete Map; delete Tmp;
			delete Current; return {A_HardDrop};
		}
		if (!k) p=Cur;
		else if (k<=HoldLen-Hold.size()) p=Next[k-1];
		else p=Hold[k-HoldLen+Hold.size()-1];
		GetAccessMap(Map, Current, p);
		pair<int,int> Place=SpawnPlace(p), Anch[4];
		Piece NewP = p;
		for (int i=0; i<4; ++i) {
			NewP.SetDir(i); Anch[i]=NewP.GetAnchor();
		}
		int d=DefaultDir, x=Place.first+Anch[d].first, y=Place.second+Anch[d].second;
		for (int i=0; i<4; ++i) {
			for (int j=0; j<4; ++j) {
				TmpKick[i][j] = GetKickTable(i,j,p.GetRealName(),RotateSystem);
				for (pair<int,int> &o : TmpKick[i][j]) {
					swap(o.first, o.second);
					o.first += Anch[j].first-Anch[i].first;
					o.second += Anch[j].second-Anch[i].second;
				}
			}
		}
		for (int d=0; d<4; ++d) {
			for (int i=1; i<=LX; ++i) {
				for (int j=1; j<=LY; ++j) Tmp->B[d][i][j]=Map->B[d][i][j];
			}
		}
		int od=FinalInst.d, ox=FinalInst.x+Anch[od].first, oy=FinalInst.y+Anch[od].second;
		Tg=NewP; TgD=FinalInst.d; TgX+=FinalInst.x; TgY=FinalInst.y;
		vector<Action> SearchRes = FinalSearch(Map,Tmp,d,x+((k)?0:BeginInc),y,od,ox,oy,FinalInst.sp,IsHalfSame(p.GetRealName()),((CurClutch)||(PreInc)||(HoldPosInc+BeginInc)));
		vector<Action> res(k, A_Hold);
		if (SearchRes.empty()) res.push_back(A_HardDrop);
		else {
			for (Action o : SearchRes) res.push_back(o);
		}
		delete Map; delete Tmp;
		delete Current;
		if (BoardID) {
			for (Status *o : Recycle) delete o;
			Recycle.clear();
		}
		return res;
	}
	int SearchStep = -1;
	vector<Action> SearchNext(bool ByStep=false) {
		TgD = -1; 
		if ((ByStep)||(SearchStep!=-1)) {
			if (SearchStep==-1) {
				if (SearchBegin()) SearchStep=0;
			}
			else if (SearchStep==-2) {
				SearchStep=-1; return SearchEnd();
			}
			else {
				bool f = SearchLayer(SearchStep);
				if ((++SearchStep>=SearchDepth)||(f)) SearchStep=-2;
			}
			return vector<Action>();
		}
		if (!SearchBegin()) return vector<Action>();
		for (int i=0; i<SearchDepth; ++i) {
			if (SearchLayer(i)) break;
		}
		SearchStep = -1;
		return SearchEnd();
	}
	int B2BColor(int x) {
		if (B2BRule==BR_Techmino) {
			if (B2B<50) return 0x888888;
			if (B2B<=800) return 0xFF6666;
			return 0x9999FF;
		}
		if (x<1) return (B2BRule==BR_Chain)?0xAA0000:0x888888;
		if (x<4) return 0xFFE211;
		if ((B2BRule!=BR_Surge)&&(B2BRule!=BR_QP)) return 0xFFE211;
		vector<pair<int,int> > lst={{4,0x00FFCC},{13,0xFFFF00},{31,0xFF0000},{45,0xDF3782},{70,0xAF2FFF},{90,0x0080FF},
		{130,0x66CCFF},{150,0x99FFFF},{220,0xE5E59C}};
		int rr = -1;
		for (int i=1; i<lst.size(); ++i) {
			if ((lst[i-1].first<=x)&&(x<=lst[i].first)) rr=Mix(lst[i].second,lst[i-1].second,double(x-lst[i-1].first)/double(lst[i].first-lst[i-1].first));
		}
		if ((rr<0)&&(x<=lst[0].first)) rr=lst[0].second;
		else if (rr<0) rr=lst.back().second;
		if (Mod8==2) rr=Mix(0xFFFFFF,0xFFFFFF^rr,0.4);
		return rr;
	}
	int GetHeight() {
		for (int i=LX; i; --i) {
			for (int j=1; j<=LY; ++j) {
				if (CheckGrid(i,j)) return i;
			}
		}
		return 0;
	}
	int GetPaintColor() {
		if (PaintType==1) return P_Z.GetColor();
		if (PaintType==2) return P_L.GetColor();
		if (PaintType==3) return P_O.GetColor();
		if (PaintType==4) return P_S.GetColor();
		if (PaintType==5) return P_I.GetColor();
		if (PaintType==6) return P_J.GetColor();
		if (PaintType==7) return P_T.GetColor();
		if (PaintType==8) return Garb;
		return 0x39C5BB;
	}
	int ModPos = 0;
	void PrintQPMod(int val, int clr, string s, string ss) {
		if (!val) return;
		double x=double(++ModPos)*1.5, y=LY+5;
		if (val==1) Poly(x,y,0.6,4,clr,acos(-1.0)*0.25);
		else {
			Poly(x, y, 1, 4, 0xFF6666, 0);
			Poly(x, y, 1, 4, 0xFF6666, acos(-1.0)*0.25);
		}
		double dd = (val==1)?1.0:1.5;
		PrintStrM((val==1)?s:ss, x-0.25*dd, y-0.2*dd, 0.8*dd, (val==1)?Mix(0x000000,clr,0.5):0xFFFFFF, 0.2);
	}
	int GetLevelColor(int x) {
		return LevelColor[max(0,min(int(LevelColor.size())-1,x))];
	}
	void PaintZenith() {
		double len = min(8,LY)*1.5;
		int MaxLevel = int(QPLevelScore.size());
		int CurLevel = max(1,min(MaxLevel,Level));
		int pp = 0;
		int SurgeCount = 0;
		if (B2B>4) {
			if (B2BRule==BR_Surge) SurgeCount=B2B-1;
			if (B2BRule==BR_QP) SurgeCount=B2B-4;
		}
		int OriSurgeCount = SurgeCount;
		SurgeCount = max(0,SurgeCount-int(AttackIn.size()));
		ll SurgeScore = DQPScore+ll(SurgeCount)*250LL*ll(QPRank+1);
		if (SurgeCount<1) SurgeScore=-1LL;
		int BegLevel = max(9,CurLevel);
		for (int i=0; i<QPLevelScore.size(); ++i) {
			if (SurgeScore>QPLevelScore[i]*1000LL) BegLevel=max(BegLevel,i+1);
		}
		int SurgeRank = QPRank;
		if (OriSurgeCount>0) {
			ll _Exp = QPExp;
			if (SurgeCount) _Exp+=500+10000*min(SurgeCount,10000);
			if (SurgeCount<OriSurgeCount) _Exp+=500+10000*(((Mod5==2)||(Mod9==2)||(ModD==2))?0:OriSurgeCount-SurgeCount);
			_Exp = max(_Exp,0LL);
			for (;;) {
				ll req = max(40000LL,40000LL*(SurgeRank+1));
				if ((SurgeRank>100)||(_Exp<req)) break;
				SurgeRank+=_Exp/req; _Exp-=req;
			}
		}
		for (int i=14; i>=0; --i) {
			int d=BegLevel-i; if (d<1) continue;
			ll Lower=QPLevelScore[d-1]*1000LL, Upper=(d==MaxLevel)?max(Lower+1000LL,DQPScore):QPLevelScore[d]*1000LL;
			if (d>Level) {
				PrintStrL("Stage "+IToS(d), i*2, LY+6, 0.7, 0x888888);
				Rect(i*2+1, LY+6, 0.3, len, 0x444444);
			}
			else {
				PrintStrL("Stage "+IToS(d), i*2, LY+6, 0.7, 0xCCCCCC);
				Rect(i*2+1, LY+6, 0.3, len, 0x888888);
				ll t=Time; if (QPSegTime.size()>d-1) t=QPSegTime[d-1];
				PrintStrR(IToS(t/1000LL/60LL)+":"+IToS(t/1000LL%60LL/10LL)+IToS(t/1000LL%60LL%10LL)+"."+IToS(t%1000LL/100LL)+"  ", i*2, LY+6+len, 0.7, (d==CurLevel)?0xFDD000:0xDDDDDD);
				for (int j=pp; j<QPSeg.size(); ++j) {
					pair<int,pair<ll,ll> > o = QPSeg[j];
					ll l=max(Lower,o.second.first), r=min(min(Upper,DQPScore),o.second.second);
					if (o.second.second<=Upper) pp=j+1;
					if (l>Upper) break;
					if (l>=r) continue;
					Rect(i*2+1, LY+6+len*double(l-Lower)/double(Upper-Lower), 0.3, len*double(r-l)/double(Upper-Lower), Mix(0x000000,abs(o.first),0.3));
					if (o.first<0)
						Rect(i*2+1+0.15, LY+6+len*double(l-Lower)/double(Upper-Lower), 0.15, len*double(r-l)/double(Upper-Lower), abs(o.first));
				}
			}
			if ((SurgeScore>=Lower)&&(SurgeScore<=Upper)) {
				Rect(i*2+1-0.1,LY+6+len*double(SurgeScore-Lower)/double(Upper-Lower)-0.05,0.1,0.1,GetLevelColor(SurgeRank));
				Rect(i*2+1,LY+6+len*double(SurgeScore-Lower)/double(Upper-Lower)-0.05,0.3,0.1,0xDDDDDD);
			}
			if ((DQPScore>=Lower)&&(DQPScore<=Upper)) 
				Rect(i*2+1-0.1,LY+6+len*double(DQPScore-Lower)/double(Upper-Lower)-0.05,0.4,0.1,0xFDD000);
		}
	}
	char KeyPause = 'O';
	string GetKeyChar(Action x) {
		int res = 0;
		for (Monitor &o : Keys) {
			if (o.Result==x) res=o.Key;
		}
		return KeyChar(res);
	}
	bool AvailBotAction(Action o) {
		return ((o==A_Retry)&&((!QPMode)||(PlayerCount<2)||(Over)));
	}
	bool PreActive = false;
	vector<Action> Actions;
	vector<ConfigContent> BoardConfig;
	int GetConfig(string Name, int Default) {
		for (ConfigContent o : BoardConfig) {
			if (ToLower(o.first)==ToLower(Name)) {
				int tmp = SToI(o.second);
				if (tmp!=SToIErr) return tmp;
			}
		}
		return Default;
	}
	string GetConfig(string Name, string Default, vector<string> List) {
		for (ConfigContent o : BoardConfig) {
			if (ToLower(o.first)==ToLower(Name)) {
				for (string x : List) {
					if (ToLower(x)==ToLower(o.second)) return ToLower(x);
				}
			}
		}
		return ToLower(Default);
	}
	bool UseLeveling() {
		return ((QPMode)||(LevTime)||(LevAttack)||(LevLine)||(LevPiece)||(LevStart)||(LevBase));
	}
	int GetLevel() {
		if (QPMode) {
			if ((Time>=0LL)&&(QPLevel<QPLevelScore.size())&&(TQPScore>=1000LL*QPLevelScore[QPLevel])) ++QPLevel;
			return QPLevel;
		}
		int res = LevBase;
		if (LevTime) {
			ll tmp=Time, req=LevTime;
			while (res<1000) {
				if (tmp>=req) {
					tmp-=req; ++res; req+=LevTimeInc;
				}
				else break;
			}
		}
		if (LevLine) {
			int tmp=TLine, req=LevLine;
			while (res<1000) {
				if (tmp>=req) {
					tmp-=req; ++res; req+=LevLineInc;
				}
				else break;
			}
		}
		if (LevPiece) {
			int tmp=TPiece, req=LevPiece;
			while (res<1000) {
				if (tmp>=req) {
					tmp-=req; ++res; req+=LevPieceInc;
				}
				else break;
			}
		}
		if (LevAttack) {
			int tmp=TAttack, req=LevAttack;
			while (res<1000) {
				if (tmp>=req) {
					tmp-=req; ++res; req+=LevAttackInc;
				}
				else break;
			}
		}
		return min(max(res,LevStart),min(1000,LevMax));
	}
	double GetLevelMultipler(int Level, double k) {
		double mul = 1.0;
		if (LevScaleMode==LS_Default) mul=pow(max(0.0,0.8-k*double(Level)),Level)/0.8;
		if (LevScaleMode==LS_Div) mul=1.0/(1.0+k*double(Level));
		if (LevScaleMode==LS_Dec) mul=max(0.0,1.0-k*double(Level));
		if (LevScaleMode==LS_Exp) mul=pow(max(0.0,1.0-k),double(Level));
		return max(0.0,min(1.0,mul));
	}
	void UpdateLevel() {
		if (!UseLeveling()) {
			DropDelay=BaseDropDelay; LockDelay=BaseLockDelay;
			ARE=BaseARE; LCD=BaseLCD; CCD=BaseCCD; GravityDelay=BaseGravityDelay; return;
		}
		int _Level = GetLevel();
		if (Level==_Level) return;
		Level = _Level;
		if ((Level)||(!QPMode))
			AddFloatText(IToS(Level), 0xAAAAAA, 1.8, 0, 0.12, 0.06,
			SX*0.25, LY*0.5-0.4, -1, 0, 0, 0);
		if (QPMode) {
			if (Level>1) QPSegTime.push_back(Time);
			if ((Mod9==2)&&(QPLevTime<60000LL)&&(Level>1)) AddNotice("COOL!",0x66CCFF);
			QPLevTime = 0LL;
		}
		if (ModM==1) {
			MutatePool.clear(); for (int i=2; i<9; ++i) MutatePool.push_back(i);
		}
		double mul = GetLevelMultipler(Level,0.001*double(LevScaleRate));
		DropDelay = (BaseDropDelay==Max)?Max:BaseDropDelay*mul;
		if (Mod4==1) DropDelay/=30;
		ARE = BaseARE*mul;
		LCD = BaseLCD*mul;
		CCD = BaseCCD; //CCD = BaseCCD*mul;
		GravityDelay = BaseGravityDelay*mul;
		LockDelay = BaseLockDelay;
		if (Mod3==2) LCD=BaseLCD;
		if (Mod4) {
			vector<int> lst = {30,30,29,28,27,26,24,22,20,18,16};
			if (Mod4==2) {
				lst={24,24,22,20,18,16,15,14,13,12,11}; DropDelay=0;
			}
			int k = min(int(lst.size())-1,Level);
			LockDelay = BaseLockDelay*lst[k]/30;
		}
		else if ((MasterBegin>=0)&&(Level>=MasterBegin)&&(!QPMode)) {
			DropDelay = 0;
			vector<int> lst = {300,295,276,252,228,204,175,168,156,150,132,115,114,108,102,96,85,84,78,72,66,55,50,45,40,37,33,30,28,26,24,22,20,18,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
			int k = min(int(lst.size())-1,Level-MasterBegin);
			LockDelay = BaseLockDelay*lst[k]/300;
		}
		if (QPMode) {
			if (Mod9) GarbageDelay=max(400,2400-Level*200);
			else {
				GarbageDelay = max(500,5500-Level*500);
				if (QPRevmode()) GarbageDelay=min(2500,GarbageDelay);
				if ((Mod3==2)||(Mod6==2)||(Mod8==2)) GarbageDelay=2500;
			}
		}
	}
	int GetGarbHeight() {
		for (int i=LX; i; --i) {
			for (int j=1; j<=LY; ++j) {
				if ((!C[i][j])&&(B[i][j]<0)) return i;
			}
		}
		if (GarbageShape==GS_Empty) {
			bool ff = false;
			for (int i=LX; i; --i) {
				bool f = true;
				for (int j=1; j<=LY; ++j) {
					if ((C[i][j])&&(B[i][j])) f=false;
				}
				if (!f) ff=true;
				else if (ff) return i;
			}
		}
		return 0;
	}
	int GetZoneBonus(int Lines) {
		if (!CurZone) return 0;
		return (Lines*(4+max(1,min(4,CurZone))*3)+3)/10;
	}
	bool IsZoneClear = false;
	bool ClearZone() {
		bool f = false;
		int Lines = 0;
		for (int i=1; i<=LX; ++i) {
			if (B[i][1]==GarbZone) ++Lines;
			for (int j=1; j<=LY; ++j) {
				if (B[i][j]==GarbZone) {
					f=true; B[i][j]=-B[i][j]; Chain=0;
				}
			}
		}
		ZoneAttack += GetZoneBonus(Lines);
		if (Lines) IsZoneClear=true;
		return f;
	}
	int ClearRound() {
		int res=0, p=0;
		for (int i=1; i<=LX; ++i) {
			bool f = false;
			for (int j=1; j<=LY; ++j) {
				if (B[i][j]==GarbRound) f=true;
			}
			if (f) {
				++res;
				if (GravityType==GV_None) {
					++p;
					for (int j=1; j<=LY; ++j) B[p][j]=Age[p][j]=U[p][j]=0;
				}
			}
			else {
				++p;
				if (p!=i) {
					for (int j=1; j<=LY; ++j) {
						B[p][j]=B[i][j]; B[i][j]=0;
						Age[p][j]=Age[i][j]; U[p][j]=U[i][j];
						Age[i][j]=U[i][j]=0;
					}
				}
			}
		}
		return res;
	}
	void TopOut() {
		if (CurZone) {
			ClearZone();
			CurZone=0; PreAREProcess(false);
			return;
		}
		if ((Stock<0)||(StockRem>0)) {
			if (Stock>=0) --StockRem;
			ClearBoard(true); return;
		}
		Over=true; KOMessage=LastAttacker;
		ClearSearchProcess();
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (C[i][j]) C[i][j]=false;
				if (!B[i][j]) continue;
				int tmp=abs(B[i][j]), grey=0;
				grey+=tmp&255; tmp>>=8;
				grey+=tmp&255; tmp>>=8;
				grey+=tmp&255; grey=max(0,min(255,(grey+1)/3));
				if (B[i][j]<0) grey=rand()%51+100;
				int clr = (grey<<16)|(grey<<8)|grey;
				if (B[i][j]<0) B[i][j]=-clr;
				else B[i][j]=clr;
				U[i][j] = NewUID();
			}
		}
	}
	int GetCopyPos() {
		for (int i=1; i<=LX; ++i) {
			bool f = true;
			for (int j=1; j<=LY; ++j) {
				if (IsUnclearable(B[i][j])) {
					f=false; break;
				}
			}
			if (f) return i;
		}
		return 0;
	}
	GarbageLayer RollGarbage(int Hole=-1) {
		if (Hole<0) Hole=HolePos;
		++GarbCount; int CopyPos=GetCopyPos();
		GarbageLayer o(LY);
		for (int i=1; i<=LY; ++i) {
			if (GarbageShape==GS_Default) o.B[i]=(i!=Hole);
			if (GarbageShape==GS_Bubble) o.B[i]=(i==Hole);
			if (GarbageShape==GS_Empty) o.B[i]=0;
			if (GarbageShape==GS_Full) o.B[i]=1;
			if (GarbageShape==GS_Board) o.B[i]=(GarbCount+i)&1;
			if (GarbageShape==GS_Random) o.B[i]=((rand()&1)&&(i!=Hole));
			if (GarbageShape==GS_Stair) {
				int p = 1;
				if (LY>1) {
					int val = (GarbCount-1)%((LY-1)<<1);
					if (val<LY-1) p=val+1;
					else p=LY-val+(LY-1);
				}
				o.B[i] = (i!=p);
			}
			if (GarbageShape==GS_Copy) o.B[i]=CheckGrid(CopyPos,i);
		}
		if ((Mod2==2)&&(LY>1)) {
			if (rand()%100<max(10,min(55,Level*5))) QPRevMod2GarbSide^=1;
			int tar = 1;
			if (Hole==1) tar=2;
			else if (Hole==LY) tar=LY-1;
			else tar=Hole+((QPRevMod2GarbSide)?1:-1);
			o.B[tar] = 0;
		}
		if (Mod6==1) {
			if (rand()&1) o.B[rand()%LY+1]=0;
		}
		if (Mod6==2) {
			for (int i=1; i<=LY; ++i) o.B[i]=(rand()%3)?0:1;
			if (LY>=2) {
				int d1=rand()%LY, d2=rand()%(LY-1); if (d2==d1) d2=LY-1;
				o.B[d1+1]=0; o.B[d2+1]=1;
			}
		}
		/*if (!BoardID) {
			for (int i=1; i<=LY; ++i) {
				if ((i<LatestReceive.B.size())&&(!LatestReceive.B[i])) o.B[i]^=1;
			}
		}*/
		for (int i=1; i<=LY; ++i) {
			if (o.B[i]) {
				if (GarbageType==GT_Solid) o.B[i]=Solid;
				else if (GarbageType==GT_Bricks) o.B[i]=RollColor();
				else o.B[i]=Garb;
			}
			else {
				if (GarbageType==GT_Lock) o.B[i]=GarbLock;
				if (GarbageType==GT_Bomb) o.B[i]=GarbBomb;
			}
		}
		//if (!BoardID) LatestReceive=o;
		return o;
	}
	void RerollHole() {
		bool Concentrate = ((QPMode)&&(LY>=5)&&(Mod5==2));
		int Luck = 0;
		if (QPMode) {
			Luck = 33-3*min(Level,10);
			if (Mod9) Luck-=33;
			if (Mod3) Luck-=25;
			if (Mod5==2) Luck=50;
		}
		if (!Luck) {
			if (Concentrate) {
				HolePos=rand()%(LY-4)+3; return;
			}
			HolePos=rand()%LY+1; return;
		}
		vector<pair<int,int> > Candidates;
		for (int i=1; i<=LY; ++i) {
			if ((Concentrate)&&((i<=2)||(i>=LY-1))) continue;
			int w=0; bool f=false;
			for (int j=LX; j; --j) {
				if (CheckGrid(j,i)) f=true;
				if (f) {
					if (CheckGrid(j,i)) ++w;
					else w+=2;
				}
			}
			if (LastAttacker!=-1) w+=5*abs(i-HolePos);
			Candidates.push_back(make_pair(i,w));
		}
		if (Candidates.empty()) {
			if (Concentrate) {
				HolePos=rand()%(LY-4)+3; return;
			}
			HolePos=rand()%LY+1; return;
		}
		shuffle(Candidates.begin(), Candidates.end(), GlobalGen);
		stable_sort(Candidates.begin(), Candidates.end(), [](pair<int,int> x, pair<int,int> y) {
			return x.second<y.second;
		});
		int totw=0, id=0;
		for (pair<int,int> o : Candidates) {
			++id; o.second=max(0,90+(9-id*2)*Luck); totw+=o.second;
		}
		if (!totw) {
			if (Concentrate) {
				HolePos=rand()%(LY-4)+3; return;
			}
			HolePos=rand()%LY+1; return;
		}
		int pp = GlobalGen()%totw;
		for (pair<int,int> o : Candidates) {
			pp -= o.second;
			if (pp<0) {
				HolePos=o.first; return;
			}
		}
		if (Concentrate) {
			HolePos=rand()%(LY-4)+3; return;
		}
		HolePos = rand()%LY+1;
	}
	int GetRiseHeight(int Level=0) {
		for (int i=1; i<=LX; ++i) {
			bool f1=false, f2=true;
			bool p0 = false;
			for (int j=1; j<=LY; ++j) {
				if ((C[i][j])||(!IsUnclearable(B[i][j]))) f2=false;
				if ((!C[i][j])&&(IsUnclearable(B[i][j]))) f1=true;
				if ((!C[i][j])&&((B[i][j]==GarbLock)||(B[i][j]==GarbBomb))) p0=true;
			}
			int cur = 0;
			if (f1) cur=1;
			if (f2) cur=2;
			if (p0) cur=0;
			if (cur<=Level) return i-1;
		}
		return LX;
	}
	void AddGarbage(GarbageLayer o, int RiseLevel=0) {
		int Row = GetRiseHeight(RiseLevel)+1;
		if (Row>LX) return;
		for (int i=LX; i>Row; --i) {
			for (int j=1; j<=LY; ++j) {
				B[i][j]=B[i-1][j]; C[i][j]=C[i-1][j];
				Age[i][j]=Age[i-1][j]; U[i][j]=U[i-1][j];
			}
		}
		int cU = NewUID();
		for (int i=1; i<=LY; ++i) {
			B[Row][i]=o.B[i]; C[Row][i]=false; U[Row][i]=0;
			if (B[Row][i]) {
				Age[Row][i]=500; U[Row][i]=cU;
			}
			else {
				Age[Row][i]=0; cU=NewUID();
			}
		}
		for (int i=LX; i>Row; --i) {
			if (FallDest[i-1]) FallDest[i]=FallDest[i-1]+1;
			else FallDest[i]=0;
		}
		FallDest[Row] = Row;
		if (CheckActive()) {
			++PosX; ++TgX;
			if (Bot) ++HoldPosInc;
			else if ((!GarbagePush)||(!SmoothLevel)) MoveDown();
		}
		else ++PreInc;
	}
	Piece GetFlip(Piece p) {
		int PLX = p.GetLX();
		vector<vector<int> > vec(PLX);
		for (int i=0; i<PLX; ++i) {
			for (int j=0; j<PLX; ++j) vec[PLX-i-1].push_back(p.GetRealGrid(i,PLX-j-1,0));
		}
		Piece *nw = new Piece(PLX,vec,p.GetColor(),p.GetRealName());
		CustomPiece.push_back(nw);
		return *nw;
	}
	Piece GetRotate(Piece p, int del) {
		del&=3; if (del<0) del=(del+4)&3;
		int PLX = p.GetLX();
		vector<vector<int> > vec(PLX);
		for (int i=0; i<PLX; ++i) {
			for (int j=0; j<PLX; ++j) vec[PLX-i-1].push_back(p.GetRealGrid(i,j,del));
		}
		Piece *nw = new Piece(PLX,vec,p.GetColor(),p.GetRealName()+"@"+"URDL"[del]);
		CustomPiece.push_back(nw);
		return *nw;
	}
	int RollColor() {
		for (;;) {
			int R = max(0,min(255,int(randf()*256.0)));
			int G = max(0,min(255,int(randf()*256.0)));
			int B = max(0,min(255,int(randf()*256.0)));
			if ((R+G+B>=400)&&(R+G+B<=600)) return (R<<16)|(G<<8)|B;
		}
		return 0;
	}
	pair<int,int> ReadMutateName(string s) {
		if ((s.empty())||(s[0]!='?')) return make_pair(++MutateID,0);
		int r=0, fst=0;
		for (char ch : s) {
			if (ch=='?') continue;
			if ((ch>='0')&&(ch<='9')) r=r*10+ch-'0';
			if (ch=='-') {
				fst=r; r=0;
			}
		}
		return make_pair(fst,r);
	}
	Piece GetMutate(Piece p) {
		int LX = p.GetLX();
		vector<vector<int> > vec(LX+2,vector<int>(LX+2));
		vector<pair<int,int> > InsPos, DelPos;
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LX; ++j) vec[i][j]=(p.GetGrid(i-1,j-1)!=0);
		}
		int dc = 0;
		for (int i=0; i<=LX+1; ++i) {
			for (int j=0; j<=LX+1; ++j) {
				int f = ((i)&&(vec[i-1][j]))
				+((i<=LX)&&(vec[i+1][j]))
				+((j)&&(vec[i][j-1]))
				+((j<=LX)&&(vec[i][j+1]));
				if ((!vec[i][j])&&(f)) {
					bool dd = ((i)&&(i<=LX))&&((j)&&(j<=LX));
					if ((LX<5)||(dd)) {
						InsPos.push_back(make_pair(i,j));
						if (dd) {
							InsPos.push_back(make_pair(i,j)); InsPos.push_back(make_pair(i,j));
						}
					}
				}
				if (vec[i][j]) {
					++dc; int w=5-f;
					for (int k=0; k<w; ++k) DelPos.push_back(make_pair(i,j));
				}
			}
		}
		if ((InsPos.empty())&&(LX<5)) InsPos.push_back(make_pair(1,1));
		int InsWeight = (InsPos.size())?7:0;
		int DelWeight = (dc>1)?max(1,LX)*2:0;
		if ((!InsWeight)&&(!DelWeight)) return p;
		if (rand()%(InsWeight+DelWeight)<InsWeight) {
			int d = rand()%InsPos.size();
			vec[InsPos[d].first][InsPos[d].second] = 1;
		}
		else {
			int d = rand()%DelPos.size();
			vec[DelPos[d].first][DelPos[d].second] = 0;
		}
		int xl=LX+1, xr=0, yl=LX+1, yr=0;
		for (int i=0; i<=LX+1; ++i) {
			for (int j=0; j<=LX+1; ++j) {
				if (!vec[i][j]) continue;
				xl=min(xl,i); xr=max(xr,i);
				yl=min(yl,j); yr=max(yr,j);
			}
		}
		int NX = max(yr-yl+1,xr-xl+1);
		int sx=xl-((NX-(xr-xl+1)+1)>>1), sy=yl-((NX-(yr-yl+1))>>1);
		vector<vector<int> > nv(NX,vector<int>(NX));
		for (int i=0; i<NX; ++i) {
			for (int j=0; j<NX; ++j) {
				int x=sx+i, y=sy+j;
				if (((x>=0)&&(y>=0))&&((x<=LX+1)&&(y<=LX+1))) nv[NX-i-1][j]=vec[x][y];
			}
		}
		pair<int,int> o = ReadMutateName(p.GetRealName());
		int ad=1; if ((o.first==MutateID)&&(!p.GetRealName().empty())) ad=2;
		bool tp = false;
		if (p.GetLX()>=2) tp=true;
		Piece *nw = new Piece(NX,nv,((o.second)||(tp))?p.GetColor():RollColor(),"?"+IToS(o.first)+"-"+IToS(o.second+ad));
		CustomPiece.push_back(nw);
		return *nw;
	}
	void DecD(double &a, int TimeDel, double r=1.0) {
		if ((Bot)&&(BotSpeed<100)) {
			a=0.0; return;
		}
		bool f = (a<0.0);
		a = fabs(a);
		double val = max(double(TimeDel)*0.001,a*double(TimeDel)*0.002)*12.0;
		a = max(0.0,a-val*r);
		if (f) a=-a;
	}
	int QPNextLevelDistance() {
		if (QPLevel>=QPLevelScore.size()) return 100000000;
		return int(min(100000000LL,max(0LL,QPLevelScore[QPLevel]*1000LL-TQPScore)));
	}
	char KeyRetry = 0;
	bool RetryMsg = false;
	ll QPExpReq() {
		return max(40000LL,40000LL*(QPRank+1));
	}
	void GiveQPExp(int x) {
		if ((Over)||(x<=0)||(!QPMode)) return;
		/*double sz = 0.8*(1.0+0.1*sqrt(max(0.0,x*0.0002)));
		AddFloatText("+"+Format("%.2f",double(x)*0.0001), 0xFDD000, sz, 0.08, 0.15, 0.1,
		SX+2, LY*0.5-0.4, 0.3, 0, 0, 0);*/
		QPAddExp += x;
		/*ll exp=QPExp+x, req=QPExpReq(); int rnk=QPRank;
		int ff = 0;
		while (exp>=req) {
			int ad = int(min(10000LL,exp/req));
			exp -= req;
			for (int i=0; i<ad; ++i) {
				if (i) QPAddExp+=req;
				++rnk; req+=40000LL;
			}
			if (++ff>1000) break;
		}*/
	}
	int SelX=0, SelY=0; bool CursorMode=false;
	bool QPHardmode() {
		return ((Mod2==2)||(Mod3==2)||(Mod4==2)||(Mod5==2)||(Mod6==2)||(Mod7==2)||(Mod8==2)||(Mod9)
		||(ModS==2)||(ModP==2)||(ModD==2)||(ModM==2)||(ModS==2));
	}
	bool QPRevmode() {
		return ((Mod2==2)||(Mod3==2)||(Mod4==2)||(Mod5==2)||(Mod6==2)||(Mod7==2)||(Mod8==2)||(Mod9==2)
		||(ModS==2)||(ModP==2)||(ModD==2)||(ModM==2)||(ModS==2));
	}
	int GetSize() {
		return max(20,max(SX,(((QPMode)&&(Obs!=-1))||(PlayerCount<2))?LY:(LY<<1)));
	}
	bool GiveQPScoreMsg = false;
	public :
	void GetActions();
	void Paint();
	void GiveQPScore(ll x, bool Advanced=true, bool Multipler=true, int Color=0x99FFFF) {
		if ((Over)||(!QPMode)||(x<=0)) return;
		if (Multipler) {
			int rem = x*(QPRank+1)%4;
			x = x*(QPRank+1)/4;
			if ((rand()&3)<rem) ++x;
		}
		if (!TargetSta) TQPScore+=x;
		if (Advanced) {
			GiveQPScoreMsg = true;
			double sz = 0.8*(1.0+0.1*sqrt(max(0.0,x*0.001)));
			AddFloatText("+"+IToS(x/1000LL)+"."+Format("%02lld",x%1000LL/10LL), Color, sz, 0.08, 0.15, 0.1,
			SX+1, LY*0.5-0.4, 0.3, 0, 0, 0);
		}
	}
	bool CheckCursor() {
		if ((K<=0.001)||(tK<=0.001)) {
			SelX=SelY=0; return false;
		}
		if (((CurX>=PX-K*2)&&(CurX<=PX+K*SX+K*2))&&((CurY>=PY-K*2)&&(CurY<=PY+K*LY+K*2))) {
			SelX = SX-max(1,min(SX,int((CurX-PX)/K+1)))+1;
			SelY = max(1,min(LY,int((CurY-PY)/K+1)));
			return true;
		}
		SelX=SelY=0;
		return false;
	}
	int Mod2=0, Mod3=0, Mod4=0, Mod5=0, Mod6=0, Mod7=0, Mod8=0, Mod9=0;
	int ModS=0, ModP=0, ModM=0, ModD=0;
	vector<int> SendAttack() {
		if (AttackOut.empty()) return vector<int>();
		int v=AttackOut.back(); AttackOut.pop_back();
		return vector<int>({v});
	}
	//#warning ReceiveAttack
	void ReceiveAttack(int x, int From=-1) {
		if (x<=0) return;
		if (LastAttacker<0) RerollHole();
		if (From>=0) LastAttacker=From;
		if (QPMode) {
			InnerMess = min(10,Level)*((Mod9)?50:30);
			if (Mod3) InnerMess+=250;
			if (Mod3==2) InnerMess+=750;
			if (Mod8==2) InnerMess+=300;
			if ((Mod9<2)||(Time<60000LL*6)) InnerMess=max(0,InnerMess-QPWeightDec*15/10000);
			if ((Mod9==2)&&(Time>=60000LL*11)) InnerMess=1000;
			SegMess = InnerMess*5/2;
			InnerMess = max(0,min(1000,InnerMess));
			SegMess = max(0,min(1000,SegMess));
			if (Mod5==1) InnerMess/=2;
			if (Mod5==2) InnerMess/=3;
		}
		bool WindUp = false;
		int Seg = min(4,(x+3)/4);
		if ((x>=8)&&(QPMode)) {
			WindUp=true; WindUpCD=1500+Seg*500;
		}
		if ((x<8)&&(QPMode)) WindUpCD=max(WindUpCD,2000*x/8);
		if ((HolePos<1)||(HolePos>LY)) HolePos=rand()%LY+1;
		x = min(5000,x);
		for (int i=0; i<x; ++i) {
			double ChangeProb = (((i)&&((!GarbageSeg)||(i%max(1,GarbageSeg))))?InnerMess:SegMess)*0.001;
			if (randf()<ChangeProb) RerollHole();
			GarbageLayer o=RollGarbage(); o.Delay=GarbageDelay;
			if (WindUp) o.Delay+=1000+500*min(Seg-1,i/4);
			//if (Mod5==2) o.Delay+=5000;
			AttackIn.push_back(o);
		}
		QPWeightDec = min(180000,QPWeightDec+x*10000/((Mod5==2)?3:1));
	}
	void KeyBind(string Type, string val) {
		Type=ToLower(Type); val=ToLower(val);
		Action Tar; bool f=false;
		for (pair<string,Action> o : KeyToAct) {
			if (ToLower(o.first)==Type) {
				f=true; Tar=o.second; break;
			}
		}
		if (Type=="keypause") KeyPause=AskKey(val);
		if (!f) return;
		for (Monitor &o : Keys) {
			if (o.Result==Tar) o.Key=AskKey(val);
		}
	}
	void ChangeHandling(string Type, int val) {
		Type = ToLower(Type);
		for (Monitor &o : Keys) {
			if ((o.Result==A_Left)||((o.Result==A_Right))) {
				if (Type=="das") o.DAS=val;
				if (Type=="arr") o.ARR=val;
				if (Type=="dcd") o.DCD=val;
			}
			if ((o.Result==A_SoftDrop)||(o.Result==A_Up)) {
				if (Type=="sddas") o.DAS=val;
				if (Type=="sdarr") o.ARR=(o.Result==A_SoftDrop)?min(0,val):val;
				if (Type=="dcd") o.DCD=val;
			}
			if (Type=="hcd") HCD=val;
		}
		if (Type=="das") DAS=val;
		if (Type=="arr") ARR=val;
		if (Type=="dcd") DCD=val;
		if (Type=="sddas") SDDAS=val;
		if (Type=="sdarr") SDARR=val;
		if (Type=="smoothlevel") SmoothLevel=val;
	}
	void SetDefaultRot(int val) {
		if ((val!=1)&&(val!=3)) return;
		DefaultRot = val;
	}
	Monitor* FindMonitor(Action Result) {
		for (Monitor &o : Keys) {
			if (o.Result==Result) return &o;
		}
		return nullptr;
	}
	bool IsBot() {
		return Bot;
	}
	double GetQPAttackWeightInc() {
		return QPAttackWeightInc;
	}
	ll GetTime() {
		return Time;
	}
	void ToggleBot() {
		Bot = !Bot;
		ClearSearchProcess(); PendingActions.clear();
		if (CheckSpawn(Cur)) {
			Spawn(Cur); ResetRem=ResetLim; FallTime=0;
		}
		HoldRem = HoldLen;
	}
	int GetLastAttacker() {
		return LastAttacker;
	}
	int GetKOMessage() {
		//if (!Over) return -2;
		int tmp=KOMessage; KOMessage=-2;
		return tmp;
	}
	void ForceRetry() {
		RetryMsg = true;
	}
	int GetQPTargetWeight() {
		if ((Over)||(Time<=0LL)||(WindUpCD>0)) return 0;
		int res = 6;
		if (Time>3*60000LL) res+=2;
		if (Time>5*60000LL) res+=2;
		if (Time>7*60000LL) res+=2;
		if (!QPHardmode()) {
			if (GetHeight()+AttackIn.size()+10>=SX) res-=3;
		}
		res = max(0,res-min(6,(QPWeightDec+2999)/3000));
		if ((Mod3==2)||(Mod6==2)||(Mod8==2)) {
			int gc = 0;
			for (int i=1; i<=LX; ++i) {
				bool f=false, df=false;
				for (int j=1; j<=LY; ++j) {
					if (C[i][j]) continue;
					if (B[i][j]<0) {
						f=true; if (IsUnclearable(B[i][j])) df=true;
					}
				}
				if ((f)&&(!df)) ++gc;
				if (gc>=5) break;
			}
			res = max(0,res-gc);
		}
		return res; 
	}
	int ObserveLevel() {
		return ((!Bot)&&(!Over));
	}
	int GetStackHeight() {
		return StackHeight;
	}
	int GetBadge() {
		return Badge;
	}
	int GetBoardID() {
		return BoardID;
	}
	int KOCount=0, Badge=0, Rank=999;
	Board(int _BoardID, vector<ConfigContent> Config={}, vector<Piece> _Pool={}, int Seed=-1, double _BotPos=0.0) {
		BoardID = _BoardID;
		LatestClock = int(GetClock());
		BotPos = _BotPos;
		if (Seed==-1) Seed=LatestClock;
		BoardConfig = Config;
		int _LX=GetConfig("LX",40), _LY=GetConfig("LY",10), _SX=GetConfig("SX",22);
		Bot = bool(GetConfig("Bot",0));
		LX=max(2,min(100,_LX)); LY=max(1,min(100,_LY));
		HoldLen = max(0,min(5,GetConfig("HoldLen",1)));
		NextLen = max(0,min(100,GetConfig("NextLen",5)));
		HoldType = max(0,min(2,GetConfig("HoldType",1)));
		DefaultRot = GetConfig("DefaultRot",3);
		UID = 1;
		if (DefaultRot!=1) DefaultRot=3;
		DefaultDir = max(0,min(3,GetConfig("DefaultDir",0)));
		BaseARE = max(0,min(Max,GetConfig("ARE",0)));
		BaseLCD = max(0,min(Max,GetConfig("LCD",0)));
		BaseCCD = max(0,min(Max,GetConfig("CCD",200)));
		SpeedCap = max(0,min(Max,GetConfig("SpeedCap",0)));
		DAS = max(-1,min(5000,GetConfig("DAS",200)));
		ARR = max(-1,min(5000,GetConfig("ARR",0)));
		SDDAS = max(-1,min(5000,GetConfig("SDDAS",200)));
		SDARR = max(-1,min(5000,GetConfig("SDARR",20)));
		HCD = max(0,min(5000,GetConfig("HCD",100)));
		DCD = max(0,min(5000,GetConfig("DCD",0)));
		SmoothLevel = max(0,min(4,GetConfig("SmoothLevel",3)));
		BaseDropDelay = max(-1,min(Max,GetConfig("DropDelay",1000)));
		if (BaseDropDelay<0) BaseDropDelay=Max;
		BaseLockDelay = max(-1,min(Max,GetConfig("LockDelay",500)));
		ResetLim = max(-1,min(Max,GetConfig("ResetLim",15)));