1 /*
2 ===========================================================================
3 blockattack - Block Attack - Rise of the Blocks
4 Copyright (C) 2005-2012 Poul Sander
5 
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see http://www.gnu.org/licenses/
18 
19 Source information and contacts persons can be found at
20 http://blockattack.net
21 ===========================================================================
22 */
23 
24 //Some definitions
25 //The game is divided in frames. FALLTIME means the blocks will fall one block every FRAMELENGTH*FALLTIME millisecond
26 #define FRAMELENGTH 50
27 #define HANGTIME 40
28 #define FALLTIME 20
29 //Don't change the following, they are fundamental and later some functions are hardcoded
30 #define BLOCKFALL 10000
31 #define GARBAGE 1000000
32 #define CHAINPLACE 10000000
33 
34 // The game uses a very special  base-10 pack system
35 // int  999999999999
36 //      YYYYYGWBHTTC
37 // YYYY = Chain
38 // G = Garbage, 1= NORMAL, 2=GRAY
39 // W = Waiting (A bomb is on it)
40 // B = 1 if the block is falling
41 // H = Block is hanging after garbage (Get Ready text on it)
42 // TT = Time (in steps) until something happens
43 // C = color
44 
block_isFalling(int block)45 static bool block_isFalling(int block) {
46 	return (block/BLOCKFALL)%10;
47 }
48 
block_setFalling(int & block,bool value)49 static void block_setFalling(int& block, bool value) {
50 	if (value) {
51 		if (!block_isFalling(block)) {
52 			block += BLOCKFALL;
53 		}
54 	}
55 	else {
56 		if (block_isFalling(block)) {
57 			block -= BLOCKFALL;
58 		}
59 	}
60 }
61 
62 #include "BlockGame.hpp"
63 #include "puzzlehandler.hpp"
64 #include "stageclearhandler.hpp"
65 #include <sstream>
66 #include <deque>
67 
68 
69 //Function to convert numbers to string (2 diget)
itoa2(int num)70 static std::string itoa2(int num) {
71 	char buffer[20];
72 	snprintf(buffer, sizeof(buffer), "%02i", num);
73 	return buffer;
74 }
75 
76 /**
77  * This function tells how long the game may pause the rise of the blocks.
78  * This decreases as the game progresses.
79  * @param stops In out variable. If larger than the max allowed
80  * @param ticks Ticks since the game was started
81  * @param gameStartedAt Tick the game was started at.
82  */
capMaxStops(int & stops,unsigned int ticks,unsigned int gameStartedAt)83 static void capMaxStops(int& stops, unsigned int ticks, unsigned int gameStartedAt) {
84 	if (ticks < gameStartedAt || stops == 0) {
85 		//Game not started or not stops, do nothing
86 		return;
87 	}
88 	unsigned int gameTime = gameStartedAt - ticks;
89 
90 	int maxSeconds = 10-( (gameTime/1000) % 30 );
91 	if (maxSeconds < 3) {
92 		maxSeconds = 1;
93 	}
94 	int cappedStops = maxSeconds * 1000;
95 	if (stops > cappedStops) {
96 		stops = cappedStops;
97 	}
98 }
99 
100 stageButton stageButtonStatus;
101 
102 static std::stringstream ss; //Used for internal formatting
103 
104 ////////////////////////////////////////////////////////////////////////////////
105 //The BloackGame class represents a board, score, time etc. for a single player/
106 ////////////////////////////////////////////////////////////////////////////////
rand2()107 int BlockGame::rand2() {
108 	nextRandomNumber = nextRandomNumber*1103515245 + 12345;
109 	return ((int)(nextRandomNumber/65536)) % 32768;
110 }
111 
firstUnusedChain()112 int BlockGame::firstUnusedChain() {
113 	bool found=false;
114 	int i = 0;
115 	while (!found) {
116 		if (!chainUsed[++i]) {
117 			found=true;
118 		}
119 		if (i>NUMBEROFCHAINS-2) {
120 			found=true;
121 		}
122 	}
123 	return i;
124 }
125 
126 //Constructor
BlockGame()127 BlockGame::BlockGame() {
128 	AI_MoveSpeed=100;
129 	baseSpeed = 0.5;           //All other speeds are relative to this
130 	speed = baseSpeed;
131 	speedLevel = 1;
132 	ticks = 0;
133 	gameStartedAt = ticks;
134 	gameEndedAfter = 0;
135 	pushedPixelAt = gameStartedAt;
136 	nextGarbageNumber = 10;
137 	handicap=0;
138 	for (int i=0; i<7; i++) {
139 		for (int j=0; j<30; j++) {
140 			board[i][j] = -1;
141 		}
142 	}
143 	lastCounter = -1;           //To prevent the final chunk to be played when stating the program
144 }   //Constructor
145 
146 
setGameSpeed(int globalSpeedLevel)147 void BlockGame::setGameSpeed(int globalSpeedLevel) {
148 	switch (globalSpeedLevel) {
149 	case 0:
150 		baseSpeed=0.5;
151 		break;
152 	case 1:
153 		baseSpeed=0.1;
154 		break;
155 	case 2:
156 		baseSpeed=0.07;
157 		break;
158 	case 3:
159 		baseSpeed=0.04;
160 		break;
161 	case 4:
162 		baseSpeed=0.015;
163 		break;
164 	default:
165 		baseSpeed=0.005;
166 		break;
167 	};
168 }
169 
setHandicap(int globalHandicap)170 void BlockGame::setHandicap(int globalHandicap) {
171 	handicap=1000*((int)globalHandicap);
172 }
173 
174 //Set the move speed of the AI based on the aiLevel parameter
175 //Also enables AI
setAIlevel(int aiLevel)176 void BlockGame::setAIlevel(int aiLevel) {
177 	AI_MoveSpeed=120-(20*(aiLevel-3));
178 };
179 
getAIlevel() const180 int BlockGame::getAIlevel() const {
181 	return (120-AI_MoveSpeed)/20+3;
182 }
183 
GetScore() const184 int BlockGame::GetScore() const {
185 	return score;
186 }
187 
GetHandicap() const188 int BlockGame::GetHandicap() const {
189 	return handicap;
190 }
191 
isGameOver() const192 bool BlockGame::isGameOver() const {
193 	return bGameOver;
194 }
195 
GetGameStartedAt() const196 int BlockGame::GetGameStartedAt() const {
197 	return gameStartedAt;
198 }
199 
GetGameEndedAt() const200 int BlockGame::GetGameEndedAt() const {
201 	return gameEndedAfter;
202 }
203 
isTimeTrial() const204 bool BlockGame::isTimeTrial() const {
205 	return timetrial;
206 }
207 
isStageClear() const208 bool BlockGame::isStageClear() const {
209 	return stageClear;
210 }
211 
isVsMode() const212 bool BlockGame::isVsMode() const {
213 	return vsMode;
214 }
215 
isPuzzleMode() const216 bool BlockGame::isPuzzleMode() const {
217 	return puzzleMode;
218 }
219 
GetLinesCleared() const220 int BlockGame::GetLinesCleared() const {
221 	return linesCleared;
222 }
223 
GetStageClearLimit() const224 int BlockGame::GetStageClearLimit() const {
225 	return stageClearLimit;
226 }
227 
GetChains() const228 int BlockGame::GetChains() const {
229 	return chain;
230 }
231 
GetPixels() const232 int BlockGame::GetPixels() const {
233 	return pixels;
234 }
235 
GetSpeedLevel() const236 int BlockGame::GetSpeedLevel() const {
237 	return speedLevel;
238 }
239 
GetTowerHeight() const240 int BlockGame::GetTowerHeight() const {
241 	return TowerHeight;
242 }
243 
GetCursorX() const244 int BlockGame::GetCursorX() const {
245 	return cursorx;
246 }
247 
GetCursorY() const248 int BlockGame::GetCursorY() const {
249 	return cursory;
250 }
251 
MoveCursorTo(int x,int y)252 void BlockGame::MoveCursorTo(int x, int y) {
253 	cursorx = x;
254 	cursory = y;
255 }
256 
GetIsWinner() const257 bool BlockGame::GetIsWinner()  const {
258 	return hasWonTheGame;
259 }
260 
NewGame(const BlockGameStartInfo & s)261 void BlockGame::NewGame(const BlockGameStartInfo& s) {
262 	replayInfo.startInfo = s;
263 	replayInfo.actions.clear();
264 	this->recordStats = s.recordStats;
265 	if (s.AI) {
266 		recordStats = false;
267 	}
268 	NewGameInternal(s.ticks);
269 	if (s.timeTrial) {
270 		timetrial = true;
271 		putStartBlocks();
272 	}
273 	if (s.stageClear) {
274 		if (s.level > -1) {
275 			stageClear = true;
276 			Level = s.level;
277 			Stats::getInstance()->addOne("PlayedStageLevel"+itoa2(s.level));
278 			stageClearLimit = 30+(Level%6)*10;
279 			baseSpeed = 0.5/((double)(Level*0.5)+1.0);
280 			speed = baseSpeed;
281 		}
282 	}
283 	if (s.puzzleMode) {
284 		if (s.level>-1) {
285 			puzzleMode = true;
286 			Level = s.level;
287 			MovesLeft = PuzzleNumberOfMovesAllowed(Level);
288 			for (int i=0; i<6; i++) {
289 				for (int j=0; j<12; j++) {
290 					board[i][j+1] = PuzzleGetBrick(Level,i,j);
291 				}
292 			}
293 			baseSpeed = 100000;
294 			speed = 100000;
295 
296 			//Now push the blines up
297 			for (int i=19; i>0; i--) {
298 				for (int j=0; j<6; j++) {
299 					board[j][i] = board[j][i-1];
300 				}
301 			}
302 			for (int j=0; j<6; j++) {
303 				board[j][0] = 5;
304 			}
305 		}
306 		this->singlePuzzle = s.singlePuzzle;
307 	}
308 	if (s.vsMode) {
309 		vsMode = true;
310 		AI_Enabled = s.AI;
311 		if (recordStats) {
312 			Stats::getInstance()->addOne("VSgamesStarted");
313 		}
314 		if (s.vsAI) {
315 			setAIlevel(s.level);
316 			vsAI = true;
317 		}
318 		if (AI_Enabled) {
319 			name = "CPU";
320 			setAIlevel(s.level);
321 		}
322 		putStartBlocks();
323 	}
324 	if (s.startBlocks >= 0) {
325 		putStartBlocks(s.startBlocks);
326 	}
327 	if (s.handicap > 0) {
328 		setHandicap(s.handicap);
329 	}
330 	if (s.gameSpeed > 0) {
331 		setGameSpeed(s.gameSpeed);
332 		speed = baseSpeed;
333 	}
334 }
335 
336 //Instead of creating new object new game is called, to prevent memory leaks
NewGameInternal(unsigned int ticks)337 void BlockGame::NewGameInternal( unsigned int ticks) {
338 	this->ticks = ticks;
339 	stageButtonStatus = SBdontShow;
340 	nrFellDown = 0;
341 	nrPushedPixel = 0;
342 	nrStops = 0;
343 	cursorx = 2;
344 	cursory = 3;
345 	stop = 0;
346 	pixels = 0;
347 	score = 0;
348 	bGameOver = false;
349 	bDraw = false;
350 	timetrial = false;
351 	stageClear = false;
352 	linesCleared = 0;
353 	hasWonTheGame = false;
354 	vsMode = false;
355 	puzzleMode = false;
356 	combo=0;
357 	chain=0;
358 	AI_Enabled = false;
359 	baseSpeed= 0.5;
360 	speed = baseSpeed;
361 	speedLevel = 1;
362 	gameStartedAt = ticks+3000;
363 	pushedPixelAt = gameStartedAt;
364 	nextGarbageNumber = 10;
365 	handicap=0;
366 	for (int i=0; i<7; i++) {
367 		for (int j=0; j<30; j++) {
368 			board[i][j] = -1;
369 		}
370 	}
371 	for (int i=0; i<NUMBEROFCHAINS; i++) {
372 		chainUsed[i]=false;
373 		chainSize[i] = 0;
374 	}
375 	lastAImove = ticks+3000;
376 }   //NewGame
377 
FinalizeBlockGameInfo()378 void BlockGame::FinalizeBlockGameInfo() {
379 	replayInfo.extra.name = name;
380 	replayInfo.extra.score = score;
381 	replayInfo.extra.seconds = gameEndedAfter;
382 }
383 
384 //Prints "winner" and ends game
setPlayerWon()385 void BlockGame::setPlayerWon() {
386 	if (!bGameOver || !hasWonTheGame) {
387 		gameEndedAfter = ticks-gameStartedAt; //We game ends now!
388 		if (recordStats) {
389 			TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter));
390 		}
391 		bGameOver = true;
392 		PlayerWonEvent();
393 		if (recordStats) {
394 			Stats::getInstance()->addOne("totalWins");
395 			if (vsAI) {
396 				//We have defeated an AI
397 				Stats::getInstance()->addOne("defeatedAI"+std::to_string(getAIlevel()));
398 			}
399 		}
400 		if (AI_Enabled && vsAI) {
401 			//The AI have defeated a human player
402 			Stats::getInstance()->addOne("defeatedByAI"+std::to_string(getAIlevel()));
403 		}
404 		FinalizeBlockGameInfo();
405 	}
406 	hasWonTheGame = true;
407 }
408 
409 //Prints "draw" and ends the game
setDraw()410 void BlockGame::setDraw() {
411 	bGameOver = true;
412 	if (recordStats) {
413 		TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter));
414 	}
415 	hasWonTheGame = false;
416 	bDraw = true;
417 	DrawEvent();
418 	if (recordStats) {
419 		Stats::getInstance()->addOne("totalDraws");
420 	}
421 	FinalizeBlockGameInfo();
422 }
423 
424 
425 //Test if LineNr is an empty line, returns false otherwise.
LineEmpty(int lineNr) const426 bool BlockGame::LineEmpty(int lineNr) const {
427 	bool empty = true;
428 	for (int i = 0; i <7; i++) {
429 		if (board[i][lineNr] != -1) {
430 			empty = false;
431 		}
432 	}
433 	return empty;
434 }
435 
436 //Test if the entire board is empty (used for Puzzles)
BoardEmpty() const437 bool BlockGame::BoardEmpty() const {
438 	bool empty = true;
439 	for (int i=0; i<6; i++) {
440 		for (int j=1; j<13; j++) {
441 			if (board[i][j] != -1) {
442 				empty = false;
443 			}
444 		}
445 	}
446 	return empty;
447 }
448 
449 //Anything that the user can't move? In that case Game Over cannot occur
hasStaticContent() const450 bool BlockGame::hasStaticContent() const {
451 	for (int i=0; i<6; i++) {
452 		for (int j=1; j<13; j++) {
453 			if (board[i][j] >= 10000000) {  //Higher than this means combos (garbage is static, but the stack is static but nothing to do about it)
454 				return true;    //They are static
455 			}
456 		}
457 	}
458 	return false;                   //Return false if no static object found
459 }
460 
putStartBlocks()461 void BlockGame::putStartBlocks() {
462 	putStartBlocks(time(0));
463 }
464 
putStartBlocks(int n)465 void BlockGame::putStartBlocks(int n) {
466 	for (int i=0; i<7; i++) {
467 		for (int j=0; j<30; j++) {
468 			board[i][j] = -1;
469 		}
470 	}
471 	nextRandomNumber = n;
472 	int choice = rand2()%3; //Pick a random layout
473 	switch (choice) {
474 	case 0:
475 		//row 0:
476 		board[0][0]=1;
477 		board[1][0]=0;
478 		board[2][0]=4;
479 		board[3][0]=3;
480 		board[4][0]=3;
481 		board[5][0]=5;
482 		//row 1:
483 		board[0][1]=1;
484 		board[1][1]=4;
485 		board[2][1]=2;
486 		board[3][1]=0;
487 		board[4][1]=4;
488 		board[5][1]=5;
489 		//row 2:
490 		board[0][2]=2;
491 		board[1][2]=3;
492 		board[2][2]=0;
493 		board[3][2]=4;
494 		board[4][2]=1;
495 		board[5][2]=1;
496 		//row 3:
497 		board[0][3]=3;
498 		board[1][3]=2;
499 		board[2][3]=3;
500 		board[3][3]=1;
501 		board[4][3]=0;
502 		board[5][3]=4;
503 		//row 4:
504 		board[0][4]=2;
505 		board[1][4]=3;
506 		board[2][4]=3;
507 		board[3][4]=1;
508 		board[4][4]=4;
509 		board[5][4]=0;
510 		//row 5:
511 		board[0][5]=-1;
512 		board[1][5]=5;
513 		board[2][5]=5;
514 		board[3][5]=-1;
515 		board[4][5]=1;
516 		board[5][5]=-1;
517 		break;
518 	case 1:
519 		//row 0:
520 		board[0][0]=3;
521 		board[1][0]=5;
522 		board[2][0]=0;
523 		board[3][0]=0;
524 		board[4][0]=4;
525 		board[5][0]=2;
526 		//row 1:
527 		board[0][1]=3;
528 		board[1][1]=5;
529 		board[2][1]=-1;
530 		board[3][1]=5;
531 		board[4][1]=4;
532 		board[5][1]=2;
533 		//row 2:
534 		board[0][2]=2;
535 		board[1][2]=-1;
536 		board[2][2]=-1;
537 		board[3][2]=4;
538 		board[4][2]=0;
539 		board[5][2]=3;
540 		//row 3:
541 		board[0][3]=2;
542 		board[5][3]=3;
543 		break;
544 	default:
545 		//row 0:
546 		board[0][0]=4;
547 		board[1][0]=5;
548 		board[2][0]=2;
549 		board[3][0]=0;
550 		board[4][0]=1;
551 		board[5][0]=5;
552 		//row 1:
553 		board[0][1]=4;
554 		board[1][1]=5;
555 		board[2][1]=2;
556 		board[3][1]=1;
557 		board[4][1]=0;
558 		board[5][1]=2;
559 		//row 2:
560 		board[0][2]=2;
561 		board[1][2]=4;
562 		board[2][2]=-1;
563 		board[3][2]=0;
564 		board[4][2]=1;
565 		board[5][2]=5;
566 		//row 3:
567 		board[0][3]=4;
568 		board[1][3]=2;
569 		board[2][3]=-1;
570 		board[3][3]=1;
571 		board[4][3]=0;
572 		board[5][3]=2;
573 		//row 4:
574 		board[0][4]=4;
575 		board[1][4]=2;
576 		board[2][4]=-1;
577 		board[3][4]=0;
578 		board[4][4]=1;
579 		board[5][4]=-1;
580 		break;
581 	};
582 }
583 
584 //decreases hang for all hanging blocks and wait for waiting blocks
ReduceStuff()585 void BlockGame::ReduceStuff() {
586 	int howMuchHang = (ticks - FRAMELENGTH*hangTicks)/FRAMELENGTH;
587 	if (howMuchHang>0) {
588 		for (int i=0; i<7; i++) {
589 			for (int j=0; j<30; j++) {
590 				if ((board[i][j]/BLOCKHANG)%10==1) {
591 					int hangNumber = (board[i][j]/10)%100;
592 					if (hangNumber<=howMuchHang) {
593 						board[i][j]-=BLOCKHANG;
594 						board[i][j]-=hangNumber*10;
595 					}
596 					else {
597 						board[i][j]-=10*howMuchHang;
598 					}
599 				}
600 				if ((board[i][j]/BLOCKWAIT)%10==1) {
601 					int hangNumber = (board[i][j]/10)%100;
602 					if (hangNumber<=howMuchHang) {
603 						//The blocks must be cleared
604 						board[i][j]-=hangNumber*10;
605 					}
606 					else {
607 						board[i][j]-=10*howMuchHang;
608 					}
609 				}
610 			}
611 		}
612 	}
613 	hangTicks+=howMuchHang;
614 }
615 
616 //Creates garbage using a given wide and height
CreateGarbage(int wide,int height)617 bool BlockGame::CreateGarbage(int wide, int height) {
618 	{
619 		if (wide>6) {
620 			wide = 6;
621 		}
622 		if (height>12) {
623 			height = 12;
624 		}
625 		int startPosition = 12;
626 		while ( startPosition <= 29 && !LineEmpty(startPosition)) {
627 			startPosition++;
628 		}
629 		if (startPosition >= 29) {
630 			return false;    //failed to place blocks
631 		}
632 		if (29-startPosition<height) {
633 			return false;    //not enough space
634 		}
635 		int start = 0;
636 		int end = 6;
637 		if (bGarbageFallLeft) {
638 			start=0;
639 			end=start+wide;
640 			bGarbageFallLeft = false;
641 		}
642 		else {
643 			start=6-wide;
644 			end = 6;
645 			bGarbageFallLeft = true;
646 		}
647 		for (int i = startPosition; i <startPosition+height; i++) {
648 			for (int j = start; j < end; j++) {
649 				board[j][i] = 1000000+nextGarbageNumber;
650 			}
651 		}
652 		nextGarbageNumber++;
653 		if (nextGarbageNumber>999999) {
654 			nextGarbageNumber = 10;
655 		}
656 		return true;
657 	}
658 	return false;
659 }
660 
661 //Creates garbage using a given wide and height
CreateGreyGarbage()662 bool BlockGame::CreateGreyGarbage() {
663 	int startPosition = 12;
664 	while ( startPosition <= 29 && !LineEmpty(startPosition) ) {
665 		startPosition++;
666 	}
667 	if (startPosition >= 29) {
668 		return false;    //failed to place blocks
669 	}
670 	int start = 0;
671 	int end = 6;
672 	for (int i = startPosition; i <startPosition+1; i++) {
673 		for (int j = start; j < end; j++) {
674 			board[j][i] = 2*1000000+nextGarbageNumber;
675 		}
676 	}
677 	nextGarbageNumber++;
678 	if (nextGarbageNumber>999999) {
679 		nextGarbageNumber = 10;
680 	}
681 	return true;
682 }
683 
684 
685 //Clears garbage, must take one the lower left corner!
GarbageClearer(int x,int y,int number,bool aLineToClear,int chain)686 int BlockGame::GarbageClearer(int x, int y, int number, bool aLineToClear, int chain) {
687 	if ((board[x][y])%1000000 != number) {
688 		return -1;
689 	}
690 	if (aLineToClear) {
691 		board[x][y] = this->rand2() % 6;
692 		board[x][y] += 10*HANGTIME+BLOCKHANG+CHAINPLACE*chain;
693 	}
694 	garbageToBeCleared[x][y] = false;
695 	GarbageClearer(x+1, y, number, aLineToClear, chain);
696 	GarbageClearer(x, y+1, number, false, chain);
697 	return 1;
698 }
699 
700 //Marks garbage that must be cleared
GarbageMarker(int x,int y)701 int BlockGame::GarbageMarker(int x, int y) {
702 	if ((x>6)||(x<0)||(y<0)||(y>29)) {
703 		return -1;
704 	}
705 	if (((board[x][y])/1000000 == 1)&&(garbageToBeCleared[x][y] == false)) {
706 		garbageToBeCleared[x][y] = true;
707 		//Float fill
708 		GarbageMarker(x-1, y);
709 		GarbageMarker(x+1, y);
710 		GarbageMarker(x, y-1);
711 		GarbageMarker(x, y+1);
712 	}
713 	return 1;
714 }
715 
FirstGarbageMarker(int x,int y)716 int BlockGame::FirstGarbageMarker(int x, int y) {
717 	if ((x>6)||(x<0)||(y<0)||(y>29)) {
718 		return -1;
719 	}
720 	if (((board[x][y])/1000000 == 2)&&(garbageToBeCleared[x][y] == false)) {
721 		for (int i=0; i<6; i++) {
722 			garbageToBeCleared[i][y] = true;
723 		}
724 	}
725 	else if (((board[x][y])/1000000 == 1)&&(garbageToBeCleared[x][y] == false)) {
726 		garbageToBeCleared[x][y] = true;
727 		//Float fill
728 		GarbageMarker(x-1, y);
729 		GarbageMarker(x+1, y);
730 		GarbageMarker(x, y-1);
731 		GarbageMarker(x, y+1);
732 	}
733 	return 1;
734 }
735 
736 //Clear Blocks if 3 or more is alligned (naive implemented)
ClearBlocks()737 void BlockGame::ClearBlocks() {
738 
739 	bool toBeCleared[7][30]; //true if blok must be removed
740 
741 	int previus=-1; //the last block checked
742 	int combo=0;
743 	for (int i=0; i<30; i++)
744 		for (int j=0; j<7; j++) {
745 			toBeCleared[j][i] = false;
746 			garbageToBeCleared[j][i] = false;
747 		}
748 	for (int i=0; i<7; i++) {
749 		bool faaling = false;
750 		for (int j=0; j<30; j++) {
751 			if ((faaling)&&(board[i][j]>-1)&&(board[i][j]%10000000<7)) {
752 				block_setFalling(board[i][j], true);
753 			}
754 			if ((!faaling)&&((board[i][j]/BLOCKFALL)%10==1)) {
755 				block_setFalling(board[i][j], false);
756 			}
757 			if (!((board[i][j]>-1)&&(board[i][j]%10000000<7))) {
758 				faaling=true;
759 			}
760 			if (((board[i][j]/1000000)%10==1)||((board[i][j]/1000000)%10==2)||((board[i][j]/BLOCKHANG)%10==1)||((board[i][j]/BLOCKWAIT)%10==1)) {
761 				faaling = false;
762 			}
763 		}
764 	}
765 
766 
767 	for (int j=0; j<7; j++) {
768 		previus = -1;
769 		combo=0;
770 
771 		for (int i=1; i<30; i++) {
772 			if ((board[j][i]>-1)&&(board[j][i]%10000000<7)) {
773 				if (board[j][i]%10000000 == previus) {
774 					combo++;
775 				}
776 				else {
777 					if (combo>2) {
778 						for (int k = i-combo; k<i; k++) {
779 							toBeCleared[j][k] = true;
780 						}
781 					}
782 					combo=1;
783 					previus = board[j][i]%10000000;
784 				}
785 			} //if board
786 			else {
787 				if (combo>2) {
788 					for (int k = i-combo; k<i; k++) {
789 						toBeCleared[j][k] = true;
790 					}
791 				}
792 				combo = 0;
793 				previus = -1;
794 			}
795 
796 		} //for i
797 	} //for j
798 
799 	chain = 0;
800 	for (int i=0; i<6; i++) {
801 		for (int j=0; j<30; j++) {
802 			//Clears blocks marked for clearing
803 			int temp=board[i][j];
804 			if (1==((temp/BLOCKWAIT)%10)) {
805 				if (((temp/10)%100)==0) {
806 					if (chainSize[chain]<chainSize[board[i][j]/10000000]) {
807 						chain = board[i][j]/10000000;
808 					}
809 
810 					AddBall(i, j, true, board[i][j]%10);
811 					AddBall(i, j, false, board[i][j]%10);
812 					AddExplosion(i, j);
813 					board[i][j]=-2;
814 				}
815 			}
816 		}
817 	}
818 	for (int i=0; i<7; i++) {
819 		bool setChain=false;
820 		for (int j=0; j<30; j++) {
821 			if (board[i][j]==-1) {
822 				setChain=false;
823 			}
824 			if (board[i][j]==-2) {
825 				board[i][j]=-1;
826 				setChain=true;
827 				BlockPopEvent();
828 			}
829 			if (board[i][j]!=-1) {
830 				if ((setChain)&&((board[i][j]/GARBAGE)%10!=1)&&((board[i][j]/GARBAGE)%10!=2)) {
831 					board[i][j]=((board[i][j]%CHAINPLACE)+CHAINPLACE*chain);
832 				}
833 			}
834 		}
835 	}
836 	int startvalue;
837 	if (pixels == 0) {
838 		startvalue=1;
839 	}
840 	else {
841 		startvalue=0;
842 	}
843 	for (int i=startvalue; i<30; i++) {
844 		previus=-1;
845 		combo=0;
846 		for (int j=0; j<7; j++) {
847 			if (((board[j][i]>-1)&&(board[j][i]%10000000<7))) {
848 				if (board[j][i]%10000000 == previus) {
849 					combo++;
850 				}
851 				else {
852 					if (combo>2) {
853 						for (int k = j-combo; k<j; k++) {
854 							toBeCleared[k][i] = true;
855 						}
856 					}
857 					combo=1;
858 					previus = board[j][i]%10000000;
859 				}
860 			} //if board
861 			else {
862 				if (combo>2) {
863 					for (int k = j-combo; k<j; k++) {
864 						toBeCleared[k][i] = true;
865 					}
866 				}
867 				combo = 0;
868 				previus = -1;
869 			}
870 
871 		} //for j
872 	} //for i
873 
874 	combo = 0;
875 	chain = 0;
876 	int grey = 0;
877 	for (int i=0; i<30; i++) {
878 		for (int j=0; j<6; j++) {
879 			if (toBeCleared[j][i]) {
880 				//see if any garbage is around:
881 				FirstGarbageMarker(j-1, i);
882 				FirstGarbageMarker(j+1, i);
883 				FirstGarbageMarker(j, i-1);
884 				FirstGarbageMarker(j, i+1);
885 				//that is checked now :-)
886 				if (board[j][i]%10000000==6) {
887 					grey++;
888 				}
889 				if ((vsMode) && (grey>2) && (board[j][i]%10000000==6)) {
890 					GarbageStruct s;
891 					s.setGarbage(6, 1, true);
892 					this->garbageSendQueue.push_back(s);
893 				}
894 				if ((board[j][i]>-1)&&(board[j][i]%10000000<7)) {
895 					board[j][i]+=BLOCKWAIT+10*FALLTIME;
896 				}
897 
898 				if (chainSize[board[j][i]/10000000]>chainSize[chain]) {
899 					chain=board[j][i]/10000000;
900 				}
901 				combo++;
902 				stop+=140*combo;
903 				score +=10;
904 				if (combo>3) {
905 					score+=3*combo;    //More points if more cleared simontanously
906 				}
907 			}
908 		}
909 	}
910 	score+=chainSize[chain]*100;
911 	if (chain==0) {
912 		chain=firstUnusedChain();
913 		chainSize[chain]=0;
914 		chainUsed[chain]=true;
915 	}
916 	chainSize[chain]++;
917 	for (int i=0; i<30; i++) {
918 		for (int j=0; j<6; j++) {
919 			if (toBeCleared[j][i]) {
920 				board[j][i]=(board[j][i]%10000000)+chain*10000000;
921 			}
922 		}
923 	}
924 
925 	{
926 		//This is here we add text to screen!
927 		bool dead = false;
928 		for (int i=29; i>=0; i--) {
929 			for (int j=0; j<6; j++) {
930 				if (toBeCleared[j][i]) {
931 					if (!dead) {
932 						dead=true;
933 						if (chainSize[chain]>1) {
934 							AddText(j, i, chainSize[chain], 1000);
935 						}
936 					}
937 				}
938 			}
939 		}
940 	} //This was there text was added
941 
942 	if (vsMode) {
943 		GarbageStruct s;
944 		switch (combo) {
945 		case 0:
946 		case 1:
947 		case 2:
948 		case 3:
949 			break;
950 		case 4:
951 			s.setGarbage(3,1);
952 			break;
953 		case 5:
954 			s.setGarbage(4,1);
955 			break;
956 		case 6:
957 			s.setGarbage(5,1);
958 			break;
959 		case 7:
960 			s.setGarbage(6,1);
961 			break;
962 		case 8:
963 			s.setGarbage(4,1);
964 			s.setGarbage(4,1);
965 			break;
966 		case 9:
967 			s.setGarbage(5,1);
968 			s.setGarbage(4,1);
969 			break;
970 		case 10:
971 			s.setGarbage(5,1);
972 			s.setGarbage(5,1);
973 			break;
974 		case 11:
975 			s.setGarbage(6,1);
976 			s.setGarbage(5,1);
977 			break;
978 		case 12:
979 			s.setGarbage(6,1);
980 			s.setGarbage(6,1);
981 			break;
982 		case 13:
983 			s.setGarbage(5,1);
984 			s.setGarbage(5,1);
985 			s.setGarbage(4,1);
986 			break;
987 		default:
988 			s.setGarbage(5,1);
989 			s.setGarbage(5,1);
990 			s.setGarbage(4,1);
991 			break;
992 		}
993 		if (s.width > 0) {
994 			garbageSendQueue.push_back(s);
995 		}
996 	}
997 	for (int i=0; i<30; i++) {
998 		for (int j=0; j<6; j++) {
999 			if (garbageToBeCleared[j][i]) {
1000 				GarbageClearer(j, i, board[j][i]%1000000, true, chain); //Clears the blocks and all blocks connected to it.
1001 			}
1002 		}
1003 	}
1004 
1005 	chain=0;
1006 
1007 	//Break chains (if a block is stable it is resetted to (chain == 0)):
1008 	for (int i=0; i<7; i++) {
1009 		bool faaling = false;  //In the beginning we are NOT falling
1010 		for (int j=0; j<30; j++) {
1011 			if ((faaling)&&(board[i][j]>-1)&&(board[i][j]<7)) {
1012 				block_setFalling(board[i][j], true);
1013 			}
1014 			if (!faaling) {
1015 				block_setFalling(board[i][j], false);
1016 			}
1017 			if ((!faaling)&&(board[i][j]>0)&&(board[i][j]/10000000!=0)&&((board[i][j]/BLOCKWAIT)%10!=1)&&((board[i][j]/BLOCKHANG)%10!=1)) {
1018 				if (chainSize[board[i][j]/10000000]>chainSize[chain]) {
1019 					chain=board[i][j]/10000000;
1020 				}
1021 				board[i][j]=board[i][j]%10000000;
1022 			}
1023 			if (!((board[i][j]>-1)&&(board[i][j]<7))) {
1024 				faaling=true;
1025 			}
1026 			if (((board[i][j]/1000000)%10==1)||((board[i][j]/BLOCKHANG)%10==1)||((board[i][j]/BLOCKWAIT)%10==1)) {
1027 				faaling = false;
1028 			}
1029 		}
1030 	}
1031 
1032 	//Calculate chain
1033 	chain=0;
1034 	for (int i=0; i<6; i++) {
1035 		for (int j=0; j<30; j++) {
1036 			if (chainSize[board[i][j]/10000000]>chain) {
1037 				chain=chainSize[board[i][j]/10000000];
1038 			}
1039 		}
1040 	}
1041 
1042 	//Make space in table for more things
1043 	if (chain==0) {
1044 		for (int i=0; i<NUMBEROFCHAINS; i++) {
1045 			if (chainUsed[i]==true) {
1046 				if ((vsMode)&&(chainSize[i]>1)) {
1047 					GarbageStruct s;
1048 					s.setGarbage(6,chainSize[i]-1);
1049 					this->garbageSendQueue.push_back(s);
1050 				}
1051 				if (chainSize[i]>4) {
1052 					LongChainDoneEvent();
1053 				}
1054 				if (chainSize[i]>1 && !puzzleMode && recordStats) {
1055 					Stats::getInstance()->addOne((std::string)"chainX"+std::to_string(chainSize[i]));
1056 				}
1057 				chainUsed[i]=false;
1058 			}
1059 		}
1060 	}
1061 } //ClearBlocks
1062 
1063 //prints "Game Over" and ends game
SetGameOver()1064 void BlockGame::SetGameOver() {
1065 	if (!bGameOver) {
1066 		gameEndedAfter = ticks-gameStartedAt; //We game ends now!
1067 		if (recordStats) {
1068 			TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter));
1069 		}
1070 		FinalizeBlockGameInfo();
1071 	}
1072 	bGameOver = true;
1073 	if (stageClear) {
1074 		stageButtonStatus = SBstageClear;
1075 	}
1076 }
1077 
GetAIenabled() const1078 bool BlockGame::GetAIenabled() const {
1079 	return AI_Enabled;
1080 }
1081 
1082 
1083 //Moves all peaces a spot down if possible
FallBlock(int x,int y,int number)1084 int BlockGame::FallBlock(int x, int y, int number) {
1085 	if (y == 0) {
1086 		return -1;
1087 	}
1088 	if (x>0) {
1089 		if (board[x-1][y] == number) {
1090 			return -1;
1091 		}
1092 	}
1093 	int i=x;
1094 	bool canFall = true;
1095 	//checks a line of a garbage block and see if something is under it
1096 	while ((board[i][y] == number)&&(canFall)&&(i<6)) {
1097 		if (board[i][y-1] != -1) {
1098 			canFall = false;
1099 		}
1100 		i++;
1101 	}
1102 	if (canFall) {
1103 		//cout << "Now falling" << "\n";
1104 		for (int j = x; j<i; j++) {
1105 			board[j][y-1] = board[j][y];
1106 			board[j][y] = -1;
1107 		}
1108 	}
1109 	return 0;
1110 }   //FallBlock
1111 
1112 
1113 //Makes all Garbage fall one spot
GarbageFall()1114 void BlockGame::GarbageFall() {
1115 	for (int i=0; i<30; i++) {
1116 		for (int j=0; j<7; j++) {
1117 			if ((((board[j][i]/1000000)%10) == 1)||(((board[j][i]/1000000)%10) == 2)) {
1118 				FallBlock(j, i, board[j][i]);
1119 			}
1120 		}
1121 	}
1122 }
1123 
1124 //Makes the blocks fall (it doesn't test time, this must be done before hand)
FallDown()1125 void BlockGame::FallDown() {
1126 	bool falling =false;        //nothing is moving unless proven otherwise
1127 	for (int i=0; i<29; i++) {
1128 		for (int j=0; j<6; j++) {
1129 			if ((board[j][i]==-1) && (board[j][i+1]!=-1) && (board[j][i+1]%BLOCKFALL<7)) {
1130 				board[j][i] = board[j][i+1];
1131 				board[j][i+1] = -1;
1132 				falling = true;              //something is moving!
1133 			}
1134 			if ((board[j][i]/BLOCKWAIT)%10==1) {
1135 				falling=true;
1136 			}
1137 		}
1138 	}
1139 	if (!falling) { //If nothing is falling
1140 		if ((puzzleMode)&&(!bGameOver)&&(MovesLeft==0)&&(!(BoardEmpty()))) {
1141 			//Puzzle not won
1142 			SetGameOver();
1143 			stageButtonStatus = SBpuzzleMode;
1144 		}
1145 	}
1146 	GarbageFall();      //Makes the garbage fall
1147 	nrFellDown++;      //Sets number of this fall, so we know then the next will occur
1148 }
1149 
1150 //Moves the cursor, receaves N,S,E or W as a char an moves as desired
MoveCursor(char way)1151 void BlockGame::MoveCursor(char way) {
1152 	if (!bGameOver) {     //If game over nothing happends
1153 		if ((way == 'N') && ((cursory<10)||(TowerHeight>12) ||(((pixels==bsize)||(pixels==0)) && (cursory<11)))) {
1154 			cursory++;
1155 		}
1156 		if ((way == 'S') && (cursory>0)) {
1157 			cursory--;
1158 		}
1159 		if ((way == 'W') && (cursorx>0)) {
1160 			cursorx--;
1161 		}
1162 		if ((way == 'E') && (cursorx<4)) {
1163 			cursorx++;
1164 		}
1165 	}
1166 }
1167 
1168 //switches the two blocks at the cursor position, unless game over
SwitchAtCursor()1169 void BlockGame::SwitchAtCursor() {
1170 	if (bGameOver) {
1171 		return;
1172 	}
1173 	ClearBlocks();  //Ensure that everything that floats are marked as floating
1174 	if ((board[cursorx][cursory+1]<7) && (board[cursorx+1][cursory+1]<7) && ((!puzzleMode)||(MovesLeft>0)) && (gameStartedAt<ticks)) {
1175 		int temp = board[cursorx][cursory+1];
1176 		board[cursorx][cursory+1] = board[cursorx+1][cursory+1];
1177 		board[cursorx+1][cursory+1] = temp;
1178 	}
1179 	if ((puzzleMode)&&(gameStartedAt<ticks)&&(MovesLeft>0)) {
1180 		MovesLeft--;
1181 	}
1182 }
1183 
PushLine()1184 void BlockGame::PushLine() {
1185 	PushLineInternal();
1186 }
1187 
1188 //Generates a new line and moves the field one block up (restart puzzle mode)
PushLineInternal()1189 void BlockGame::PushLineInternal() {
1190 	//If not game over, not high tower and not puzzle mode
1191 	if ((!bGameOver) && TowerHeight<13 && (!puzzleMode) && (gameStartedAt<ticks)&&(chain==0)) {
1192 		for (int i=19; i>0; i--) {
1193 			for (int j=0; j<6; j++) {
1194 				board[j][i] = board[j][i-1];
1195 			}
1196 		}
1197 		for (int j=0; j<6; j++) {
1198 			board[j][0] = rand2() % 4;
1199 			if (j > 0) {
1200 				if (board[j][0] == board[j-1][0]) {
1201 					board[j][0] = rand2() % 6;
1202 				}
1203 			}
1204 			if (board[j][0] == board[j][1]) {
1205 				board[j][0] = rand2() % 6;
1206 			}
1207 			if (board[j][0] == board[j][1]) {
1208 				board[j][0] = rand2() % 6;
1209 			}
1210 			while ((j>0)&&(board[j][0]==board[j-1][0])) {
1211 				board[j][0] = rand2() % 6;
1212 			}
1213 		}
1214 		score+=1;
1215 		MoveCursor('N'); //Workaround for this being done registred too
1216 		if (vsMode) {
1217 			if (rand2()%6==1) {
1218 				board[rand2()%6][0]=6;
1219 			}
1220 		}
1221 		pixels = 0;
1222 		stop=0;
1223 		pushedPixelAt = ticks;
1224 		linesCleared++;
1225 		AI_LineOffset++;
1226 		nrPushedPixel=(int)((double)(pushedPixelAt-gameStartedAt)/(1000.0*speed));
1227 
1228 		if (recordStats) {
1229 			Stats::getInstance()->addOne("linesPushed");
1230 		}
1231 	} //if !bGameOver
1232 
1233 	//Restart Puzzle mode
1234 	if (puzzleMode && !bGameOver) {
1235 		//Reloads level
1236 		MovesLeft = PuzzleNumberOfMovesAllowed(Level);
1237 		for (int i=0; i<6; i++) {
1238 			for (int j=0; j<12; j++) {
1239 				board[i][j+1] = PuzzleGetBrick(Level,i,j);
1240 			}
1241 		}
1242 		score=0;
1243 		bGameOver=false;
1244 	}
1245 
1246 	if ((TowerHeight>12) && (!puzzleMode)&&(!bGameOver)&&(chain==0)) {
1247 		SetGameOver();
1248 	}
1249 
1250 
1251 }//PushLine
1252 
1253 //Pushes a single pixel, so it appears to scrool
PushPixels()1254 void BlockGame::PushPixels() {
1255 	nrPushedPixel++;
1256 	if ((pixels < bsize) && TowerHeight<13) {
1257 		pixels++;
1258 	}
1259 	else {
1260 		PushLineInternal();
1261 	}
1262 	if (pixels>bsize) {
1263 		pixels=0;
1264 	}
1265 }
1266 
1267 
1268 //See how high the tower is, saved in integer TowerHeight
FindTowerHeight()1269 void BlockGame::FindTowerHeight() {
1270 	/*
1271 	 * Old implementation, used until I find the bug in the other.
1272 	 * This function has a bug in stage clear! if an empty line appears.
1273 	 */
1274 	prevTowerHeight = TowerHeight;
1275 	bool found = false;
1276 	TowerHeight = 0;
1277 	while (!found) {
1278 		found = true;
1279 		for (int j=0; j<6; j++)
1280 			if (board[j][TowerHeight] != -1) {
1281 				found = false;
1282 			}
1283 		TowerHeight++;
1284 	}
1285 	TowerHeight--;
1286 }
1287 
1288 ///////////////////////////////////////////////////////////////////////////
1289 /////////////////////////// AI starts here! ///////////////////////////////
1290 ///////////////////////////////////////////////////////////////////////////
1291 //First the helpet functions:
nrOfType(int line,int type)1292 int BlockGame::nrOfType(int line, int type) {
1293 	int counter = 0;
1294 	for (int i=0; i<6; i++)
1295 		if (board[i][line]==type) {
1296 			counter++;
1297 		}
1298 	return counter;
1299 }
1300 
1301 
1302 //See if a combo can be made in this line
horiInLine(int line)1303 int BlockGame::horiInLine(int line) {
1304 	int nrOfType[7] = {0, 0, 0, 0, 0, 0, 0};
1305 	int max = 0;
1306 	for (int i=0; i<6; i++) {
1307 		int iTemp = board[i][line];
1308 		if ((iTemp>-1)&&(iTemp<7)) {
1309 			nrOfType[iTemp]++;
1310 		}
1311 	}
1312 	for (int j=0; j<7; j++) {
1313 		if (nrOfType[j]>max) {
1314 			max = nrOfType[j];
1315 			AIcolorToClear = j;
1316 		}
1317 	}
1318 	return max;
1319 }
1320 
horiClearPossible()1321 bool BlockGame::horiClearPossible() {
1322 	int i=13;
1323 	bool solutionFound = false;
1324 	do {
1325 		if (horiInLine(i)>2) {
1326 			AI_LineOffset = 0;
1327 			AIlineToClear = i;
1328 			solutionFound = true;
1329 		}
1330 		i--;
1331 	}
1332 	while ((!solutionFound)&&(i>0));
1333 	return solutionFound;
1334 }
1335 
1336 //the Line Has Unmoveable Objects witch might stall the AI
lineHasGarbage(int line)1337 bool BlockGame::lineHasGarbage(int line) {
1338 	for (int i=0; i<6; i++) {
1339 		if (board[i][line]>1000000) {
1340 			return true;
1341 		}
1342 	}
1343 	return false;
1344 }
1345 
1346 //Types 0..6 in line
nrOfRealTypes(int line)1347 int BlockGame::nrOfRealTypes(int line) {
1348 	int counter = 0;
1349 	for (int i=0; i<6; i++) {
1350 		if ((board[i][line]>-1)&&(board[i][line]<7)) {
1351 			counter++;
1352 		}
1353 	}
1354 	return counter;
1355 }
1356 
1357 //See if there is a tower
ThereIsATower()1358 bool BlockGame::ThereIsATower() {
1359 	bool bThereIsATower = false; //Unless proven otherwise!
1360 	bool topReached = false; //If we have reached the top
1361 	int lineNumber = 0;
1362 	bool emptySpacesFound = false;
1363 	do {
1364 		if ((emptySpacesFound) && (nrOfRealTypes(lineNumber)>0)&&(nrOfType(lineNumber, -1)>0)) {
1365 			AIlineToClear = lineNumber;
1366 			if (lineHasGarbage(lineNumber)) {
1367 				return false;
1368 			}
1369 			else {
1370 				bThereIsATower = true;
1371 			}
1372 		}
1373 		else {
1374 			emptySpacesFound=false;
1375 		}
1376 		if ((!emptySpacesFound)&&(nrOfType(lineNumber, -1)>0)) {
1377 			emptySpacesFound = true;
1378 		}
1379 		if (lineNumber<12) {
1380 			lineNumber++;
1381 		}
1382 		else {
1383 			topReached = true;
1384 		}
1385 	}
1386 	while ((!bThereIsATower)&&(!topReached));
1387 	return bThereIsATower;
1388 }
1389 
firstInLine1(int line)1390 double BlockGame::firstInLine1(int line) {
1391 	if (line > 20 || line < 0) {
1392 		std::cerr << "Warning: first in Line1: " << line << "\n";
1393 		return 3.0;
1394 	}
1395 	for (int i=0; i<6; i++) {
1396 		if ((board[i][line]>-1)&&(board[i][line]<7)) {
1397 			return (double)i;
1398 		}
1399 	}
1400 	return 3.0;
1401 }
1402 
1403 //returns the first coordinate of the block of type
firstInLine(int line,int type)1404 double BlockGame::firstInLine(int line, int type) {
1405 	if (line > 20 || line < 0) {
1406 		std::cerr << "Warning: first in Line: " << line << "\n";
1407 		return 3.0;
1408 	}
1409 	for (int i=0; i<6; i++) {
1410 		if (board[i][line]==type) {
1411 			return (double)i;
1412 		}
1413 	}
1414 	return 3.0;
1415 }
1416 
1417 //There in the line shall we move
closestTo(int line,int place)1418 int BlockGame::closestTo(int line, int place) {
1419 	if ((int)firstInLine1(line)>place) {
1420 		return (int)firstInLine1(line)-1;
1421 	}
1422 	for (int i=place; i>=0; i--) {
1423 		if ((board[i][line]>-1)&&(board[i][line]<7)) {
1424 			return i;
1425 		}
1426 	}
1427 	AIstatus=0;
1428 	return place;
1429 }
1430 
1431 //The AI will remove a tower
AI_ClearTower()1432 void BlockGame::AI_ClearTower() {
1433 	int place = (int)firstInLine(AIlineToClear-1, -1); //Find an empty field to frop a brick into
1434 	int xplace = closestTo(AIlineToClear, place); //Find the brick to drop in it
1435 	if (cursory+1<AIlineToClear) {
1436 		MoveCursor('N');
1437 	}
1438 	else if (cursory+1>AIlineToClear) {
1439 		MoveCursor('S');
1440 	}
1441 	else if (cursorx<xplace) {
1442 		MoveCursor('E');
1443 	}
1444 	else if (cursorx>xplace) {
1445 		MoveCursor('W');
1446 	}
1447 	else {
1448 		SwitchAtCursor();
1449 	}
1450 	if (!ThereIsATower()) {
1451 		AIstatus = 0;
1452 	}
1453 }
1454 
1455 //The AI will try to clear block horisontally
AI_ClearHori()1456 void BlockGame::AI_ClearHori() {
1457 	int lowestLine = AIlineToClear;
1458 	for (int i=0; i<7; i++) {
1459 		if (nrOfType(lowestLine, i)>2) {
1460 			AIcolorToClear = i;
1461 		}
1462 	}
1463 	if (cursory>lowestLine-1) {
1464 		MoveCursor('S');
1465 	}
1466 	else if (cursory<lowestLine-1) {
1467 		MoveCursor('N');
1468 	}
1469 	else if (nrOfType(lowestLine, AIcolorToClear)>2) {
1470 		int left=0, right=0;
1471 		if (board[0][lowestLine]==AIcolorToClear) {
1472 			left++;
1473 		}
1474 		if (board[1][lowestLine]==AIcolorToClear) {
1475 			left++;
1476 		}
1477 		if (board[2][lowestLine]==AIcolorToClear) {
1478 			left++;
1479 		}
1480 		if (board[3][lowestLine]==AIcolorToClear) {
1481 			right++;
1482 		}
1483 		if (board[4][lowestLine]==AIcolorToClear) {
1484 			right++;
1485 		}
1486 		if (board[5][lowestLine]==AIcolorToClear) {
1487 			right++;
1488 		}
1489 		int xplace = 0;
1490 		if (left<right) {
1491 			int count=0;
1492 			for (int i=0; (i<4)&&(count<1); i++) {
1493 				if ((board[i][lowestLine]==AIcolorToClear)&&((i==0)||(board[i+1][lowestLine]!=AIcolorToClear))) {
1494 					count++;
1495 					xplace = i;
1496 				}
1497 			}
1498 		}
1499 		else {
1500 			int count=0;
1501 			for (int i=3; (i<=5)&&(count<1); i++) {
1502 				if ((board[i][lowestLine]==AIcolorToClear)&&(board[i-1][lowestLine]!=AIcolorToClear)) {
1503 					count++;
1504 					xplace = --i;
1505 				}
1506 			}
1507 		}
1508 		if (cursorx<xplace) {
1509 			MoveCursor('E');
1510 		}
1511 		else if (cursorx>xplace) {
1512 			MoveCursor('W');
1513 		}
1514 		else if (cursorx==xplace) {
1515 			SwitchAtCursor();
1516 		}
1517 		else {
1518 			AIstatus = 0;
1519 		}
1520 	}
1521 	else {
1522 		AIstatus = 0;
1523 	}
1524 }
1525 
1526 //Test if vertical clear is possible
veriClearPossible()1527 bool BlockGame::veriClearPossible() {
1528 	bool found=false;
1529 	int colors[7] = {0, 0, 0, 0, 0, 0, 0};
1530 	for (int i=12; (i>0)&&(!found); i--) {
1531 		for (int j=0; j<7; j++) {
1532 			if (nrOfType(i, j)==0) {
1533 				colors[j]=0;
1534 			}
1535 			else if (++colors[j]>2) {
1536 				AIcolorToClear = j;
1537 				AIlineToClear = i;
1538 				found=true;
1539 			}
1540 		}
1541 	}
1542 	return found;
1543 }
1544 
1545 
1546 
1547 //There in the line shall we move
closestTo(int line,int type,int place)1548 int BlockGame::closestTo(int line, int type, int place) {
1549 	if ((int)firstInLine(line, type)>place) {
1550 		return (int)firstInLine(line, type)-1;
1551 	}
1552 	for (int i=place; i>=0; i--) {
1553 		if (board[i][line]==type) {
1554 			return i;
1555 		}
1556 	}
1557 	AIstatus=0;
1558 	return place;
1559 }
1560 
1561 //The AI will try to clear blocks vertically
AI_ClearVertical()1562 void BlockGame::AI_ClearVertical() {
1563 	//First we find the place there we will align the bricks
1564 	int placeToCenter = (int)(firstInLine(AIlineToClear, AIcolorToClear)/3.0+firstInLine(AIlineToClear+1, AIcolorToClear)/3.0+firstInLine(AIlineToClear+2, AIcolorToClear)/3.0);
1565 	int unlimitedLoop=0;
1566 	if (AIlineToClear < 0 || AIlineToClear > 20) {
1567 		std::cerr << "AIlineToClear out of range: " << AIlineToClear << "\n";
1568 		return;
1569 	}
1570 	if (placeToCenter<0 || placeToCenter > 5) {
1571 		std::cerr << "placeToCenter out of range: " << placeToCenter << "\n";
1572 		return;
1573 	}
1574 	while (((board[placeToCenter][AIlineToClear]>1000000)||(board[placeToCenter][AIlineToClear+1]>1000000)||(board[placeToCenter][AIlineToClear+2]>1000000))&&(unlimitedLoop<10)) {
1575 		unlimitedLoop++;
1576 		placeToCenter++;
1577 		if (placeToCenter>5) {
1578 			placeToCenter=0;
1579 		}
1580 	}
1581 	if (unlimitedLoop>9) {
1582 		AIstatus = 0;
1583 		return;
1584 	}
1585 	if (cursory+1>AIlineToClear+2) {
1586 		MoveCursor('S');
1587 	}
1588 	if (cursory+1<AIlineToClear) {
1589 		MoveCursor('N');
1590 	}
1591 	bool toAlign[3]= {true, true, true};
1592 	if (board[placeToCenter][AIlineToClear+0]==AIcolorToClear) {
1593 		toAlign[0]=false;
1594 	}
1595 	if (board[placeToCenter][AIlineToClear+1]==AIcolorToClear) {
1596 		toAlign[1]=false;
1597 	}
1598 	if (board[placeToCenter][AIlineToClear+2]==AIcolorToClear) {
1599 		toAlign[2]=false;
1600 	}
1601 	if (cursory+1==AIlineToClear) {
1602 		if (toAlign[0]==false) {
1603 			MoveCursor('N');
1604 		}
1605 		else {
1606 			if (cursorx>closestTo(AIlineToClear, AIcolorToClear, placeToCenter)) {
1607 				MoveCursor('W');
1608 			}
1609 			else if (cursorx<closestTo(AIlineToClear, AIcolorToClear, placeToCenter)) {
1610 				MoveCursor('E');
1611 			}
1612 			else {
1613 				SwitchAtCursor();
1614 			}
1615 		}
1616 	}
1617 	else if (cursory+1==AIlineToClear+1) {
1618 		if (toAlign[1]==false) {
1619 			if (toAlign[2]) {
1620 				MoveCursor('N');
1621 			}
1622 			else {
1623 				MoveCursor('S');
1624 			}
1625 		}
1626 		else {
1627 			if (cursorx>closestTo(AIlineToClear+1, AIcolorToClear, placeToCenter)) {
1628 				MoveCursor('W');
1629 			}
1630 			else if (cursorx<closestTo(AIlineToClear+1, AIcolorToClear, placeToCenter)) {
1631 				MoveCursor('E');
1632 			}
1633 			else {
1634 				SwitchAtCursor();
1635 			}
1636 		}
1637 	}
1638 	else if (cursory+1==AIlineToClear+2) {
1639 		if (toAlign[2]==false) {
1640 			MoveCursor('S');
1641 		}
1642 		else {
1643 			if (cursorx>closestTo(AIlineToClear+2, AIcolorToClear, placeToCenter)) {
1644 				MoveCursor('W');
1645 			}
1646 			else if (cursorx<closestTo(AIlineToClear+2, AIcolorToClear, placeToCenter)) {
1647 				MoveCursor('E');
1648 			}
1649 			else {
1650 				SwitchAtCursor();
1651 			}
1652 		}
1653 	}
1654 
1655 	if ((!toAlign[0])&&(!toAlign[1])&&(!toAlign[2])) {
1656 		AIstatus = 0;
1657 	}
1658 	if ((nrOfType(AIlineToClear, AIcolorToClear)==0)||(nrOfType(AIlineToClear+1, AIcolorToClear)==0)||(nrOfType(AIlineToClear+2, AIcolorToClear)==0)) {
1659 		AIstatus = 0;
1660 	}
1661 }
1662 
1663 
AI_Move()1664 void BlockGame::AI_Move() {
1665 	switch (AIstatus) {
1666 	case 1:
1667 		if (TowerHeight<8) {
1668 			PushLine();
1669 		}
1670 		else {
1671 			AIstatus = 0;
1672 		}
1673 		break;
1674 	case 2:
1675 		AI_ClearTower();
1676 		break;
1677 	case 3:
1678 		AI_ClearHori();
1679 		break;
1680 	case 4:
1681 		AI_ClearVertical();
1682 		break;
1683 	case 5:
1684 		if (!firstLineCreated) {
1685 			PushLine();
1686 			firstLineCreated = true;
1687 		}
1688 		else {
1689 			PushLine();
1690 			AIstatus = 0;
1691 		}
1692 		break;
1693 	case 6:
1694 		PushLine();
1695 		AIstatus = 0;
1696 		break;
1697 	default:
1698 		if (TowerHeight<6) {
1699 			AIstatus = 1;
1700 		}
1701 		else if (horiClearPossible()) {
1702 			AIstatus = 3;
1703 		}
1704 		else if (veriClearPossible()) {
1705 			AIstatus = 4;
1706 		}
1707 		else if (ThereIsATower()) {
1708 			AIstatus = 2;
1709 		}
1710 		else {
1711 			AIstatus = 5;
1712 		}
1713 		break;
1714 	}
1715 }
1716 
1717 //////////////////////////////////////////////////////////////////////////
1718 ///////////////////////////// AI ends here! //////////////////////////////
1719 //////////////////////////////////////////////////////////////////////////
1720 
1721 
1722 //Updates evrything, if not called nothing happends
Update()1723 void BlockGame::Update() {
1724 	unsigned int nowTime = ticks; //We remember the time, so it doesn't change during this call
1725 	capMaxStops(stop, nowTime, gameStartedAt);
1726 	{
1727 		FindTowerHeight();
1728 		if ((linesCleared-TowerHeight>stageClearLimit) && (stageClear) && (!bGameOver)) {
1729 			StageClearSetClear(Level, score, nowTime-gameStartedAt);
1730 			setPlayerWon();
1731 			stageButtonStatus = SBstageClear;
1732 		}
1733 		if ((TowerHeight>12)&&(prevTowerHeight<13)&&(!puzzleMode)) {
1734 			stop+=1000;
1735 		}
1736 
1737 		while ( nowTime> nrStops*40+gameStartedAt) { //Increase stops, till we reach nowTime
1738 			if (stop>0) {
1739 				stop = stop-20;
1740 				if (stop<=0) {
1741 					nrPushedPixel=(int)((nowTime-gameStartedAt)/(1000.0*speed));
1742 				}
1743 			}
1744 			if (stop<0) {
1745 				stop = 0;
1746 			}
1747 			nrStops++;
1748 		}
1749 		//If we have static content, we don't raise at all!
1750 		if (hasStaticContent()) {
1751 			stop++;
1752 		}
1753 		if ((puzzleMode)&&(!bGameOver)&&BoardEmpty()) {
1754 			if (!this->singlePuzzle) {
1755 				PuzzleSetClear(Level);
1756 				stageButtonStatus = SBpuzzleMode;
1757 			}
1758 			setPlayerWon();
1759 		}
1760 
1761 		//increse speed:
1762 		if ((nowTime>gameStartedAt+20000*speedLevel)&&(speedLevel <99)&&(!bGameOver)) {
1763 			speed = (baseSpeed*0.9)/((double)speedLevel*0.8);
1764 			speedLevel++;
1765 			nrPushedPixel=(int)((double)(nowTime-gameStartedAt)/(1000.0*speed));
1766 		}
1767 
1768 
1769 		//To prevent the stack from raising a lot then we stop a chain (doesn't work anymore)
1770 		if (chain>0) {
1771 			stop+=1;
1772 		}
1773 		//Raises the stack
1774 		if ((nowTime>gameStartedAt+nrPushedPixel*1000*speed) && (!bGameOver)&&(!stop))
1775 			while ((nowTime>gameStartedAt+nrPushedPixel*1000*speed)&&(!(puzzleMode))) {
1776 				PushPixels();
1777 			}
1778 		if (!bGameOver) {
1779 			ClearBlocks();
1780 		}
1781 
1782 		/*************************************************************
1783 		Ai stuff
1784 		 **************************************************************/
1785 		if (bGameOver) {
1786 			AIstatus = 0;       //Enusres that AI is resetted
1787 		}
1788 		else if (AI_Enabled) {
1789 			if (lastAImove+AI_MoveSpeed<ticks) {
1790 				AI_Move();
1791 				lastAImove=ticks;
1792 			}
1793 		}
1794 
1795 		/*************************************************************
1796 		Ai stuff ended
1797 		 **************************************************************/
1798 		if ((nowTime>gameStartedAt+nrFellDown*140) && (!bGameOver)) {
1799 			FallDown();
1800 		}
1801 		if ((nowTime<gameStartedAt)&&(puzzleMode)) {
1802 			FallDown();
1803 			nrFellDown--;
1804 		}
1805 		ReduceStuff();
1806 		if ((timetrial) && (!bGameOver) && (nowTime>gameStartedAt+2*60*1000)) {
1807 			SetGameOver();
1808 			TimeTrialEndEvent();
1809 		}
1810 	}
1811 }
1812 
IsNearDeath() const1813 bool BlockGame::IsNearDeath() const {
1814 	if ((TowerHeight>12)&&(!puzzleMode)&&(!bGameOver)) {
1815 		return true;
1816 	}
1817 	else {
1818 		return false;
1819 	}
1820 }
1821 
UpdateInternal(unsigned int newtick)1822 void BlockGame::UpdateInternal(unsigned int newtick) {
1823 	while (newtick >= ticks+10) {
1824 		ticks+=10;
1825 		Update();
1826 	}
1827 }
1828 
DoAction(const BlockGameAction & action)1829 void BlockGame::DoAction (const BlockGameAction& action) {
1830 	if (action.action == BlockGameAction::Action::UPDATE && action.tick < ticks+10) {
1831 		return;  //Ignore if this is an update and not high enough
1832 	}
1833 	if (action.action == BlockGameAction::Action::UPDATE && replayInfo.actions.size() > 0 && replayInfo.actions.back().action == action.action) {
1834 		replayInfo.actions.back() = action;
1835 	}
1836 	else {
1837 		replayInfo.actions.push_back(action);
1838 	}
1839 	if (action.action == BlockGameAction::Action::UPDATE) {
1840 		UpdateInternal(action.tick);
1841 	}
1842 	if (action.action == BlockGameAction::Action::SET_DRAW) {
1843 		setDraw();
1844 	}
1845 	if (action.action == BlockGameAction::Action::SET_WON) {
1846 		setPlayerWon();
1847 	}
1848 	if (action.action == BlockGameAction::Action::SET_GAME_OVER) {
1849 		SetGameOver();
1850 	}
1851 	if (action.action == BlockGameAction::Action::PUSH) {
1852 		PushLine();
1853 	}
1854 	if (action.action == BlockGameAction::Action::MOVE) {
1855 		char direction = action.way;
1856 		MoveCursor(direction);
1857 	}
1858 	if (action.action == BlockGameAction::Action::SWITCH) {
1859 		SwitchAtCursor();
1860 	}
1861 	if (action.action == BlockGameAction::Action::PUSH_GARBAGE) {
1862 		for (const GarbageStruct& i : action.garbage) {
1863 			if (i.greyGarbage) {
1864 				CreateGreyGarbage();
1865 			}
1866 			else {
1867 				CreateGarbage(i.width, i.height);
1868 			}
1869 		}
1870 	}
1871 	if (action.action == BlockGameAction::Action::MOUSE_DOWN) {
1872 		MouseDown(action.x, action.y);
1873 	}
1874 	if (action.action == BlockGameAction::Action::MOUSE_UP) {
1875 		MouseUp();
1876 	}
1877 	if (action.action == BlockGameAction::Action::MOUSE_MOVE) {
1878 		MouseMove(action.x);
1879 	}
1880 }
1881 
isSinglePuzzle() const1882 bool BlockGame::isSinglePuzzle() const {
1883 	return singlePuzzle;
1884 }
1885 
getLevel() const1886 int BlockGame::getLevel() const {
1887 	return Level;
1888 }
1889 
PopSendGarbage(std::vector<GarbageStruct> & poppedData)1890 void BlockGame::PopSendGarbage(std::vector<GarbageStruct>& poppedData) {
1891 	for (const GarbageStruct& g : this->garbageSendQueue) {
1892 		poppedData.push_back(g);
1893 	}
1894 	this->garbageSendQueue.clear();
1895 }
1896 
GetMouseCursor(bool & pressed,int & x,int & y) const1897 void BlockGame::GetMouseCursor(bool& pressed, int& x, int& y) const {
1898 	if (mouse_cursorx < 0 || mouse_cursory < 0 || mouse_cursorx >=6 || mouse_cursory > 13) {
1899 		pressed = false;
1900 		x = 0;
1901 		y = 0;
1902 		return;
1903 	}
1904 	pressed = true;
1905 	x = mouse_cursorx;
1906 	y = mouse_cursory;
1907 }
1908 
MouseDown(int x,int y)1909 void BlockGame::MouseDown(int x, int y) {
1910 	if (AI_Enabled) {
1911 		//AI may not use mouse move. It must use the controller
1912 		return;
1913 	}
1914 	mouse_cursorx = x;
1915 	mouse_cursory = y;
1916 }
1917 
MouseMove(int x)1918 void BlockGame::MouseMove(int x) {
1919 	if (AI_Enabled) {
1920 		//AI may not use mouse move. It must use the controller
1921 		return;
1922 	}
1923 	if (mouse_cursorx < 0) {
1924 		return;
1925 	}
1926 	if (x < 0 || x >= 6) {
1927 		return;
1928 	}
1929 	if (x > mouse_cursorx) {
1930 		MoveCursorTo(mouse_cursorx, mouse_cursory);
1931 		++mouse_cursorx;
1932 		SwitchAtCursor();
1933 	}
1934 	if (x < mouse_cursorx) {
1935 		--mouse_cursorx;
1936 		MoveCursorTo(mouse_cursorx, mouse_cursory);
1937 		SwitchAtCursor();
1938 	}
1939 }
1940 
MouseUp()1941 void BlockGame::MouseUp() {
1942 	mouse_cursorx = -1;
1943 	mouse_cursory = -1;
1944 }
1945 //Play the next level
nextLevel(BlockGame & g,unsigned int ticks)1946 void nextLevel(BlockGame& g, unsigned int ticks) {
1947 	BlockGameStartInfo s;
1948 	s.ticks = ticks;
1949 	s.level = g.getLevel()+1;
1950 	if (g.isPuzzleMode()) {
1951 		if (g.getLevel()<PuzzleGetNumberOfPuzzles()-1) {
1952 			s.puzzleMode = true;
1953 			g.NewGame(s);
1954 		}
1955 	}
1956 	else if (g.isStageClear()) {
1957 		if (g.getLevel() < 50-1) {
1958 			s.stageClear = true;
1959 			g.NewGame(s);
1960 		}
1961 	}
1962 }
1963 
retryLevel(BlockGame & g,unsigned int ticks)1964 void retryLevel(BlockGame& g, unsigned int ticks) {
1965 	BlockGameStartInfo s;
1966 	s.ticks = ticks;
1967 	s.level = g.getLevel();
1968 	if (g.isPuzzleMode()) {
1969 		s.puzzleMode = true;
1970 		g.NewGame(s);
1971 	}
1972 	else if (g.isStageClear()) {
1973 		s.stageClear = true;
1974 		g.NewGame(s);
1975 	}
1976 }
1977