PrintStrR("/ "+IToS(Stock+1), StatEnd+1.5, -2, 0.7, 0x99FFFF);
			}
			if (QPMode) {
				PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
				ll val=DQPScore; clr=0x99FFFF;
				if (TarScore) {
					if (TQPScore<TarScore) {
						clr=(TypeScore)?0xEE82EE:0xFDD000; val=TarScore-DQPScore;
					}
					else {
						clr=(TypeScore)?0xFF6666:0x00FFCC; if (TypeScore) val=TarScore;
					}
				}
				PrintStrL(IToS(val/1000LL)+".", StatEnd-2, LY+1, 1, clr, 0.13);
				PrintStrL(Format("%02lld",val%1000LL/10LL), StatEnd-1.8, LY+1.4+IToS(val/1000LL).size()*2.0/3.0, 0.7, clr, 0.13);
				PrintStrL("Stage "+IToS(Level), StatEnd-1, LY+1, 0.7, 0x99FFFF);
				PrintStrL(Format("%.1f",double(QPLevTime)/1000.0), StatEnd, LY+1, 0.7, 0xDDDDDD);
			}
			else if (PlayerCount>1) {
				PrintStrL("Rank", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
				PrintStrL(IToS(Rank), StatEnd-2, LY+1, 1, 0x99FFFF, 0.13);
				PrintStrL("/ "+IToS(PlayerCount), StatEnd-1, LY+1, 0.7, 0x99FFFF);
			}
			else {
				PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
				ll val=DScore; clr=0x99FFFF;
				if (TarScore) {
					if (TScore<TarScore) {
						clr=(TypeScore)?0xEE82EE:0xFDD000; val=TarScore-DScore;
					}
					else {
						clr=(TypeScore)?0xFF6666:0x00FFCC; if (TypeScore) val=TarScore;
					}
				}
				PrintStrL(IToS(val), StatEnd-2, LY+1, 1, clr, 0.13);
				PrintStrL(Format("%.2f /p",double(TScore)/double(max(1,TPiece))), StatEnd-1, LY+1, 0.7, 0x99FFFF);
			}
		}
		if (HideStat) {
			bool ff = ((UseClick)&&(!CursorMoveLock));
			PrintStrR("Guide", StatEnd-13, -2, 0.8, 0xFDD000);
			PrintStrR("Move Piece", StatEnd-11.8, -2, 0.7, 0xEEEEEE);
			PrintStrR((ff)?"Move Cursor":GetKeyChar(A_Left)+" "+GetKeyChar(A_Right), StatEnd-11, -2, (ff)?0.85:1, 0x99FFFF);
			PrintStrR("Rotate Piece", StatEnd-9.8, -2, 0.7, 0xEEEEEE);
			PrintStrR((ff)?"Tap Right":GetKeyChar(A_Rotate)+" "+GetKeyChar(A_CW)+" "+GetKeyChar(A_CCW), StatEnd-9, -2, (ff)?0.85:1, 0x99FFFF);
			PrintStrR("Drop Faster", StatEnd-7.8, -2, 0.7, 0xEEEEEE);
			PrintStrR((ff)?"Hold Left":GetKeyChar(A_SoftDrop)+" "+GetKeyChar(A_SonicDrop), StatEnd-7, -2, (ff)?0.85:1, 0x99FFFF);
			PrintStrR("Place Piece", StatEnd-5.8, -2, 0.7, 0xEEEEEE);
			PrintStrR((ff)?"Tap Left":GetKeyChar(A_HardDrop), StatEnd-5, -2, (ff)?0.85:1, 0x99FFFF);
			PrintStrR("Hold Piece", StatEnd-3.8, -2, 0.7, 0xEEEEEE);
			PrintStrR((ff)?"Hold Right":GetKeyChar(A_Hold), StatEnd-3, -2, (ff)?0.85:1, 0x99FFFF);
			PrintStrR("Retry", StatEnd-1.8, -2, 0.7, 0xEEEEEE);
			PrintStrR(GetKeyChar(A_Retry), StatEnd-1, -2, 1, 0x99FFFF);
			PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
			PrintStrL(IToS(DScore), StatEnd-2, LY+1, 1, 0x99FFFF, 0.13);
			PrintStrL(Format("%.2f /p",double(TScore)/double(max(1,TPiece))), StatEnd-1, LY+1, 0.7, 0x99FFFF);
		}
		for (Particle o : Particles) Poly(o.x,o.y,o.sz,o.n,o.clr,o.th);
		for (FloatText o : FloatTexts) PrintStrM(o.Text,o.x,o.y,o.sz,o.clr,o.bd,o.w,"",o.th);
		if (TargetSta==1) {
			PrintStrM("Complete", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xAAFFAA, 0.15);
			PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
		}
		else if (TargetSta==-1) {
			PrintStrM("Finish", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xFDD000, 0.15);
			PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
		}
		else if (Over) {
			PrintStrM("Game Over", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xFF6666, 0.15);
			PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
		}
		else if (Pause) {
			PrintStrM("Paused", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0x66CCFF, 0.15);
			PrintStrM("Press "+KeyChar(KeyPause)+" to Resume", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
		}
	}
	char KeyPause = 'O';
	string GetKeyChar(Action x) {
		int res = 0;
		for (Monitor &o : Keys) {
			if (o.Result==x) res=o.Key;
		}
		return KeyChar(res);
	}
	#warning Board::Paint End
	bool AvailBotAction(Action o) {
		return ((o==A_Retry)&&((!QPMode)||(PlayerCount<2)||(Over)));
	}
	bool PreActive = false;
	vector<Action> GetActions() {
		int TimeDel = GlobalRealTimeDel;
		vector<Action> res;
		bool Active = CheckActive();
		for (Monitor &o : Keys) {
			if ((Bot)&&(!AvailBotAction(o.Result))) continue;
			bool Ignore = false;
			if (o.Result==A_Left) Ignore=((!Active)||(!CheckLeft()));
			if (o.Result==A_Right) Ignore=((!Active)||(!CheckRight()));
			if (o.Result==A_SoftDrop) Ignore=((!Active)||((!CheckDown())&&(!UseDeep)));
			if (o.Result==A_Rotate) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(DefaultRot))));
			if (o.Result==A_CW) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(1))));
			if (o.Result==A_Half) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(2))));
			if (o.Result==A_CCW) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(3))));
			if (o.Result==A_Up) Ignore=((!Active)||(!CheckUp()));
			if ((o.Result==A_Hold)||(o.Result==A_Waste)||(o.Result==A_Reset)
			||(o.Result==A_Lock)||(o.Result==A_Flip)) Ignore=((!Active)&&(!PreActive));
			if (((Pause)||(Over))&&(o.Result!=A_Retry)) Ignore=true;
			int cnt = o.Output(TimeDel,Ignore);
			for (int i=0; i<cnt; ++i) res.push_back(o.Result);
		}
		return res;
	}
	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 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;
	#warning Board::Board
	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)));
		BaseGravityDelay = max(0,min(Max,GetConfig("GravityDelay",0)));
		if (ResetLim<0) ResetLim=9999999;
		BotSpeed = max(0,min(Max,GetConfig("BotSpeed",1000)));
		GenType = (EGenerationType)max(0,min(2,GetConfig("RandomType",G_Bag)));
		ActType = max(0,min(9,GetConfig("BotType",1)));
		Mod8 = max(0,min(2,GetConfig("Mod8",0)));
		BaseRule = (EBaseRule)max(0,min(5,GetConfig("BaseRule",BSR_Default)));
		ComboRule = (EComboRule)max(0,min(13,GetConfig("ComboRule",CR_Default)));
		SpinRule = (ESpinRule)max(0,min(7,GetConfig("SpinRule",SR_Default)));
		B2BRule = (EB2BRule)max(0,min(5,GetConfig("B2BRule",BR_Surge)));
		AllClearRule = (EAllClearRule)max(0,min(6,GetConfig("AllClearRule",AR_Default)));
		ClutchRule = (EClutchRule)max(0,min(2,GetConfig("ClutchRule",CLR_Default)));
		GarbageDelay = max(0,min(Max,GetConfig("GarbageDelay",500)));
		GarbageSeg = max(0,min(Max,GetConfig("GarbageSeg",0)));
		GarbagePass = max(0,min(Max,GetConfig("GarbagePass",0)));
		GarbagePush = max(0,min(Max,GetConfig("GarbagePush",100)));
		GarbageGap = max(-1,min(Max,GetConfig("GarbageGap",8)));
		GarbagePreview = max(0,min(30,GetConfig("GarbagePreview",10)));
		ForceShowAttack = max(0,min(1,GetConfig("ForceShowAttack",0)));
		AutoRespawn = max(0,min(1,GetConfig("AutoRespawn",0)));
		HideStat = max(0,min(1,GetConfig("HideStat",0)));
		RotateSystem = (ERotateSystem)max(0,min(15,GetConfig("RotateSystem",R_Default)));
		TarPiece = max(0,min(Max,GetConfig("TarPiece",0)));
		TarLine = max(0,min(Max,GetConfig("TarLine",0)));
		TarDig = max(0,min(Max,GetConfig("TarDig",0)));
		TarAttack = max(0,min(Max,GetConfig("TarAttack",0)));
		TarTime = max(0,min(Max,GetConfig("TarTime",0)));
		TarScore = max(0,min(Max,GetConfig("TarScore",0)));
		TypePiece = max(0,min(1,GetConfig("TypePiece",0)));
		TypeLine = max(0,min(1,GetConfig("TypeLine",0)));
		TypeDig = max(0,min(1,GetConfig("TypeDig",0)));
		TypeAttack = max(0,min(1,GetConfig("TypeAttack",0)));
		TypeTime = max(0,min(1,GetConfig("TypeTime",0)));
		TypeScore = max(0,min(1,GetConfig("TypeScore",0)));
		LevTime = max(0,min(Max,GetConfig("LevTime",0)));
		LevPiece = max(0,min(Max,GetConfig("LevPiece",0)));
		LevLine = max(0,min(Max,GetConfig("LevLine",0)));
		LevAttack = max(0,min(Max,GetConfig("LevAttack",0)));
		LevTimeInc = max(0,min(Max,GetConfig("LevTimeInc",0)));
		LevPieceInc = max(0,min(Max,GetConfig("LevPieceInc",0)));
		LevLineInc = max(0,min(Max,GetConfig("LevLineInc",0)));
		LevAttackInc = max(0,min(Max,GetConfig("LevAttackInc",0)));
		LevBase = max(0,min(1000,GetConfig("LevBase",0)));
		LevStart = max(0,min(1000,GetConfig("LevStart",0)));
		LevMax = max(0,min(100,GetConfig("LevMax",50)));
		LevScaleMode = (ELevScaleMode)max(0,min(3,GetConfig("LevScaleMode",LS_Default)));
		LevScaleRate = max(0,min(1000,GetConfig("LevScaleRate",7)));
		MasterThreshold = max(0,min(Max,GetConfig("MasterThreshold",0)));
		GarbageShape = (EGarbageShape)max(0,min(7,GetConfig("GarbageShape",GS_Default)));
		GarbageType = (EGarbageType)max(0,min(4,GetConfig("GarbageType",GT_Default)));
		StaticGarbHeight = max(0,min(100,GetConfig("StaticHeight",0)));
		StartHeight = max(0,min(100,GetConfig("StartHeight",0)));
		InnerMess = max(0,min(1000,GetConfig("InnerMess",0)));
		SegMess = max(0,min(1000,GetConfig("SegMess",1000)));
		IntRule = (EIntRule)max(0,min(3,GetConfig("IntRule",IR_Floor)));
		GarbageActive = (EGarbageActive)max(0,min(3,GetConfig("GarbageActive",GA_Default)));
		UseMove = max(0,min(1,GetConfig("UseMove",1)));
		UseRotate = max(0,min(1,GetConfig("UseRotate",1)));
		Use2Rot = max(0,min(1,GetConfig("Use2Rot",1)));
		UseZone = max(0,min(1,GetConfig("UseZone",0)));
		ZoneReq = max(0,min(Max,GetConfig("ZoneReq",8)));
		ZoneSeg = max(1,min(Max,GetConfig("ZoneSeg",4)));
		ZoneTime = max(0,min(Max,GetConfig("ZoneTime",5000)));
		UseHalf = max(0,min(1,GetConfig("UseHalf",1)));
		UseSoftDrop = max(0,min(1,GetConfig("UseSoftDrop",1)));
		UseHardDrop = max(0,min(1,GetConfig("UseHardDrop",1)));
		UseSonicDrop = max(0,min(1,GetConfig("UseSonicDrop",1)));
		UseFlip = max(0,min(1,GetConfig("UseFlip",0)));
		UseUp = max(0,min(1,GetConfig("UseUp",0)));
		UseWaste = max(0,min(1,GetConfig("UseWaste",0)));
		UseTip = max(0,min(1,GetConfig("UseTip",0)));
		UseUndo = max(0,min(1,GetConfig("UseUndo",0)));
		if (BoardID>1) UseUndo=false;
		UseLock = max(0,min(1,GetConfig("UseLock",0)));
		UseDeep = max(0,min(1,GetConfig("UseDeep",0)));
		UsePlace = max(0,min(1,GetConfig("UsePlace",0)));
		UseClick = max(0,min(1,GetConfig("UseClick",1)));
		RandomRot = max(0,min(1,GetConfig("RandomRot",0)));
		ShadowType = max(0,min(2,GetConfig("ShadowType",1)));
		StartDelay = max(0,min(Max,GetConfig("StartDelay",3000)));
		VanishHeight = max(0,min(100,GetConfig("VanishHeight",0)));
		GravityType = (EGravityType)max(0,min(7,GetConfig("GravityType",0)));
		Stock = max(-1,min(Max,GetConfig("Stock",0)));
		Mutate = max(0,min(1,GetConfig("Mutate",0)));
		DigBonus = max(0,min(2,GetConfig("DigBonus",2)));
		SingleBonus = max(0,min(1,GetConfig("SingleBonus",0)));
		OpenerProtect = max(0,min(Max,GetConfig("OpenerProtect",14)));
		Pool = (_Pool.empty())?DefaultPool:_Pool;
		int tmp = max(-1,min(8,GetConfig("TargetType",-1)));
		if (tmp<0) {
			if (Bot) tmp=rand()%6+2;
			else tmp=2;
		}
		TargetType = (ETargetType)tmp;
		Mod2 = max(0,min(2,GetConfig("Mod2",0)));
		Mod3 = max(0,min(2,GetConfig("Mod3",0)));
		Mod4 = max(0,min(2,GetConfig("Mod4",0)));
		Mod5 = max(0,min(2,GetConfig("Mod5",0)));
		Mod6 = max(0,min(2,GetConfig("Mod6",0)));
		Mod7 = max(0,min(2,GetConfig("Mod7",0)));
		Mod8 = max(0,min(2,GetConfig("Mod8",0)));
		Mod9 = max(0,min(2,GetConfig("Mod9",0)));
		ModP = max(0,min(2,GetConfig("ModP",0)));
		ModM = max(0,min(2,GetConfig("ModM",0)));
		ModS = max(0,min(2,GetConfig("ModS",0)));
		if (ModM==2) Mutate=true;
		if (ModP==1) Pool=PentominoPool;
		if (ModP==2) Pool=HexaminoPool;
		//if (!BoardID&&1) Pool={P_I,P_J,P_L,P_O,P_S,P_T,P_Z,P_A,P_B,P_C};
		//if (!BoardID&&1) Pool={P_A,P_A,P_A,P_A,P_A,P_A,\
		P_B,P_B,P_B,P_B,P_B,\
		P_C,P_D,P_C,P_D,P_C,P_D,P_C,P_D,\
		P_I,P_J,P_L,P_O,P_S,P_T,P_Z,\
		P_I,P_J,P_L,P_O,P_S,P_T,P_Z,\
		P_I,P_J,P_L,P_O,P_S,P_T,P_Z,\
		P_I5,P_T5,P_U,P_V,P_X,P_F,P_E,P_S5,P_Z5,P_J5,P_L5,P_Y,P_R,P_H,P_N,P_Q,P_P,P_W,\
		P_I5,P_T5,P_U,P_V,P_X,P_F,P_E,P_S5,P_Z5,P_J5,P_L5,P_Y,P_R,P_H,P_N,P_Q,P_P,P_W,\
		P_30,P_31,P_32,P_33,P_34,P_35,P_36,P_37,P_38,P_39,P_40,P_41,P_42,P_43,P_44,P_45,P_46,P_47,P_48,P_49,P_50,P_51,P_52,P_53,P_54,P_55,P_56,P_57,P_58,P_59,P_60,P_61,P_62,P_63,P_64,P_65,P_66,P_67,P_68,P_69,P_70,P_71,P_72,P_73,P_74,P_75,P_76,P_77,P_78,P_79,P_80,P_81,P_82,P_83,P_84,P_85,P_86,P_87,P_88,P_89};
		MasterBegin = -1;
		CursorMode=((UsePlace)&&(!HideStat)); CursorMoveLock=true;
		for (int i=0; i<=1000; ++i) {
			int val = BaseDropDelay*GetLevelMultipler(i,0.001*double(LevScaleRate));
			if (val<=MasterThreshold) {
				MasterBegin=i; break;
			}
		}
		if (BaseDropDelay<0) MasterBegin=-1;
		if (_SX==-1) SX=LX;
		else SX=max(2,min(LX,_SX));
		if (Mod5==2) SX=max(2,SX-6);
		//if (ModM) SX=min(LX,SX+4);
		if (ModS) {
			SX=6+max(ModP,ModM); LY=4+max(ModP,ModM); LX=max(LX,40);
		}
		B = new int*[LX+2];
		C = new bool*[LX+2];
		Age = new int*[LX+2];
		U = new int*[LX+2];
		FallDest = new int[LX+2];
		for (int i=0; i<=LX+1; ++i) {
			FallDest[i] = 0;
			B[i] = new int[LY+2];
			C[i] = new bool[LY+2];
			Age[i] = new int[LY+2];
			U[i] = new int[LY+2];
			for (int j=0; j<=LY+1; ++j) {
				Age[i][j]=B[i][j]=U[i][j]=0; C[i][j]=false;
			}
		}
		SearchStep = -1;
		ResetBoard(Seed);
		PX=PY=0.0; K=28.0;
		KeyPause = AskKey(GetConfig("KeyPause","o",KeyList));
		if (ModS==2) DAS=ARR=SDDAS=SDARR=0;
		Keys = {
			Monitor(A_SoftDrop, AskKey(GetConfig("KeySoft","down",KeyList)), UseSoftDrop, SDDAS, min(SDARR,0), DCD),
			Monitor(A_SonicDrop, AskKey(GetConfig("KeySonic","s",KeyList)), UseSonicDrop, 0, 0),
			Monitor(A_Left, AskKey(GetConfig("KeyLeft","left",KeyList)), UseMove, DAS, ARR, DCD),
			Monitor(A_Right, AskKey(GetConfig("KeyRight","right",KeyList)), UseMove, DAS, ARR, DCD),
			Monitor(A_Rotate, AskKey(GetConfig("KeyRotate","up",KeyList)), UseRotate),
			Monitor(A_CW, AskKey(GetConfig("KeyCW","x",KeyList)), Use2Rot),
			Monitor(A_CCW, AskKey(GetConfig("KeyCCW","z",KeyList)), Use2Rot),
			Monitor(A_Half, AskKey(GetConfig("KeyHalf","a",KeyList)), UseHalf),
			Monitor(A_HardDrop, AskKey(GetConfig("KeyHard","space",KeyList)), UseHardDrop),
			Monitor(A_Hold, AskKey(GetConfig("KeyHold","c",KeyList))),
			Monitor(A_Retry, AskKey(GetConfig("KeyRetry","u",KeyList))),
			Monitor(A_Reset, AskKey(GetConfig("KeyReset","e",KeyList)), HoldType>1),
			Monitor(A_Up, AskKey(GetConfig("KeyUp","w",KeyList)), UseUp, DAS, SDARR, DCD),
			Monitor(A_Waste, AskKey(GetConfig("KeyWaste","v",KeyList)), UseWaste),
			Monitor(A_Flip, AskKey(GetConfig("KeyFlip","q",KeyList)), UseFlip),
			Monitor(A_Lock, AskKey(GetConfig("KeyLock","r",KeyList)), UseLock),
			Monitor(A_CursorMode, AskKey(GetConfig("KeyCursorMode","i",KeyList))),
			Monitor(A_TargetMode, AskKey(GetConfig("KeyTargetMode","f",KeyList))),
			Monitor(A_Undo, AskKey(GetConfig("KeyUndo","t",KeyList)), ((!Bot)&&(UseUndo))),
			Monitor(A_Redo, AskKey(GetConfig("KeyRedo","y",KeyList)), ((!Bot)&&(UseUndo))),
			Monitor(A_Action, AskKey(GetConfig("KeyAction","g",KeyList)), !Bot),
		};
		KeyRetry = AskKey(GetConfig("KeyRetry","u",KeyList));
		Monitor *ml=FindMonitor(A_Left), *mr=FindMonitor(A_Right);
		if (ml!=nullptr) ml->Opposite=mr;
		if (mr!=nullptr) mr->Opposite=ml;
		ml=FindMonitor(A_SoftDrop); mr=FindMonitor(A_Up);
		if (ml!=nullptr) ml->Opposite=mr;
		if (mr!=nullptr) mr->Opposite=ml;
		//if ((Bot)&&(PlayerCount>2)) LX=min(LX,SX+2); 
	}
	~Board() {
		ClearSearchProcess();
		for (Piece *p : CustomPiece) delete p;
		for (int i=0; i<=LX+1; ++i) {
			delete[] B[i]; delete[] C[i]; delete[] Age[i]; delete[] U[i];
		}
		delete[] B; delete[] C;
		delete[] Age; delete[] U; delete[] FallDest;
	}
	bool IsOver() {
		return Over;
	}
	ETargetType GetTargetType() {
		return TargetType;
	}
	inline double GetQPScoreF() {
		return double(TQPScore)*0.001;
	}
	void SetQPScore(ll x) {
		TQPScore = x;
	}
	void SetTime(ll x) {
		Time = x;
	}
	void SetScale(double _PX, double _PY, double _K) {
		int Lev = GetSize();
		tK = 0.8*_K/double(Lev);
		tPX = _PX+_K*(0.1+0.4*double(Lev-SX)/double(Lev));
		tPY = _PY+_K*(0.1+0.4*double(Lev-LY)/double(Lev));
		if ((Menu=="home")||(Menu=="settings")) {
			tPX+=3.0; tPY-=16.0;
		}
		if (FirstSetScale) {
			PX=tPX; PY=tPY; K=tK;
			FirstSetScale = false;
		}
	}
	#warning Board::Frame
	bool ClickHoldCD=false, CursorMoveLock=true;
	void Frame(int TimeDel, int _LatestClock, bool PermitSearch=false) {
		if ((Bot)&&(Over)&&(tK<0.001)&&(!CheckKey(KeyRetry))&&(!QPMode)) return;
		LatestClock = _LatestClock;
		int RealTimeDel = TimeDel;
		if (Pause) TimeDel=0;
		while (AttackIn.size()>5000) AttackIn.pop_back();
		while (AttackOut.size()>5000) AttackOut.pop_back();
		if (Over) {
			AttackIn.clear(); AttackOut.clear();
		}
		ShowParticle = ((!Bot)||((K*double(GetSize())>=8.0)&&(BotSpeed>=100)));
		if (!Over) {
			if (!TargetSta) Time+=ll(TimeDel);
		}
		FillNext();
		bool Active=CheckActive(); PreActive=false;
		if ((!Bot)&&(!Active)&&(!Over)&&(InactiveTime<=TimeDel)) PreActive=true;
		vector<Action> Actions = GetActions();
		bool G20 = ((!Bot)&&((!DropDelay)&&(GravityCD<=0)));
		if (Bot) {
			FallTime=0; ResetRem=99999999;
		}
		bool ForceLock = false;
		if (Bot) UseUndo=false;
		if (!UseUndo) History.clear();
		StackHeight = 0;
		for (int i=LX; i; --i) {
			for (int j=1; j<=LY; ++j) {
				if (CheckGrid(i,j)) {
					StackHeight=i; break;
				}
			}
			if (StackHeight) break;
		}
		if ((!Over)&&(!TargetSta)) {
			int t0=0, c0=0, t1=0, c1=0;
			if (TarPiece) {
				++((TypePiece)?t1:t0);
				if (TPiece>=TarPiece) ++((TypePiece)?c1:c0);
			}
			if (TarLine) {
				++((TypeLine)?t1:t0);
				if (TLine>=TarLine) ++((TypeLine)?c1:c0);
			}
			if (TarDig) {
				++((TypeDig)?t1:t0);
				if (TDig>=TarDig) ++((TypeDig)?c1:c0);
			}
			if (TarAttack) {
				++((TypeAttack)?t1:t0);
				if (TAttack>=TarAttack) ++((TypeAttack)?c1:c0);
			}
			if (TarTime) {
				++((TypeTime)?t1:t0);
				if (Time>=TarTime) ++((TypeTime)?c1:c0);
			}
			if (TarScore) {
				++((TypeScore)?t1:t0);
				if (((QPMode)?TQPScore:TScore)>=TarScore) ++((TypeScore)?c1:c0);
			}
			if ((c0==t0)&&(t0)) TargetSta=1;
			else if (c1) TargetSta=-1;
		}
		if (QPMode) {
			ll req = QPExpReq();
			if ((!Over)&&(Time>=0LL)&&(!TargetSta)) {
				if (Mod9<2) GiveQPScore(min(TimeDel,min(QPNextLevelDistance()/100,QPNextLevelDistance()-1)),false);
				else {
					ll Bottom = QPLevelScore[max(0,min(int(QPLevelScore.size())-1,Level-1))]*1000LL;
					int lv = min(Level,10);
					TQPScore = max(Bottom,TQPScore-(ll(lv)*ll(lv+1)+10LL)*ll(TimeDel)/20LL);
				}
			}
			QPAddExp = max(QPAddExp,0LL);
			if (QPAddExp) {
				ll val = max(1LL,min(QPAddExp,QPAddExp*ll(TimeDel)/300LL));
				QPAddExp-=val; QPExp+=val;
			}
			if (Over) ;
			else if (QPExpDecCD>0) QPExpDecCD=max(0,QPExpDecCD-TimeDel);
			else if (TimeDel) {
				ll dec = ll(TimeDel)*ll(QPRank+1)*ll(QPRank+2)*((Mod9)?5LL:3LL)/6LL;
				QPExp -= dec;
				if (QPExp<0LL) {
					if (QPRank) {
						--QPRank; QPExp=QPExpReq()-1LL;
					}
					else QPExp=0LL;
				}
			}
			if (QPExp>=req) {
				QPExp-=req; ++QPRank;
				QPExpDecCD = 5000;
				req = QPExpReq();
				if (QPExp+QPAddExp>=req) {
					QPExp += QPAddExp;
					QPRank+=min(1000LL,QPExp/max(req,1LL)); QPExp-=req;
					ll val = min(QPExp,QPAddExp);
					QPExp-=val; QPAddExp=val;
				}
			}
			WindUpCD = max(0,WindUpCD-TimeDel);
			int ProtectDec = QPProtectDec[min(int(QPProtectDec.size())-1,Level)];
			if (Mod9==2) ProtectDec=max(100,1100-Level*100);
			QPWeightDec = max(0,QPWeightDec-TimeDel*10000/ProtectDec);
			if ((Time>=0LL)&&(!Over)) {
				ll pre=QPLevTime; QPLevTime+=TimeDel;
				if (Mod9==2) {
					if ((pre<60000LL)&&(QPLevTime>=60000LL)) AddNotice("REGRET",0xFF6666);
					if (pre>=60000LL) QPAttackWeightInc+=double(TimeDel)*0.001*0.005;
				}
			}
			if ((!QPSeg.empty())&&(K*double(GetSize())>=8.0)) {
				int clr = LevelColor[min(int(LevelColor.size())-1,QPRank)];
				if ((GiveQPScoreMsg)&&(QPSeg.size()<9000)) {
					if (Level<int(QPLevelScore.size())) clr=-clr;
					GiveQPScoreMsg = false;
				}
				if (QPSeg.back().first==clr) QPSeg.back().second.second=TQPScore;
				else if (QPSeg.size()<10000) QPSeg.push_back(make_pair(clr,make_pair(QPSeg.back().second.second,TQPScore)));
			}
		}
		if (Active) {
			if ((!Bot)&&(UseTip)&&(TgD<0)) SearchNext(true);
			bool CDown = CheckDown();
			InactiveTime = 0;
			if (Bot) FallTime=0;
			else if (((!CDown)||(DropDelay<Max))&&(GravityCD<=0)) FallTime+=TimeDel;
			if (CDown) {
				if ((!Bot)&&(FallTime>=DropDelay)) {
					FallTime=min(FallTime-DropDelay,DropDelay+5000); MoveDown();
					if (G20) SonicDrop();
				}
			}
			else {
				if ((!Bot)&&(LockDelay>=0)&&(FallTime>=LockDelay)) {
					FallTime=0; ForceLock=true;
				}
			}
			GravityCD = max(0,GravityCD-TimeDel);
			if (SideGravity) {
				int tot = 110;
				while ((tot>0)&&(CheckLeft())) {
					--tot; MoveLeft();
				}
			}
		}
		else {
			InactiveTime = max(0,InactiveTime-TimeDel);
			int flag = 0;
			if ((Over)||(InactiveTime>0)) ;
			else if ((CurZone)&&(ZoneRem<0)) flag=4;
			else {
				if (InCascade) {
					CascadeProcess(); InCascade=false; flag=3;
				}
				else {
					PostAREProcess();
					if (CheckCascade()) flag=2;
					else flag=1;
				}
			}
			if (flag==4) {
				ClearZone(); CurZone=0; PreAREProcess(false,true);
			}
			if (flag==1) {
				bool UseHis=((!Bot)&&(UseUndo)&&(!ReadHistoryMsg)); ReadHistoryMsg=false;
				BoardHistory His;
				if (UseHis) His=WriteHistory();
				Piece p = Next[0];
				if (Spawn(p,0,-1,((CurClutch)||((Bot)&&((PreInc)||(HoldPosInc+BeginInc)))))) {
					for (int i=0; i+1<Next.size(); ++i) Next[i]=Next[i+1];
					Next.pop_back(); ++DNext;
					if ((!Bot)&&(UseTip)) {
						ClearSearchProcess(); SearchNext(true);
					}
					if ((SmoothLevel>=4)&&(NextLen>0)&&((!Bot)||(BotSpeed>=100))) {
						PDx+=1-(SX-PosX); PDy+=(LY-1)-PosY;
					}
				}
				else if (!Bot) {
					int Rot=0; bool UseHold=false;
					vector<Action> NewActions;
					for (Action o : Actions) {
						if (o==A_Rotate) Rot+=DefaultRot;
						else if (o==A_CW) Rot+=1;
						else if (o==A_CCW) Rot+=3;
						else if (o==A_Half) Rot+=2;
						else if ((o==A_Hold)&&(HoldLen)&&(HoldType)) UseHold=true;
						else NewActions.push_back(o); 
					}
					Piece old = p;
					if (UseHold) {
						p = (Hold.size()==HoldLen)?Hold[0]:Next[1];
					}
					if (Spawn(p,0,(DefaultDir+Rot)&3,CurClutch)) {
						Actions = NewActions;
						for (int i=0; i+1<Next.size(); ++i) Next[i]=Next[i+1];
						Next.pop_back(); ++DNext;
						bool ff = false;
						if (UseHold) {
							if (Hold.size()==HoldLen) {
								for (int i=0; i+1<HoldLen; ++i) Hold[i]=Hold[i+1];
								Hold[HoldLen-1] = old;
								ff = true;
							}
							else {
								Hold.push_back(old);
								for (int i=0; i+1<Next.size(); ++i) Next[i]=Next[i+1];
								Next.pop_back();
							}
							HoldRem = max(0,HoldRem-1);
						}
						if ((SmoothLevel>=4)&&(NextLen>0)&&((!Bot)||(BotSpeed>=100))) {
							if (ff) {
								PDx+=1-(SX-PosX); PDy+=(-4)-PosY;
							}
							else {
								PDx+=1-(SX-PosX); PDy+=(LY-1)-PosY;
							}
						}
					}
					else TopOut();
				}
				else TopOut();
				FillNext();
				if ((UseHis)&&(CheckActive())) {
					while ((HistoryPos>0)&&(!History.empty())) {
						History.pop_front(); --HistoryPos;
					}
					HistoryPos=0; History.push_front(His);
					while (History.size()>101) History.pop_back();
				}
			}
			else if (flag==2) {
				InCascade = true; 
				InactiveTime=InactiveTot=CCD;
			}
			else if (flag==3) PreAREProcess(false,true);
		}
		if ((QPMode)&&(PlayerCount>1)&&(!Over)) {
			vector<int> lst = (Mod9<2)?QPRiseTime:QPRevMod9RiseTime;
			if ((QPRisePos<lst.size())&&(Time>=1000LL*ll(lst[QPRisePos]))) {
				++QPRisePos; GarbageLayer o(LY);
				for (int i=1; i<=LY; ++i) o.B[i]=Solid;
				AddGarbage(o, 2);
			}
		}
		Active = CheckActive();
		bool Reset=false, L20=false, R20=false, SDInDAS=false;
		if (SideGravity) L20=true;
		if (Time<0) ActionTime=int(-BotPos*double(BotSpeed));
		if (Bot) {
			int d = max(1,GetSearchSeg());
			int SegTime = BotSpeed/2/(d+2);
			ActionTime += TimeDel;
			ActionCD = max(0,ActionCD-TimeDel);
			vector<Action> tmp=Actions; Actions.clear();
			if ((!Pause)&&(!Over)&&(!DropDelay)&&(CheckActive())) SonicDrop();
			if ((Pause)||(Over)) ;
			else if (!PendingActions.empty()) {
				for (pair<Action,int> &o : PendingActions) o.second=min(o.second,BotSpeed);
				int dd=TimeDel, tot=0;
				while ((tot<PendingActions.size())&&(dd>0)) {
					int val = min(dd,PendingActions[tot].second);
					dd-=val; PendingActions[tot].second-=val;
					if (PendingActions[tot].second) break;
					++tot;
				}
				for (int i=0; i<tot; ++i) Actions.push_back(PendingActions[i].first);
				for (int i=0; i+tot<PendingActions.size(); ++i) PendingActions[i]=PendingActions[i+tot];
				PendingActions.resize(PendingActions.size()-tot);
			}
			else if ((ActionTime>=BotSpeed)&&(PermitSearch)&&(ActionCD<=0)) {
				if (BotSpeed) {
					vector<Action> Nxt = SearchNext(!((!BotType)||((AliveCount>5)&&(ActType==9)&&(PlayerCount>1))));
					//SearchNext(!((!BotType)||(BotType==8)||(BotType==9)));
					//printf("%lld step=%d\n", this, SearchStep);
					int tt = Nxt.size();
					int ts=BotSpeed/2, d=ts/max(1,tt);
					for (Action o : Nxt) {
						int cur = 1;
						PendingActions.push_back(make_pair(o,cur*d));
					}
					if (!Nxt.empty()) PendingActions[0].second+=ts-d*tt;
					//printf("[%d,%d]\n",BotType,SearchStep);
					if (!Nxt.empty()) ActionTime=min(BotSpeed+5000,max(0,ActionTime-BotSpeed));
					else ActionCD=SegTime;
				}
				else {
					Actions=SearchNext(); ActionTime=min(BotSpeed+5000,max(0,ActionTime-BotSpeed));
				}
			}
			for (Action o : tmp) {
				if (AvailBotAction(o)) Actions.push_back(o);
			}
			if ((UseZone)&&(!CurZone)&&(ZoneProgress>=ZoneReq*((GetHeight()+AttackIn.size()<=SX)?min(ZoneSeg,2):1))) Actions.push_back(A_Action);
			G20 = false;
		}
		else {
			ActionTime=ActionCD=0; if ((!UseTip)||(TgD>=0)) SearchStep=-1;
			for (Monitor &o : Keys) {
				if (!o.Enabled) continue;
				if (o.Result==A_SoftDrop) {
					if ((!SDARR)&&(o.CheckDAS())) G20=true;
					if (o.CheckDAS()) SDInDAS=true;
				}
				if (o.Result==A_Left) {
					if ((!o.ARR)&&(o.CheckDAS())&&(!o.DCDRem)) L20=true;
				}
				if (o.Result==A_Right) {
					if ((!o.ARR)&&(o.CheckDAS())&&(!o.DCDRem)) R20=true;
				}
				if (o.Result==A_SonicDrop) {
					if (CheckKey(o.Key)) G20=true;
				}
			}
		}
		if (((AutoRespawn)||((QPMode)&&(Bot)&&(PlayerCount>1)&&(Obs!=BoardID)))&&(Over)) Actions.push_back(A_Retry);
		if (RetryMsg) {
			Actions.push_back(A_Retry); RetryMsg=false;
		}
		if ((!Bot)&&(!CursorMode)&&(!Pause)&&(UseClick)&&(SelX)&&(CheckActive())) {
			bool PreCDown = CheckDown();
			bool ResetOper = false;
			int HalfLen = Cur.GetShapeMid();
			while ((UseMove)&&(PosY+HalfLen<SelY)&&(!CursorMoveLock)) {
				bool f=MoveRight(); ResetOper|=f;
				if (!f) break;
			}
			while ((UseMove)&&(PosY+HalfLen>SelY)&&(!CursorMoveLock)) {
				bool f=MoveLeft(); ResetOper|=f;
				if (!f) break;
			}
			if (RightUp) {
				CursorMoveLock = false;
				if (RightUp<200) {
					int UseRotate=DefaultRot, mndel=10;
					if (!CheckDown()) {
						for (int ii : {1,3,2}) {
							int i = ii;
							if ((i&1)&&(DefaultRot==3)) i^=2;
							if ((i==2)&&(!Use2Rot)) continue;
							CheckRotate(i); if (RotateToX<=-10) continue;
							int del = RotateToX-PosX;
							if (del<mndel) {
								mndel=del; UseRotate=i;
							}
						}
					}
					if ((UseRotate)||(Use2Rot)) {
						ResetOper |= Rotate(UseRotate);
						if (G20) {
							if (SonicDrop()) FallTime=0;
						}
					}
				}
			}
			if ((RightHold>=200)&&(!ClickHoldCD)) {
				ClickHoldCD=true; Actions.push_back(A_Hold);
			}
			if (!RightHold) ClickHoldCD=false;
			if (LeftUp) {
				if (CursorMoveLock) {
					if (Time>100LL) CursorMoveLock=false;
				}
				else if (LeftUp<200) ForceLock=true;
			}
			if (LeftHold>=200) {
				if (SDARR<1) {
					int f = SonicDrop();
					if (f) {
						ResetOper=true; FallTime=0;
					}
				}
				else if (CheckDown()) {
					FallTime += int(min(ll(DropDelay),ll(DropDelay)*ll(TimeDel)/ll(SDARR)));
					if (FallTime>=DropDelay) {
						MoveDown();
						if (!TargetSta) ++TScore;
						FallTime=0;
					}
				}
			}
			if ((ResetOper)&&((!CheckDown())||(!PreCDown))) {
				if (ResetRem>0) FallTime=0;
				if (ResetLim<Max) ResetRem=max(ResetRem-1, 0);
			}
			if (MidClick) Actions.push_back(A_Hold);
		}
		for (Action o : Actions) {
			bool ResetOper=false, PreCDown=CheckDown();
			if (o==A_Left) {
				CursorMoveLock = true;
				for (;;) {
					bool f = MoveLeft();
					if (!f) break;
					ResetOper = true;
					if (G20) {
						if (SonicDrop()) FallTime=0;
					}
					if (!L20) break;
				}
			}
			if (o==A_Right) {
				CursorMoveLock = true;
				for (;;) {
					bool f = MoveRight();
					if (!f) break;
					ResetOper = true;
					if (G20) {
						if (SonicDrop()) FallTime=0;
					}
					if (!R20) break;
				}
			}
			if (o==A_SoftDrop) {
				if ((!Bot)&&((G20)||((SDInDAS)&&(!SDARR)))) {
					if (SonicDrop(true)) FallTime=0;
				}
				else if ((Bot)||(!SDInDAS)||(SDARR<0)) {
					if (MoveDown()) {
						if (SmoothLevel>=3) {
							double FallRate = 1.0;
							if (DropDelay) FallRate=max(0.0,min(1.0,double(FallTime)/double(DropDelay)));
							PDx += 1.0-FallRate;
						}
						FallTime=0; if (!TargetSta) ++TScore;
					}
					else if ((UseDeep)&&(!Bot)) {
						if (DeepDrop()) FallTime=0;
					}
				}
				else {
					if (CheckDown()) {
						FallTime += int(min(ll(DropDelay),ll(DropDelay)*ll(TimeDel)/ll(SDARR)));
						if (FallTime>=DropDelay) {
							MoveDown();
							FallTime=0; if (!TargetSta) ++TScore;
						}
					}
				}
			}
			if (o==A_Rotate) {
				ResetOper = Rotate(DefaultRot);
				if (G20) {
					if (SonicDrop()) FallTime=0;
				}
			}
			if (o==A_CW) {
				ResetOper = Rotate(1);
				if (G20) {
					if (SonicDrop()) FallTime=0;
				}
			}
			if (o==A_Half) {
				ResetOper = Rotate(2);
				if (G20) {
					if (SonicDrop()) FallTime=0;
				}
			}
			if (o==A_CCW) {
				ResetOper = Rotate(3);
				if (G20) {
					if (SonicDrop()) FallTime=0;
				}
			}
			if (o==A_HardDrop) {
				if ((Bot)||(LastLock>=HCD)) HardDrop();
			}
			if (o==A_SonicDrop) {
				if (SonicDrop(true)) FallTime=0;
			}
			if (o==A_Hold) {
				if (!CheckActive()) continue;
				if (!Bot) HoldPosInc=0;
				if ((HoldType)&&(HoldLen)&&((HoldType==2)||(HoldRem>0))) {
					FillNext(); Piece NewP=Next[0];
					while (Hold.size()>HoldLen) Hold.pop_back();
					if (Hold.size()==HoldLen) NewP=Hold[0];
					if (CheckSpawn(NewP,HoldPosInc,CurClutch)) {
						Cur.SetDir(DefaultDir);
						HoldRem = max(0,HoldRem-1);
						bool ff = false;
						if (Hold.size()==HoldLen) {
							for (int i=0; i+1<HoldLen; ++i) Hold[i]=Hold[i+1];
							Hold[HoldLen-1] = Cur;
							ff = true;
						}
						else {
							Hold.push_back(Cur);
							for (int i=0; i+1<Next.size(); ++i) Next[i]=Next[i+1];
							Next.pop_back(); ++DNext;
						}
						Spawn(NewP,HoldPosInc,-1,CurClutch); ResetRem=ResetLim; FallTime=0;
						FillNext();
						if (((SmoothLevel>=4)&&((!Bot)||(BotSpeed>=100)))) {
							if (ff) {
								PDx+=1-(SX-PosX); PDy+=(-4)-PosY;
							}
							else {
								PDx+=1-(SX-PosX); PDy+=(LY-1)-PosY;
							}
						}
					}
				}
			}
			if (o==A_Retry) Reset=true;
			if (o==A_Up) {
				if (ResetRem) {
					if (MoveUp()) {
						if (ResetLim<Max) --ResetRem;
					}
				}
			}
			if (o==A_Reset) {
				if (CheckSpawn(Cur)) {
					Spawn(Cur); ResetRem=ResetLim; FallTime=0;
				}
			}
			if (o==A_Flip) {
				ResetOper = Rotate(0,GetFlip(Cur));
				if (G20) {
					if (SonicDrop()) FallTime=0;
				}
			}
			if (o==A_Waste) Waste();
			if (o==A_Lock) Lock();
			if (o==A_Action) {
				if ((!Over)&&(Time>=0LL)&&(ZoneProgress>=ZoneReq)) {
					bool f = true;
					for (int i=1; i<=LX; ++i) {
						for (int j=1; j<=LY; ++j) {
							if ((B[i][j]==GarbZone)||(B[i][j]==-GarbZone)) f=false;
						}
					}
					if (f) {
						CurZone=max(1,min(ZoneSeg,(ZoneReq)?ZoneProgress/ZoneReq:ZoneSeg)); ZoneRem=ZoneTime*CurZone;
					}
				}
			}
			if (o==A_Undo) {
				if (HistoryPos+1<History.size()) ReadHistory(History[++HistoryPos]);
			}
			if (o==A_Redo) {
				if (HistoryPos>0) ReadHistory(History[--HistoryPos]);
			}
			if (o==A_CursorMode) {
				if (UsePlace) CursorMode^=1;
				else {
					CursorMode=false; CursorMoveLock^=1;
				}
			}
			if (o==A_TargetMode) {
				if (PlayerCount>1) {
					int v = int(TargetType);
					if ((v>=1)&&(v<=7)) {
						++v; if (v>7) v=1;
						TargetType = ETargetType(v);
					}
				}
				else PaintType=(PaintType+1)%9;
			}
			if ((ResetOper)&&((!CheckDown())||(!PreCDown))) {
				if (ResetRem>0) FallTime=0;
				if (ResetLim<Max) ResetRem=max(ResetRem-1, 0);
			}
		}
		if ((UsePlace)&&(CursorMode)&&(!Bot)&&(!Over)&&(SelX)&&(Time>=0LL)) {
			SelX = max(1,min(SX,SelX));
			SelY = max(1,min(LY,SelY));
			if (CheckKey(VK_LBUTTON)) {
				if (!PlaceUID) PlaceUID=NewUID();
				if (!C[SelX][SelY]) {
					B[SelX][SelY]=GetPaintColor(); Age[SelX][SelY]=500; U[SelX][SelY]=PlaceUID; 
				}
				else {
					bool f = false;
					if ((!C[SelX-1][SelY])&&(MoveUp())) f=true;
					else if ((!C[SelX][SelY+1])&&(MoveLeft())) f=true;
					else if ((!C[SelX][SelY-1])&&(MoveRight())) f=true;
					else if ((!C[SelX+1][SelY])&&(MoveDown())) f=true;
					if (f) {
						B[SelX][SelY]=GetPaintColor(); Age[SelX][SelY]=500; U[SelX][SelY]=PlaceUID;
					}
				}
			}
			else PlaceUID=0;
			if (CheckKey(VK_RBUTTON)) {
				if (!C[SelX][SelY]) B[SelX][SelY]=Age[SelX][SelY]=U[SelX][SelY]=0;
			}
		}
		if ((ForceLock)&&(!Pause)) HardDrop();
		if ((Active)&&(!CheckActive())&&(!Over)) PreAREProcess();
		for (int i=1; i<=LX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if (Age[i][j]<0) {
					if (InCascade) continue;
					else Age[i][j]=500;
				}
				Age[i][j] = max(Age[i][j]-TimeDel,0);
			}
		}
		if (Reset) ResetBoard();
		LineClearAge += TimeDel*(((LastClearType.second==LineClearText)||(!Mod8))?1:5);
		SpinAge += TimeDel*(((LastClearType.first==SpinText)||(!SpinGrade))?1:5);
		ComboAge += TimeDel*((ComboText)?1:5);
		B2BAge += TimeDel*((B2BText)?1:5);
		if (CurZone) ZoneRem=max(-1,ZoneRem-TimeDel);
		B2BBlinkRem = max(0,B2BBlinkRem-TimeDel);
		AllClearAge += TimeDel;
		LastLock += TimeDel;
		if (ComboRule==CR_C2) {
			C2ComboRem = max(0,C2ComboRem-TimeDel);
			if (!C2ComboRem) {
				if (Combo) Combo=ComboText=ComboAge=0;
			}
			else if (ComboText) ComboAge=max(0,3000-C2ComboRem/2);
		}
		else C2ComboRem=0;
		if (B2BText) B2BAge=min(B2BAge,1000);
		if (ShowParticle) {
			int tot = 0;
			vector<Particle> NewParticles;
			for (Particle o : Particles) {
				o.Update(TimeDel);
				if ((o.sz>=0.001)&&(abs(o.x)<=LX*2.0)&&(abs(o.y)<=LY*2.0)) NewParticles.push_back(o);
				if (++tot>=1000) break;
			}
			Particles = NewParticles;
			vector<pair<Particle,int> > NewDelayParticles;
			tot = 0;
			for (pair<Particle,int> &o : DelayParticles) {
				o.second = max(0,o.second-TimeDel);
				if (o.second<=0) Particles.push_back(o.first);
				else NewDelayParticles.push_back(o);
				if (++tot>=1000) break;
			}
			DelayParticles = NewDelayParticles;
			vector<FloatText> NewFloatTexts;
			tot = 0;
			for (FloatText &o : FloatTexts) {
				o.Update(TimeDel);
				o.Update(TimeDel);
				if ((o.sz>=0.001)&&(o.bd>=0.001)&&(abs(o.x)<=LX*2.0)&&(abs(o.y)<=LY*2.0)) NewFloatTexts.push_back(o);
				if (++tot>=1000) break;
			}
			FloatTexts = NewFloatTexts;
		}
		else {
			Particles.clear(); DelayParticles.clear(); FloatTexts.clear();
		}
		if (TScore<=DScore) DScore=TScore;
		else {
			ll del = max(200LL,TScore-DScore)*ll(TimeDel)/200LL;
			DScore = min(TScore, DScore+del);
		}
		if (TQPScore<=DQPScore) DQPScore=TQPScore;
		else {
			ll del = max(200LL,TQPScore-DQPScore)*ll(TimeDel)/200LL;
			DQPScore = min(TQPScore, DQPScore+del);
		}
		DecD(PDth,TimeDel); DecD(PDx,TimeDel); DecD(PDy,TimeDel); DecD(DNext,TimeDel,0.5);
		K-=tK; PX-=tPX; PY-=tPY;
		DecD(K,RealTimeDel); DecD(PX,RealTimeDel); DecD(PY,RealTimeDel);
		K+=tK; PX+=tPX; PY+=tPY;
		if (GarbageActive==GA_Instant) ActivateGarbage();
		if (PushCD) {
			int val = min(TimeDel,PushCD);
			PushCD -= val;
			if ((!Bot)&&(CheckDown())&&(DropDelay>0)&&(GarbagePush)&&(SmoothLevel>=1))
				FallTime+=int(min(ll(DropDelay),ll(val)*ll(DropDelay)/ll(GarbagePush)));
		}
		if ((AttackIn.size())&&(AttackIn[0].Delay<0)&&((!CurZone)||(!ZoneReq))&&(!PushCD)) {
			AddGarbage(AttackIn[0]);
			for (int i=1; i<AttackIn.size(); ++i) AttackIn[i-1]=AttackIn[i];
			PushCD=GarbagePush; AttackIn.pop_back();
		}
		if ((!PushCD)&&(StaticGarbHeight)) {
			int h = ((GarbageType==GT_Bricks)?GetHeight():GetGarbHeight())-((GarbageType==GT_Solid)?0:GetRiseHeight());
			if (h<StaticGarbHeight) {
				if (randf()*1000.0<SegMess) RerollHole();
				AddGarbage(RollGarbage()); PushCD=GarbagePush;
			}
		}
		Paint();
		for (GarbageLayer &o : AttackIn) {
			if (o.Delay>=0) o.Delay=max(0,o.Delay-TimeDel);
		}
		InList.clear(); InList.insert(Cur.GetHash());
		for (Piece &o : Next) InList.insert(o.GetHash());
		for (Piece &o : Hold) InList.insert(o.GetHash());
		for (Piece &o : Pool) InList.insert(o.GetHash());
		for (Piece &o : Gen.Pool) InList.insert(o.GetHash());
		for (Piece &o : Gen.RemPool) InList.insert(o.GetHash());
		if (UseUndo) {
			for (BoardHistory &o : History) o.MarkIn();
		}
		vector<Piece*> NewCustomPiece;
		for (Piece *p : CustomPiece) {
			if (InList.count(p->GetHash())) NewCustomPiece.push_back(p);
			else delete p;
		}
		CustomPiece = NewCustomPiece;
		if ((!Pause)&&(StartMsg>=0)&&(InactiveTime<=1000*StartMsg)) {
			AddFloatText((StartMsg)?IToS(StartMsg):"GO", 0xDDDDDD, 3.5,
			0.2, 0.15, 0.3, SX*0.5-2, LY*0.5-0.7, 0, 0, 0, 0);
			AddFloatText((StartMsg)?IToS(StartMsg):"GO", 0xFFE211, 3.5,
			0.2, 0.14, 0.3, SX*0.5-2, LY*0.5-0.7, 0, 0, 0, 0);
			--StartMsg;
		}
	}
	int GetAIUrgency(int TimeDel) {
		if ((!Bot)||(Over)||(Pause)||(!PendingActions.empty())) return -1;
		if (ActionTime+TimeDel<BotSpeed) return -1;
		if (InactiveTime>0) return -1;
		if (ActionCD>0) return -1;
		if ((!BoardID)&&(QPMode)) return 100000002;
		if (PlayerCount<=2) return 100000002;
		if (SearchStep!=-1) return 100000001;
		return min(10000000,ActionTime+TimeDel-BotSpeed);
	}
};
vector<pair<int,int> > Board::TmpKick[4][4];
const double DefaultScale = 30.0;
ll Time = 0LL;
vector<Board*> Boards;
vector<vector<Board*> > Targets, TargetFrom;
void ArrangeBoards(int Obs=-1) {
	int n=Boards.size(), an=0;
	if (n==1) {
		for (Board *p : Boards) p->SetScale(0.0,DefaultScale*0.5,DefaultScale);
		return;
	}
	if ((QPMode)&&((Obs>=0)&&(Obs<n))) {
		for (int i=0; i<n; ++i) {
			if (i==Obs) Boards[i]->SetScale(0.0,DefaultScale*0.5,DefaultScale);
			else Boards[i]->SetScale(0.0,0.0,0.0001);
		}
		return;
	}
	vector<Board*> SelectBoards; int sn=0;
	for (Board *p : Boards) {
		if (!p->IsOver()) ++an;
		if ((p->IsOver())&&(p->GetBoardID()!=Obs)&&(p->IsBot())) p->SetScale(0.0,0.0,0.0001);
		else {
			SelectBoards.push_back(p); ++sn;
		}
	}
	if (an<2) {