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