/* =========================================================================== blockattack - Block Attack - Rise of the Blocks Copyright (C) 2005-2012 Poul Sander This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/ Source information and contacts persons can be found at http://blockattack.net =========================================================================== */ //Some definitions //The game is divided in frames. FALLTIME means the blocks will fall one block every FRAMELENGTH*FALLTIME millisecond #define FRAMELENGTH 50 #define HANGTIME 40 #define FALLTIME 20 //Don't change the following, they are fundamental and later some functions are hardcoded #define BLOCKFALL 10000 #define GARBAGE 1000000 #define CHAINPLACE 10000000 #include "BlockGame.hpp" #include "puzzlehandler.hpp" #include "stageclearhandler.hpp" #include using namespace std; static std::stringstream ss; //Used for internal formatting //////////////////////////////////////////////////////////////////////////////// //The BloackGame class represents a board, score, time etc. for a single player/ //////////////////////////////////////////////////////////////////////////////// int BlockGame::rand2() { nextRandomNumber = nextRandomNumber*1103515245 + 12345; return ((int)(nextRandomNumber/65536)) % 32768; } int BlockGame::firstUnusedChain() { bool found=false; int i = 0; while (!found) { if (!chainUsed[++i]) { found=true; } if (i>NUMBEROFCHAINS-2) { found=true; } } return i; } //Constructor BlockGame::BlockGame() { srand((int)time(nullptr)); nrFellDown = 0; nrPushedPixel = 0; garbageTarget = this; nrStops=0; //topx = tx; //topy = ty; cursorx = 2; cursory = 3; stop = 0; pixels = 0; score = 0; bGameOver = false; bDraw = false; timetrial = false; stageClear = false; vsMode = false; puzzleMode = false; linesCleared = 0; AI_Enabled = false; AI_MoveSpeed=100; AIlineToClear = 0; AIcolorToClear = 0; hasWonTheGame = false; combo=0; //counts chain=0; hangTicks = 0; baseSpeed = 0.5; //All other speeds are relative to this speed = baseSpeed; speedLevel = 1; ticks = 0; gameStartedAt = ticks; gameEndedAfter = 0; pushedPixelAt = gameStartedAt; nextGarbageNumber = 10; handicap=0; for (int i=0; i<7; i++) for (int j=0; j<30; j++) { board[i][j] = -1; } for (int i=0; iticks = ticks; stageButtonStatus = SBdontShow; nrFellDown = 0; nrPushedPixel = 0; nrStops = 0; cursorx = 2; cursory = 3; stop = 0; pixels = 0; score = 0; bGameOver = false; bDraw = false; timetrial = false; stageClear = false; linesCleared = 0; hasWonTheGame = false; vsMode = false; puzzleMode = false; combo=0; chain=0; AI_Enabled = false; baseSpeed= 0.5; speed = baseSpeed; speedLevel = 1; gameStartedAt = ticks+3000; pushedPixelAt = gameStartedAt; nextGarbageNumber = 10; handicap=0; for (int i=0; i<7; i++) for (int j=0; j<30; j++) { board[i][j] = -1; } for (int i=0; i -1) { NewGame(ticks); stageClear = true; Level = level; Stats::getInstance()->addOne("PlayedStageLevel"+itoa2(level)); stageClearLimit = 30+(Level%6)*10; baseSpeed = 0.5/((double)(Level*0.5)+1.0); speed = baseSpeed; } } void BlockGame::NewPuzzleGame(int level, unsigned int ticks) { if (level>-1) { NewGame(ticks); puzzleMode = true; Level = level; MovesLeft = PuzzleNumberOfMovesAllowed(Level); for (int i=0; i<6; i++) for (int j=0; j<12; j++) { board[i][j+1] = PuzzleGetBrick(Level,i,j); } baseSpeed = 100000; speed = 100000; //Now push the blines up for (int i=19; i>0; i--) for (int j=0; j<6; j++) { board[j][i] = board[j][i-1]; } for (int j=0; j<6; j++) { board[j][0] = rand() % 6; if (j > 0) { if (board[j][0] == board[j-1][0]) { board[j][0] = rand() % 6; } } if (board[j][0] == board[j][1]) { board[j][0] = 6; } if (board[j][0] == board[j][1]) { board[j][0] = 6; } } } } //Replay the current level void BlockGame::retryLevel(unsigned int ticks) { if (puzzleMode) { NewPuzzleGame(Level, ticks); } else if (stageClear) { NewStageGame(Level, ticks); } } //Play the next level void BlockGame::nextLevel(unsigned int ticks) { if (puzzleMode) { if (LeveladdOne("VSgamesStarted"); } //Starts new Vs Game (two Player) void BlockGame::NewVsGame(BlockGame* target, bool AI, unsigned int ticks) { NewGame(ticks); vsMode = true; AI_Enabled = AI; if (!AI) { Stats::getInstance()->addOne("VSgamesStarted"); } else { name = "CPU"; } putStartBlocks(); garbageTarget = target; } //Prints "winner" and ends game void BlockGame::setPlayerWon() { if (!bGameOver || !hasWonTheGame) { gameEndedAfter = ticks-gameStartedAt; //We game ends now! if (!AI_Enabled) { TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter)); } bGameOver = true; PlayerWonEvent(); if (!AI_Enabled) { Stats::getInstance()->addOne("totalWins"); if (garbageTarget->AI_Enabled) { //We have defeated an AI Stats::getInstance()->addOne("defeatedAI"+itoa(garbageTarget->getAIlevel())); } } if (AI_Enabled && !(garbageTarget->AI_Enabled)) { //The AI have defeated a human player Stats::getInstance()->addOne("defeatedByAI"+itoa(getAIlevel())); } } hasWonTheGame = true; } //Prints "draw" and ends the game void BlockGame::setDraw() { bGameOver = true; if (!AI_Enabled) { TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter)); } hasWonTheGame = false; bDraw = true; DrawEvent(); if (!AI_Enabled) { Stats::getInstance()->addOne("totalDraws"); } } //Test if LineNr is an empty line, returns false otherwise. bool BlockGame::LineEmpty(int lineNr) const { bool empty = true; for (int i = 0; i <7; i++) if (board[i][lineNr] != -1) { empty = false; } return empty; } //Test if the entire board is empty (used for Puzzles) bool BlockGame::BoardEmpty() const { bool empty = true; for (int i=0; i<6; i++) for (int j=1; j<13; j++) if (board[i][j] != -1) { empty = false; } return empty; } //Anything that the user can't move? In that case Game Over cannot occur bool BlockGame::hasStaticContent() const { for (int i=0; i<6; i++) for (int j=1; j<13; j++) if (board[i][j] >= 10000000) { //Higher than this means combos (garbage is static, but the stack is static but nothing to do about it) return true; //They are static } return false; //Return false if no static object found } /* * Generates some blocks so the user don't see a board without blocks */ //void putStartBlocks(int); void BlockGame::putStartBlocks() { putStartBlocks(time(0)); } void BlockGame::putStartBlocks(int n) { for (int i=0; i<7; i++) { for (int j=0; j<30; j++) { board[i][j] = -1; } } nextRandomNumber = n; int choice = rand2()%3; //Pick a random layout switch (choice) { case 0: //row 0: board[0][0]=1; board[1][0]=0; board[2][0]=4; board[3][0]=3; board[4][0]=3; board[5][0]=5; //row 1: board[0][1]=1; board[1][1]=4; board[2][1]=2; board[3][1]=0; board[4][1]=4; board[5][1]=5; //row 2: board[0][2]=2; board[1][2]=3; board[2][2]=0; board[3][2]=4; board[4][2]=1; board[5][2]=1; //row 3: board[0][3]=3; board[1][3]=2; board[2][3]=3; board[3][3]=1; board[4][3]=0; board[5][3]=4; //row 4: board[0][4]=2; board[1][4]=3; board[2][4]=3; board[3][4]=1; board[4][4]=4; board[5][4]=0; //row 5: board[0][5]=-1; board[1][5]=5; board[2][5]=5; board[3][5]=-1; board[4][5]=1; board[5][5]=-1; break; case 1: //row 0: board[0][0]=3; board[1][0]=5; board[2][0]=0; board[3][0]=0; board[4][0]=4; board[5][0]=2; //row 1: board[0][1]=3; board[1][1]=5; board[2][1]=-1; board[3][1]=5; board[4][1]=4; board[5][1]=2; //row 2: board[0][2]=2; board[1][2]=-1; board[2][2]=-1; board[3][2]=4; board[4][2]=0; board[5][2]=3; //row 3: board[0][3]=2; board[5][3]=3; break; default: //row 0: board[0][0]=4; board[1][0]=5; board[2][0]=2; board[3][0]=0; board[4][0]=1; board[5][0]=5; //row 1: board[0][1]=4; board[1][1]=5; board[2][1]=2; board[3][1]=1; board[4][1]=0; board[5][1]=2; //row 2: board[0][2]=2; board[1][2]=4; board[2][2]=-1; board[3][2]=0; board[4][2]=1; board[5][2]=5; //row 3: board[0][3]=4; board[1][3]=2; board[2][3]=-1; board[3][3]=1; board[4][3]=0; board[5][3]=2; //row 4: board[0][4]=4; board[1][4]=2; board[2][4]=-1; board[3][4]=0; board[4][4]=1; board[5][4]=-1; break; }; } //decreases hang for all hanging blocks and wait for waiting blocks void BlockGame::ReduceStuff() { int howMuchHang = (ticks - FRAMELENGTH*hangTicks)/FRAMELENGTH; if (howMuchHang>0) { for (int i=0; i<7; i++) for (int j=0; j<30; j++) { if ((board[i][j]/BLOCKHANG)%10==1) { int hangNumber = (board[i][j]/10)%100; if (hangNumber<=howMuchHang) { board[i][j]-=BLOCKHANG; board[i][j]-=hangNumber*10; } else { board[i][j]-=10*howMuchHang; } } if ((board[i][j]/BLOCKWAIT)%10==1) { int hangNumber = (board[i][j]/10)%100; if (hangNumber<=howMuchHang) { //The blocks must be cleared board[i][j]-=hangNumber*10; } else { board[i][j]-=10*howMuchHang; } } } } hangTicks+=howMuchHang; } //Creates garbage using a given wide and height bool BlockGame::CreateGarbage(int wide, int height) { { if (wide>6) { wide = 6; } if (height>12) { height = 12; } int startPosition = 12; while ((!(LineEmpty(startPosition))) || (startPosition == 29)) { startPosition++; } if (startPosition == 29) { return false; //failed to place blocks } if (29-startPosition999999) { nextGarbageNumber = 10; } //bGarbageFallLeft = !(bGarbageFallLeft); return true; } return false; } //Creates garbage using a given wide and height bool BlockGame::CreateGreyGarbage() { int startPosition = 12; while ((!(LineEmpty(startPosition))) || (startPosition == 29)) { startPosition++; } if (startPosition == 29) { return false; //failed to place blocks } if (29-startPosition<1) { return false; //not enough space } int start, end; { start=0; end=6; } for (int i = startPosition; i 999999) { nextGarbageNumber = 10; } return true; } //Clears garbage, must take one the lower left corner! int BlockGame::GarbageClearer(int x, int y, int number, bool aLineToClear, int chain) { if ((board[x][y])%1000000 != number) { return -1; } if (aLineToClear) { board[x][y] = rand() % 6; board[x][y] += 10*HANGTIME+BLOCKHANG+CHAINPLACE*chain; } garbageToBeCleared[x][y] = false; GarbageClearer(x+1, y, number, aLineToClear, chain); GarbageClearer(x, y+1, number, false, chain); return 1; } //Marks garbage that must be cleared int BlockGame::GarbageMarker(int x, int y) { if ((x>6)||(x<0)||(y<0)||(y>29)) { return -1; } if (((board[x][y])/1000000 == 1)&&(garbageToBeCleared[x][y] == false)) { garbageToBeCleared[x][y] = true; //Float fill GarbageMarker(x-1, y); GarbageMarker(x+1, y); GarbageMarker(x, y-1); GarbageMarker(x, y+1); } return 1; } int BlockGame::FirstGarbageMarker(int x, int y) { if ((x>6)||(x<0)||(y<0)||(y>29)) { return -1; } if (((board[x][y])/1000000 == 2)&&(garbageToBeCleared[x][y] == false)) { for (int i=0; i<6; i++) { garbageToBeCleared[i][y] = true; } } else if (((board[x][y])/1000000 == 1)&&(garbageToBeCleared[x][y] == false)) { garbageToBeCleared[x][y] = true; //Float fill GarbageMarker(x-1, y); GarbageMarker(x+1, y); GarbageMarker(x, y-1); GarbageMarker(x, y+1); } return 1; } //Clear Blocks if 3 or more is alligned (naive implemented) void BlockGame::ClearBlocks() { bool toBeCleared[7][30]; //true if blok must be removed int previus=-1; //the last block checked int combo=0; for (int i=0; i<30; i++) for (int j=0; j<7; j++) { toBeCleared[j][i] = false; garbageToBeCleared[j][i] = false; } for (int i=0; i<7; i++) { bool faaling = false; for (int j=0; j<30; j++) { if ((faaling)&&(board[i][j]>-1)&&(board[i][j]%10000000<7)) { board[i][j]+=BLOCKFALL; } if ((!faaling)&&((board[i][j]/BLOCKFALL)%10==1)) { board[i][j]-=BLOCKFALL; } if (!((board[i][j]>-1)&&(board[i][j]%10000000<7))) { faaling=true; } 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)) { faaling = false; } } } for (int j=0; j<7; j++) { previus = -1; combo=0; for (int i=1; i<30; i++) { if ((board[j][i]>-1)&&(board[j][i]%10000000<7)) { if (board[j][i]%10000000 == previus) { combo++; } else { if (combo>2) for (int k = i-combo; k2) for (int k = i-combo; k-1)&&(board[j][i]%10000000<7))) { if (board[j][i]%10000000 == previus) { combo++; } else { if (combo>2) { for (int k = j-combo; k2) { for (int k = j-combo; k2) && (board[j][i]%10000000==6)) { garbageTarget->CreateGreyGarbage(); } if ((board[j][i]>-1)&&(board[j][i]%10000000<7)) { board[j][i]+=BLOCKWAIT+10*FALLTIME; } if (chainSize[board[j][i]/10000000]>chainSize[chain]) { chain=board[j][i]/10000000; } combo++; stop+=140*combo; score +=10; if (combo>3) { score+=3*combo; //More points if more cleared simontanously } } } } score+=chainSize[chain]*100; if (chain==0) { chain=firstUnusedChain(); chainSize[chain]=0; chainUsed[chain]=true; } chainSize[chain]++; for (int i=0; i<30; i++) { for (int j=0; j<6; j++) { //if(board[j][i]/10==(BLOCKWAIT+10*FALLTIME)/10) if (toBeCleared[j][i]) { board[j][i]=(board[j][i]%10000000)+chain*10000000; } } } { //This is here we add text to screen! bool dead = false; for (int i=29; i>=0; i--) { for (int j=0; j<6; j++) { if (toBeCleared[j][i]) { if (!dead) { dead=true; string tempS = itoa(chainSize[chain]); if (chainSize[chain]>1) { AddText(j, i, tempS, 1000); } } } } } } //This was there text was added if (vsMode) switch (combo) { case 0: case 1: case 2: case 3: break; case 4: garbageTarget->CreateGarbage(3, 1); break; case 5: garbageTarget->CreateGarbage(4, 1); break; case 6: garbageTarget->CreateGarbage(5, 1); break; case 7: garbageTarget->CreateGarbage(6, 1); break; case 8: garbageTarget->CreateGarbage(4, 1); garbageTarget->CreateGarbage(4, 1); break; case 9: garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(4, 1); break; case 10: garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(5, 1); break; case 11: garbageTarget->CreateGarbage(6, 1); garbageTarget->CreateGarbage(5, 1); break; case 12: garbageTarget->CreateGarbage(6, 1); garbageTarget->CreateGarbage(6, 1); break; case 13: garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(4, 1); break; default: garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(5, 1); garbageTarget->CreateGarbage(4, 1); break; } for (int i=0; i<30; i++) for (int j=0; j<6; j++) if (garbageToBeCleared[j][i]) { GarbageClearer(j, i, board[j][i]%1000000, true, chain); //Clears the blocks and all blocks connected to it. } chain=0; //Break chains (if a block is stable it is resetted to (chain == 0)): for (int i=0; i<7; i++) { bool faaling = false; //In the beginning we are NOT falling for (int j=0; j<30; j++) { if ((faaling)&&(board[i][j]>-1)&&(board[i][j]<7)) { board[i][j]+=BLOCKFALL; } if ((!faaling)&&((board[i][j]/BLOCKFALL)%10==1)) { board[i][j]-=BLOCKFALL; } if ((!faaling)&&(board[i][j]>0)&&(board[i][j]/10000000!=0)&&((board[i][j]/BLOCKWAIT)%10!=1)&&((board[i][j]/BLOCKHANG)%10!=1)) { if (chainSize[board[i][j]/10000000]>chainSize[chain]) { chain=board[i][j]/10000000; } board[i][j]=board[i][j]%10000000; } if (!((board[i][j]>-1)&&(board[i][j]<7))) { faaling=true; } if (((board[i][j]/1000000)%10==1)||((board[i][j]/BLOCKHANG)%10==1)||((board[i][j]/BLOCKWAIT)%10==1)) { faaling = false; } } } //Create garbage as a result //if((vsMode)&&(chainSize[chain]>1)) garbageTarget->CreateGarbage(6,chainSize[chain]-1); //Calculate chain chain=0; for (int i=0; i<6; i++) { for (int j=0; j<30; j++) { if (chainSize[board[i][j]/10000000]>chain) { chain=chainSize[board[i][j]/10000000]; } } } //Make space in table for more things if (chain==0) { for (int i=0; i1)) { garbageTarget->CreateGarbage(6, chainSize[i]-1); } if (chainSize[i]>4) { LongChainDoneEvent(); } if (chainSize[i]>1 && !puzzleMode && !AI_Enabled) { Stats::getInstance()->addOne((string)"chainX"+itoa(chainSize[i])); } chainUsed[i]=false; } } } } //ClearBlocks //prints "Game Over" and ends game void BlockGame::SetGameOver() { if (!bGameOver) { gameEndedAfter = ticks-gameStartedAt; //We game ends now! if (!AI_Enabled) { TimeHandler::addTime("playTime",TimeHandler::ms2ct(gameEndedAfter)); } } bGameOver = true; if (stageClear) { stageButtonStatus = SBstageClear; } } bool BlockGame::GetAIenabled() { return AI_Enabled; } //Moves all peaces a spot down if possible int BlockGame::FallBlock(int x, int y, int number) { if (y == 0) { return -1; } if (x>0) { if (board[x-1][y] == number) { return -1; } } int i=x; bool canFall = true; //checks a line of a garbage block and see if something is under it while ((board[i][y] == number)&&(canFall)&&(i<6)) { if (board[i][y-1] != -1) { canFall = false; } i++; } if (canFall) { //cout << "Now falling" << endl; for (int j = x; j12) ||(((pixels==bsize)||(pixels==0)) && (cursory<11)))) { cursory++; } if ((way == 'S') && (cursory>0)) { cursory--; } if ((way == 'W') && (cursorx>0)) { cursorx--; } if ((way == 'E') && (cursorx<4)) { cursorx++; } } } //switches the two blocks at the cursor position, unless game over void BlockGame::SwitchAtCursor() { if ((board[cursorx][cursory+1]<7) && (board[cursorx+1][cursory+1]<7) && (!bGameOver) && ((!puzzleMode)||(MovesLeft>0)) && (gameStartedAt0)) { MovesLeft--; } } void BlockGame::PushLine() { PushLineInternal(); } //Generates a new line and moves the field one block up (restart puzzle mode) void BlockGame::PushLineInternal() { //If not game over, not high tower and not puzzle mode if ((!bGameOver) && TowerHeight<13 && (!puzzleMode) && (gameStartedAt0; i--) { for (int j=0; j<6; j++) { board[j][i] = board[j][i-1]; } } for (int j=0; j<6; j++) { board[j][0] = rand2() % 4; if (j > 0) { if (board[j][0] == board[j-1][0]) { board[j][0] = rand2() % 6; } } if (board[j][0] == board[j][1]) { board[j][0] = rand2() % 6; } if (board[j][0] == board[j][1]) { board[j][0] = rand2() % 6; } while ((j>0)&&(board[j][0]==board[j-1][0])) { board[j][0] = rand2() % 6; } } score+=1; MoveCursor('N'); //Workaround for this being done registred too if (vsMode) { if (rand2()%6==1) { board[rand2()%6][0]=6; } } pixels = 0; stop=0; pushedPixelAt = ticks; linesCleared++; AI_LineOffset++; nrPushedPixel=(int)((double)(pushedPixelAt-gameStartedAt)/(1000.0*speed)); if (!AI_Enabled) { Stats::getInstance()->addOne("linesPushed"); } } //if !bGameOver //Restart Puzzle mode if (puzzleMode && !bGameOver) { //Reloads level MovesLeft = PuzzleNumberOfMovesAllowed(Level); for (int i=0; i<6; i++) { for (int j=0; j<12; j++) { board[i][j+1] = PuzzleGetBrick(Level,i,j); } } score=0; bGameOver=false; } if ((TowerHeight>12) && (!puzzleMode)&&(!bGameOver)&&(chain==0)) { SetGameOver(); } }//PushLine //Pushes a single pixel, so it appears to scrool void BlockGame::PushPixels() { nrPushedPixel++; if ((pixels < bsize) && TowerHeight<13) { pixels++; } else { PushLineInternal(); } if (pixels>bsize) { pixels=0; } } //See how high the tower is, saved in integer TowerHeight void BlockGame::FindTowerHeight() { /* * Old implementation, used until I find the bug in the other. * This function has a bug in stage clear! if an empty line appears. */ prevTowerHeight = TowerHeight; bool found = false; TowerHeight = 0; while (!found) { found = true; for (int j=0; j<6; j++) if (board[j][TowerHeight] != -1) { found = false; } TowerHeight++; } TowerHeight--; } /////////////////////////////////////////////////////////////////////////// /////////////////////////// AI starts here! /////////////////////////////// /////////////////////////////////////////////////////////////////////////// //First the helpet functions: int BlockGame::nrOfType(int line, int type) { // cout << "Start_ nrOfType" << endl; int counter = 0; for (int i=0; i<6; i++) if (board[i][line]==type) { counter++; } return counter; } //See if a combo can be made in this line int BlockGame::horiInLine(int line) { //cout << "Start_ hori in line" << endl; int nrOfType[7] = {0, 0, 0, 0, 0, 0, 0}; int iTemp; int max = 0; for (int i=0; i<6; i++) { iTemp = board[i][line]; if ((iTemp>-1)&&(iTemp<7)) { nrOfType[iTemp]++; } } for (int j=0; j<7; j++) { if (nrOfType[j]>max) { max = nrOfType[j]; AIcolorToClear = j; } } return max; } bool BlockGame::horiClearPossible() { //cout << "Start_ horiclear possible" << endl; int i=13; bool solutionFound = false; do { if (horiInLine(i)>2) { AI_LineOffset = 0; AIlineToClear = i; solutionFound = true; } i--; } while ((!solutionFound)&&(i>0)); return solutionFound; } //the Line Has Unmoveable Objects witch might stall the AI bool BlockGame::lineHasGarbage(int line) { for (int i=0; i<6; i++) { if (board[i][line]>1000000) { return true; } } return false; } //Types 0..6 in line int BlockGame::nrOfRealTypes(int line) { //cout << "Start_ nrOfReal" << endl; int counter = 0; for (int i=0; i<6; i++) { if ((board[i][line]>-1)&&(board[i][line]<7)) { counter++; } } return counter; } //See if there is a tower bool BlockGame::ThereIsATower() { //cout << "Start_ there is a tower" << endl; bool bThereIsATower = false; //Unless proven otherwise! bool topReached = false; //If we have reached the top int lineNumber = 0; bool emptySpacesFound = false; do { if ((emptySpacesFound) && (nrOfRealTypes(lineNumber)>0)&&(nrOfType(lineNumber, -1)>0)) { AIlineToClear = lineNumber; if (lineHasGarbage(lineNumber)) { return false; } else { bThereIsATower = true; } } else { emptySpacesFound=false; } if ((!emptySpacesFound)&&(nrOfType(lineNumber, -1)>0)) { emptySpacesFound = true; } if (lineNumber<12) { lineNumber++; } else { topReached = true; } } while ((!bThereIsATower)&&(!topReached)); //if(bThereIsATower) //cout << "There is actually a tower" << endl; return bThereIsATower; } double BlockGame::firstInLine1(int line) { if (line > 20 || line < 0) { cerr << "Warning: first in Line1: " << line << endl; return 3.0; } for (int i=0; i<6; i++) { if ((board[i][line]>-1)&&(board[i][line]<7)) { return (double)i; } } return 3.0; } //returns the first coordinate of the block of type double BlockGame::firstInLine(int line, int type) { if (line > 20 || line < 0) { cerr << "Warning: first in Line: " << line << endl; return 3.0; } for (int i=0; i<6; i++) if (board[i][line]==type) { return (double)i; } return 3.0; } //There in the line shall we move int BlockGame::closestTo(int line, int place) { if ((int)firstInLine1(line)>place) { return (int)firstInLine1(line)-1; } for (int i=place; i>=0; i--) { if ((board[i][line]>-1)&&(board[i][line]<7)) { return i; } } AIstatus=0; return place; } //The AI will remove a tower void BlockGame::AI_ClearTower() { // cout << "AI: ClearTower, line: " << AIlineToClear << endl; int place = (int)firstInLine(AIlineToClear-1, -1); //Find an empty field to frop a brick into int xplace = closestTo(AIlineToClear, place); //Find the brick to drop in it if (cursory+1AIlineToClear) { MoveCursor('S'); } else if (cursorxxplace) { MoveCursor('W'); } else { SwitchAtCursor(); } if (!ThereIsATower()) { AIstatus = 0; } } //The AI will try to clear block horisontally void BlockGame::AI_ClearHori() { // cout << "AI: ClearHori"; int lowestLine = AIlineToClear; //AIcolorToClear bool found =true; /*for(int i; (i<12)&&(!found);i++) * { * if(horiInLine(i)>2) * { * int lowestLine = i; * found = true; * } * }*/ for (int i=0; i<7; i++) { if (nrOfType(lowestLine, i)>2) { AIcolorToClear = i; } } if (found) { if (cursory>lowestLine-1) { MoveCursor('S'); } else if (cursory2) { int left=0, right=0; if (board[0][lowestLine]==AIcolorToClear) { left++; } if (board[1][lowestLine]==AIcolorToClear) { left++; } if (board[2][lowestLine]==AIcolorToClear) { left++; } if (board[3][lowestLine]==AIcolorToClear) { right++; } if (board[4][lowestLine]==AIcolorToClear) { right++; } if (board[5][lowestLine]==AIcolorToClear) { right++; } int xplace = 0; if (leftleft"; int count=0; for (int i=0; (i<4)&&(count<1); i++) if ((board[i][lowestLine]==AIcolorToClear)&&((i==0)||(board[i+1][lowestLine]!=AIcolorToClear))) { count++; xplace = i; } } else { // cout << ", left>=right"; int count=0; for (int i=3; (i<=5)&&(count<1); i++) if ((board[i][lowestLine]==AIcolorToClear)&&(board[i-1][lowestLine]!=AIcolorToClear)) { count++; xplace = --i; } } //cout << ", xplace: " << xplace; if (cursorxxplace) { MoveCursor('W'); } else if (cursorx==xplace) { SwitchAtCursor(); } else { AIstatus = 0; } } else { AIstatus = 0; } } else { AIstatus = 0; } //cout << endl; //for debugging } //Test if vertical clear is possible bool BlockGame::veriClearPossible() { bool found=false; int colors[7] = {0, 0, 0, 0, 0, 0, 0}; for (int i=12; (i>0)&&(!found); i--) { for (int j=0; j<7; j++) { if (nrOfType(i, j)==0) { colors[j]=0; } else if (++colors[j]>2) { AIcolorToClear = j; AIlineToClear = i; found=true; } } } return found; } //There in the line shall we move int BlockGame::closestTo(int line, int type, int place) { if ((int)firstInLine(line, type)>place) { return (int)firstInLine(line, type)-1; } for (int i=place; i>=0; i--) { if (board[i][line]==type) { return i; } } AIstatus=0; return place; } //The AI will try to clear blocks vertically void BlockGame::AI_ClearVertical() { // cout << "AI: ClearVeri"; //First we find the place there we will align the bricks int placeToCenter = (int)(firstInLine(AIlineToClear, AIcolorToClear)/3.0+firstInLine(AIlineToClear+1, AIcolorToClear)/3.0+firstInLine(AIlineToClear+2, AIcolorToClear)/3.0); int unlimitedLoop=0; if (AIlineToClear < 0 || AIlineToClear > 20) { cerr << "AIlineToClear out of range: " << AIlineToClear << endl; return; } if (placeToCenter<0 || placeToCenter > 5) { cerr << "placeToCenter out of range: " << placeToCenter << endl; return; } //cout << "AI_ClearVertical: " << placeToCenter << ", " << AIlineToClear << endl; while (((board[placeToCenter][AIlineToClear]>1000000)||(board[placeToCenter][AIlineToClear+1]>1000000)||(board[placeToCenter][AIlineToClear+2]>1000000))&&(unlimitedLoop<10)) { unlimitedLoop++; placeToCenter++; if (placeToCenter>5) { placeToCenter=0; } } if (unlimitedLoop>9) { AIstatus = 0; return; } //cout << ", ptc: " << placeToCenter << ", line: " << AIlineToClear << ", cy: " << cursory; if (cursory+1>AIlineToClear+2) { // cout << ", cursory>line+2"; MoveCursor('S'); } if (cursory+1closestTo(AIlineToClear, AIcolorToClear, placeToCenter)) { MoveCursor('W'); } else if (cursorxclosestTo(AIlineToClear+1, AIcolorToClear, placeToCenter)) { MoveCursor('W'); } else if (cursorxclosestTo(AIlineToClear+2, AIcolorToClear, placeToCenter)) { MoveCursor('W'); } else if (cursorxstageClearLimit) && (stageClear) && (!bGameOver)) { StageClearSetClear(Level, score, nowTime-gameStartedAt); setPlayerWon(); stageButtonStatus = SBstageClear; } if ((TowerHeight>12)&&(prevTowerHeight<13)&&(!puzzleMode)) { stop+=1000; } while ( nowTime> nrStops*40+gameStartedAt) { //Increase stops, till we reach nowTime if (stop>0) { stop = stop-20; if (stop<=0) { nrPushedPixel=(int)((nowTime-gameStartedAt)/(1000.0*speed)); } } if (stop<0) { stop = 0; } nrStops++; } //If we have static content, we don't raise at all! if (hasStaticContent()) { stop++; } if ((puzzleMode)&&(!bGameOver)&&BoardEmpty()) { if (!singlePuzzle) { PuzzleSetClear(Level); stageButtonStatus = SBpuzzleMode; } setPlayerWon(); } //increse speed: if ((nowTime>gameStartedAt+20000*speedLevel)&&(speedLevel <99)&&(!bGameOver)) { speed = (baseSpeed*0.9)/((double)speedLevel*0.5); speedLevel++; nrPushedPixel=(int)((double)(nowTime-gameStartedAt)/(1000.0*speed)); } //To prevent the stack from raising a lot then we stop a chain (doesn't work anymore) if (chain>0) { stop+=1; } //Raises the stack if ((nowTime>gameStartedAt+nrPushedPixel*1000*speed) && (!bGameOver)&&(!stop)) while ((nowTime>gameStartedAt+nrPushedPixel*1000*speed)&&(!(puzzleMode))) { PushPixels(); } if (!bGameOver) { ClearBlocks(); } /************************************************************* Ai stuff **************************************************************/ if (bGameOver) { AIstatus = 0; //Enusres that AI is resetted } else if (AI_Enabled) { if (lastAImove+AI_MoveSpeedgameStartedAt+nrFellDown*140) && (!bGameOver)) { FallDown(); } if ((nowTimegameStartedAt+2*60*1000)) { SetGameOver(); TimeTrialEndEvent(); } } } bool BlockGame::IsNearDeath() { if ((TowerHeight>12)&&(!puzzleMode)&&(!bGameOver)) { return true; } else { return false; } } void BlockGame::UpdateInternal(unsigned int newtick) { while (newtick >= ticks+50) { ticks+=50; Update(); } } void BlockGame::Update(unsigned int newtick) { UpdateInternal(newtick); } void BlockGame::PerformAction(unsigned int tick, int action, string param) { ss.str(std::string()); ss.clear(); ss << param; int p1,p2; int p3; switch (action) { case ACTION_UPDATE: UpdateInternal(tick); break; case ACTION_MOVECURSOR: MoveCursor(param.at(0)); break; case ACTION_MOVECURSORTO: { int p1,p2; ss >> p1 >> p2; MoveCursorTo(p1,p2); } break; case ACTION_SWITCH: SwitchAtCursor(); break; case ACTION_PUSH: PushLine(); break; case ACTION_CREATEGARBAGE: { ss >> p1 >> p2; CreateGarbage(p1,p2); } break; case ACTION_CREATEGRAYGARBAGE: CreateGreyGarbage(); break; case ACTION_GAMEOVER: SetGameOver(); break; case ACTION_WIN: setPlayerWon(); break; case ACTION_DRAW: setDraw(); break; case ACTION_GAMESPEED: ss >> p1; setGameSpeed(p1); break; case ACTION_HANDICAP: ss >> p1; setHandicap(p1); break; case ACTION_NEW: break; case ACTION_NEWTT: timetrial = true; break; case ACTION_NEWVS: break; case ACTION_STARTBLOCKS: ss >> p3; putStartBlocks(p3); break; case ACTION_NOP: break; default: cerr << "Unknown action: " << action << " " << param << endl; break; }; } //////////////////////////////////////////////////////////////////////////////// ///////////////////////// BlockAttack class end //////////////////////////////// ////////////////////////////////////////////////////////////////////////////////