diff options
| -rw-r--r-- | src/Controllers/ConsoleController/ConsoleController.cpp | 45 | ||||
| -rw-r--r-- | src/Controllers/ConsoleController/ConsoleController.hpp | 4 | ||||
| -rw-r--r-- | src/Helpers/Keyboard.hpp | 1 | ||||
| -rw-r--r-- | src/Model/Game.cpp | 52 | ||||
| -rw-r--r-- | src/Model/Game.hpp | 11 | ||||
| -rw-r--r-- | src/Model/Grid.cpp | 355 | ||||
| -rw-r--r-- | src/Model/Grid.hpp | 42 | ||||
| -rw-r--r-- | src/main.cpp | 2 |
8 files changed, 344 insertions, 168 deletions
diff --git a/src/Controllers/ConsoleController/ConsoleController.cpp b/src/Controllers/ConsoleController/ConsoleController.cpp index d91e807..1bc9b84 100644 --- a/src/Controllers/ConsoleController/ConsoleController.cpp +++ b/src/Controllers/ConsoleController/ConsoleController.cpp @@ -2,14 +2,22 @@ #include <SFML/Window/Keyboard.hpp> #include "../../Helpers/Keyboard.hpp" + +//==================== Constructor and Destructor ==================== + +//Constructor ConsoleController::ConsoleController() { } +//Destructor ConsoleController::~ConsoleController() { } +//==================== Helpers ==================== + +//Run the game. void ConsoleController::run() { @@ -22,6 +30,9 @@ void ConsoleController::run() //Pop a random number on the grid m_game.popRandomNumber(); + //First cout stats + this->coutStats(); + //First cout grid m_game.coutGrid(); @@ -33,20 +44,27 @@ void ConsoleController::run() keyPress=this->waitArrowKeyPress(); //Apply move - m_game.swipe(keyPress); + bool moveDone=m_game.swipe(keyPress); - //Pop a random number on the grid - m_game.popRandomNumber(); + //Cout stats + this->coutStats(); //Cout grid m_game.coutGrid(); } + + //Last cout stats + this->coutStats(); + + //Last cout grid m_game.coutGrid(); } + +//Wait for keypress and return the keyPress. kbdh::Direction ConsoleController::waitArrowKeyPress() { //Initialise keyPress @@ -59,7 +77,8 @@ kbdh::Direction ConsoleController::waitArrowKeyPress() keyPress=kbdh::Left; while(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { - //Wait for release + //Wait for release and try to remove arrow printed characters + std::cout << "\r" << " "; } break; } @@ -68,7 +87,8 @@ kbdh::Direction ConsoleController::waitArrowKeyPress() keyPress=kbdh::Right; while(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { - //Wait for release + //Wait for release and try to remove arrow printed characters + std::cout << "\r" << " "; } break; } @@ -77,17 +97,18 @@ kbdh::Direction ConsoleController::waitArrowKeyPress() keyPress=kbdh::Up; while(sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { - //Wait for release + //Wait for release and try to remove arrow printed characters + std::cout << "\r" << " "; } break; } if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { - // la touche "flèche gauche" est enfoncée : on bouge le personnage keyPress=kbdh::Down; while(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { - //Wait for release + //Wait for release and try to remove arrow printed characters + std::cout << "\r" << " "; } break; } @@ -95,3 +116,11 @@ kbdh::Direction ConsoleController::waitArrowKeyPress() return keyPress; } + + +//Cout the stats of the game +void ConsoleController::coutStats(){ + + std::cout << std::endl << "Score : " << m_game.getScore() << std::endl; + std::cout << "Nombre de coups : " << m_game.getNbMove() << std::endl; +} diff --git a/src/Controllers/ConsoleController/ConsoleController.hpp b/src/Controllers/ConsoleController/ConsoleController.hpp index 8d73f79..f3e9952 100644 --- a/src/Controllers/ConsoleController/ConsoleController.hpp +++ b/src/Controllers/ConsoleController/ConsoleController.hpp @@ -18,9 +18,13 @@ class ConsoleController Game m_game; kbdh::Direction waitArrowKeyPress(); public: + //Constructor and Destructor ConsoleController(); ~ConsoleController(); + + //Helpers void run(); + void coutStats(); }; #endif diff --git a/src/Helpers/Keyboard.hpp b/src/Helpers/Keyboard.hpp index 9d2dfa3..c24c1ba 100644 --- a/src/Helpers/Keyboard.hpp +++ b/src/Helpers/Keyboard.hpp @@ -10,6 +10,7 @@ namespace kbdh { //Key arrow enum Direction { Up, Down, Left, Right }; + //Key arrow typedef typedef enum Direction Direction; } diff --git a/src/Model/Game.cpp b/src/Model/Game.cpp index 6039bc9..284d82b 100644 --- a/src/Model/Game.cpp +++ b/src/Model/Game.cpp @@ -1,49 +1,85 @@ #include "Game.hpp" +//==================== Constructor and Destructor ==================== -Game::Game() : m_grid(){ +//Constructor +Game::Game() : m_grid(), m_score(0), m_nbMove(0){ } +//Destructor Game::~Game(){ } +//==================== Helpers ==================== - +//Swipe action bool Game::swipe(kbdh::Direction direction){ + bool moveDone; + switch(direction){ case kbdh::Left: - m_grid.swipeLeft(); + moveDone=m_grid.swipeLeft(); break; case kbdh::Right: - m_grid.swipeRight(); + moveDone=m_grid.swipeRight(); break; case kbdh::Up: - m_grid.swipeUp(); + moveDone=m_grid.swipeUp(); break; case kbdh::Down: - m_grid.swipeDown(); + moveDone=m_grid.swipeDown(); break; } - return true; + if(moveDone){ + m_score+=m_grid.getLastMoveScore(); + m_nbMove++; + this->popRandomNumber(); + } + + return moveDone; } +//Cout the grid void Game::coutGrid(){ std::cout << m_grid.description(); } +//Return true if the game is lost. False else. bool Game::isOver(){ return m_grid.isOver(); } +//Pop a random number on the grid void Game::popRandomNumber(){ std::tuple<int, int> coord(m_grid.getRandomEmptyCellCoord()); - int number=2; + int percent=rand() % 100; + + int number; + + if(percent <= 10){ + number=4; + } + else{ + number=2; + } + m_grid.setCell(coord, number); } +//==================== Getters and Setter ==================== + +//Retrieve the Score +int Game::getScore(){ + return m_score; +} + +//Retrieve the number of moves +int Game::getNbMove(){ + return m_nbMove; +} diff --git a/src/Model/Game.hpp b/src/Model/Game.hpp index ab19340..0b2ee4d 100644 --- a/src/Model/Game.hpp +++ b/src/Model/Game.hpp @@ -16,16 +16,25 @@ class Game { private: + //Members Grid m_grid; + int m_score; + int m_nbMove; public: + //Constructor and Destructor Game(); ~Game(); - + + //Helpers bool swipe(kbdh::Direction direction); void coutGrid(); void popRandomNumber(); bool isOver(); + + //Getters and Setters + int getScore(); + int getNbMove(); }; #endif diff --git a/src/Model/Grid.cpp b/src/Model/Grid.cpp index 3a8a075..cc0fe60 100644 --- a/src/Model/Grid.cpp +++ b/src/Model/Grid.cpp @@ -1,5 +1,7 @@ #include "Grid.hpp" +//==================== Constructor and Destructor ==================== + //Constructor Grid::Grid(): m_size(4), m_grid(4){ @@ -9,7 +11,6 @@ Grid::Grid(): m_size(4), m_grid(4){ m_grid.at(i).push_back(0); } } - } //Destructor @@ -17,109 +18,10 @@ Grid::~Grid(){ } -std::string Grid::description(){ - - //Init stringstream description - std::stringstream description; - - //Get max str len of the grid - int maxStrLen=this->maxStrLenInGrid(); - - //Start to write description - std::stringstream gridBorder; - for(int i=0;i<(maxStrLen+2)*4+1;i++){ - gridBorder<<"-"; - } - description << std::endl << gridBorder.str() << std::endl; - for(int i=0;i<m_size;i++){ - for(int j=0;j<m_size;j++){ - std::stringstream spaceCol; - for(int k=0;k<maxStrLen-std::to_string(m_grid.at(i).at(j)).size();k++){ - spaceCol << " "; - } - if(m_grid.at(i).at(j) == 0) - description << "| " << " " << spaceCol.str(); - else - description << "| " << m_grid.at(i).at(j) << spaceCol.str(); - } - description << "|"; - description << std::endl; - } - description << gridBorder.str() << std::endl << std::endl; - - //Return description - return description.str(); -} - - -int Grid::maxStrLenInGrid(){ - int max=0; - for(int i=0;i<m_size;i++){ - for(int j=0;j<m_size;j++){ - std::string number=std::to_string(m_grid.at(i).at(j)); - if(number.size() > max) - max=number.size(); - } - } - return max; -} - -bool Grid::isEmpty(int i, int j){ - if(m_grid.at(i).at(j) == 0) - return true; - return false; -} - -std::tuple<int, int> Grid::getRandomEmptyCellCoord(){ - - //Init list of candidate - std::vector<std::tuple<int, int> > candidates; - - //Construct list of candidates - for(int i=0;i<m_size;i++){ - for(int j=0;j<m_size;j++){ - if(this->isEmpty(i,j)){ - std::tuple<int, int> currentCandidate(i,j); - candidates.push_back(currentCandidate); - } - } - } - - //If no candidate available - if(candidates.size() == 0) - return std::tuple<int, int>(-1, -1); - - //Select the candidates - int winnerIs(rand() % candidates.size()); - - //Return the candidate - return candidates.at(winnerIs); - -} - - - -//Change value of cell -bool Grid::setCell(std::tuple<int, int> coord, int value){ - int i=std::get<0>(coord); - int j=std::get<1>(coord); +//==================== Merge and defragment methods ==================== - if(i>=0 && i<m_size && j>=0 && j<m_size){ - m_grid.at(i).at(j)=value; - return true; - } - - return false; -} - -//Another setCell method -bool Grid::setCell(int i, int j, int value){ - std::tuple<int, int> coord(i,j); - return this->setCell(coord, value); -} - - -std::vector<int> Grid::defragmentLine(std::vector<int> line){ +//Defragment the line to the right +std::vector<int> Grid::rightDefragment(std::vector<int> line){ for(int j=0; j<m_size-1;j++){ for(int i=0; i<m_size-1;i++){ int val1=line.at(i); @@ -134,7 +36,16 @@ std::vector<int> Grid::defragmentLine(std::vector<int> line){ return line; } -std::vector<int> Grid::mergeLine(std::vector<int> line){ +//Defragment the line to the left using right defragmentation +std::vector<int> Grid::leftDefragment(std::vector<int> line){ + + std::vector<int> reversedLine= this->reverseLine(line); + + return this->reverseLine(this->rightDefragment(reversedLine)); +} + +//Merge line to the right +std::vector<int> Grid::rightMerge(std::vector<int> line){ for(int i=0; i< m_size-1;i++){ int val1=line.at(i); int val2=line.at(i+1); @@ -142,72 +53,153 @@ std::vector<int> Grid::mergeLine(std::vector<int> line){ if(val1==val2){ line.at(i)=0; line.at(i+1)=val1*2; + m_lastMoveScore+=val1*2; i++; } } return line; } -std::vector<int> Grid::swipeLine(std::vector<int> line){ - - //Swipe line is : - //- A defragmentation - //- A merging - //- Another defragmentation - line=this->defragmentLine(line); - line=this->mergeLine(line); - line=this->defragmentLine(line); +//Merge line to the left +std::vector<int> Grid::leftMerge(std::vector<int> line){ + for(int i=m_size-1; i>0;i--){ + int val1=line.at(i); + int val2=line.at(i-1); - //Return swiped line + if(val1==val2){ + line.at(i)=0; + line.at(i-1)=val1*2; + m_lastMoveScore+=val1*2; + i--; + } + } return line; } -//Swipe to right -void Grid::swipeRight(){ +//==================== Swipe methods ==================== + + +//Swipe to the right +bool Grid::swipeRight(){ + + m_lastMoveScore=0; + bool moveDone=false; for(int i=0; i<m_size;i++){ - m_grid.at(i)=this->swipeLine(m_grid.at(i)); + std::vector<int> swipedLine(this->rightDefragment(this->leftMerge(this->rightDefragment(m_grid.at(i))))); + + if(!this->compareLines(m_grid.at(i), swipedLine)){ + moveDone=true; + m_grid.at(i)=swipedLine; + } } + return moveDone; } -//Swipe to right -void Grid::swipeLeft(){ +//Swipe to the left +bool Grid::swipeLeft(){ + m_lastMoveScore=0; + bool moveDone=false; for(int i=0; i<m_size;i++){ - m_grid.at(i)=this->reverseLine(this->swipeLine(this->reverseLine(m_grid.at(i)))); + std::vector<int> swipedLine(this->leftDefragment(this->rightMerge(this->leftDefragment(m_grid.at(i))))); + if(!this->compareLines(m_grid.at(i), swipedLine)){ + moveDone=true; + m_grid.at(i)=swipedLine; + } } + return moveDone; } -void Grid::swipeUp(){ +//Swipe to the top +bool Grid::swipeUp(){ + m_lastMoveScore=0; + bool moveDone=false; for(int i=0; i<m_size;i++){ std::vector<int> colVect=this->getCol(i); - this->setCol(i,this->reverseLine(this->swipeLine(this->reverseLine(colVect)))); + + std::vector<int> swipedLine(this->leftDefragment(this->rightMerge(this->leftDefragment(colVect)))); + if(!this->compareLines(colVect, swipedLine)){ + moveDone=true; + this->setCol(i,swipedLine); + } } + return moveDone; } -void Grid::swipeDown(){ +//Swipe to the bottom +bool Grid::swipeDown(){ + m_lastMoveScore=0; + bool moveDone=false; for(int i=0; i<m_size;i++){ std::vector<int> colVect=this->getCol(i); - this->setCol(i,this->swipeLine(colVect)); + + std::vector<int> swipedLine(this->rightDefragment(this->leftMerge(this->rightDefragment(colVect)))); + + if(!this->compareLines(colVect, swipedLine)){ + moveDone=true; + this->setCol(i,swipedLine); + } } + return moveDone; } -void Grid::setCol(int col, std::vector<int> colVect){ +//==================== Helpers ==================== + +//Get the max len of a string of the numbers in the grid : +// Exemple: +// - 1 return 1 +// - 22 return 2 +// - 120 return 3 +// - 1000 return 4 +//This method help to calculate the columns size +int Grid::maxStrLenInGrid(){ + int max=0; for(int i=0;i<m_size;i++){ - m_grid.at(i).at(col)=colVect.at(i); + for(int j=0;j<m_size;j++){ + std::string number=std::to_string(m_grid.at(i).at(j)); + if(number.size() > max) + max=number.size(); + } } + return max; } -std::vector<int> Grid::getCol(int col){ +//Test if the cell at (i,j) is empty +bool Grid::isEmpty(int i, int j){ + if(m_grid.at(i).at(j) == 0) + return true; + return false; +} - std::vector<int> colVect; +//Return a tuple that contain a random empty cell +std::tuple<int, int> Grid::getRandomEmptyCellCoord(){ + + //Init list of candidate + std::vector<std::tuple<int, int> > candidates; + //Construct list of candidates for(int i=0;i<m_size;i++){ - colVect.push_back(m_grid.at(i).at(col)); + for(int j=0;j<m_size;j++){ + if(this->isEmpty(i,j)){ + std::tuple<int, int> currentCandidate(i,j); + candidates.push_back(currentCandidate); + } + } } - return colVect; + //If no candidate available + if(candidates.size() == 0) + return std::tuple<int, int>(-1, -1); + + //Select the candidates + int winnerIs(rand() % candidates.size()); + + //Return the candidate + return candidates.at(winnerIs); + } +//Reverse a line std::vector<int> Grid::reverseLine(std::vector<int> line){ std::vector<int> reversedLine; @@ -218,7 +210,7 @@ std::vector<int> Grid::reverseLine(std::vector<int> line){ return reversedLine; } - +//Return true if the grid is full. False else. bool Grid::isFull(){ for(int i=0;i<m_size;i++){ @@ -227,10 +219,11 @@ bool Grid::isFull(){ return false; } } - + return true; } +//Return true if the grid is full and no move can be performed. False else. bool Grid::isOver(){ if(!this->isFull()) @@ -242,7 +235,7 @@ bool Grid::isOver(){ return false; } } - + for(int i=0;i<m_size;i++){ std::vector<int> colVect(this->getCol(i)); @@ -256,3 +249,99 @@ bool Grid::isOver(){ } + +//Return true if line1 is equals to line2. False else. +bool Grid::compareLines(std::vector<int> line1, std::vector<int> line2){ + + for(int i=0;i<m_size;i++){ + + if(line1.at(i) != line2.at(i)) + return false; + } + return true; +} + +//Return the description of the grid in a string. +//In other terms, the grid in ASCII +std::string Grid::description(){ + + //Init stringstream description + std::stringstream description; + + //Get max str len of the grid + int maxStrLen=this->maxStrLenInGrid(); + + //Start to write description + std::stringstream gridBorder; + for(int i=0;i<(maxStrLen+1)*4+1;i++){ + gridBorder<<"-"; + } + description << std::endl << gridBorder.str() << std::endl; + for(int i=0;i<m_size;i++){ + for(int j=0;j<m_size;j++){ + std::stringstream spaceCol; + //Get number of space needed in cell + int nbSpace=maxStrLen-std::to_string(m_grid.at(i).at(j)).size(); + + for(int k=0;k<nbSpace;k++){ + spaceCol << " "; + } + if(m_grid.at(i).at(j) == 0) + description << "|" << spaceCol.str() << " "; + else + description << "|"<< spaceCol.str() << m_grid.at(i).at(j) ; + } + description << "|"; + description << std::endl; + } + description << gridBorder.str() << std::endl << std::endl; + + //Return description + return description.str(); +} + +//==================== Getters and Setters ==================== + +//Getter for m_lastMoveScore +int Grid::getLastMoveScore(){ + return m_lastMoveScore; +} + + +//Change value of cell with a tuple +bool Grid::setCell(std::tuple<int, int> coord, int value){ + int i=std::get<0>(coord); + int j=std::get<1>(coord); + + if(i>=0 && i<m_size && j>=0 && j<m_size){ + m_grid.at(i).at(j)=value; + return true; + } + + return false; +} + +//Change value of cell with int +bool Grid::setCell(int i, int j, int value){ + std::tuple<int, int> coord(i,j); + return this->setCell(coord, value); +} + +//Assign a vector to a column. +void Grid::setCol(int col, std::vector<int> colVect){ + for(int i=0;i<m_size;i++){ + m_grid.at(i).at(col)=colVect.at(i); + } +} + +//Retrieve a specific column. +std::vector<int> Grid::getCol(int col){ + + std::vector<int> colVect; + + for(int i=0;i<m_size;i++){ + colVect.push_back(m_grid.at(i).at(col)); + } + + return colVect; +} diff --git a/src/Model/Grid.hpp b/src/Model/Grid.hpp index 21c653a..51168a9 100644 --- a/src/Model/Grid.hpp +++ b/src/Model/Grid.hpp @@ -15,38 +15,46 @@ class Grid { private: + //Members int m_size; std::vector<std::vector<int> > m_grid; + int m_lastMoveScore; + //Private methods int maxStrLenInGrid(); + public: + //Constructor and Destructor Grid(); ~Grid(); - std::string description(); + //Defragment and merge methods + std::vector<int> rightDefragment(std::vector<int> line); + std::vector<int> leftDefragment(std::vector<int> line); + std::vector<int> rightMerge(std::vector<int> line); + std::vector<int> leftMerge(std::vector<int> line); + + //Swipe methods + bool swipeRight(); + bool swipeLeft(); + bool swipeUp(); + bool swipeDown(); + + //Helpers + bool isFull(); + bool isOver(); bool isEmpty(int i, int j); std::tuple<int, int> getRandomEmptyCellCoord(); + bool compareLines(std::vector<int> line1, std::vector<int> line2); + std::vector<int> reverseLine(std::vector<int> line); + std::string description(); + //Getters and Setters bool setCell(std::tuple<int, int> coord, int value); bool setCell(int i, int j, int value); - - std::vector<int> swipeLine(std::vector<int> line); - std::vector<int> defragmentLine(std::vector<int> line); - std::vector<int> mergeLine(std::vector<int> line); - std::vector<int> getCol(int col); - - bool isFull(); - bool isOver(); - void setCol(int col, std::vector<int> colVect); - std::vector<int> reverseLine(std::vector<int> line); - - //Moves - void swipeRight(); - void swipeLeft(); - void swipeUp(); - void swipeDown(); + int getLastMoveScore(); }; diff --git a/src/main.cpp b/src/main.cpp index 49c7a20..48c1766 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,6 @@ int main() //Run the game controller.run(); - + //End the application return 0; } |
