mis en place la PR de simon sur les couleurs
This commit is contained in:
@@ -2,11 +2,13 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
Bag::Bag(const std::vector<Piece>& pieces) : pieces(pieces) {
|
||||
Bag::Bag(const std::vector<Piece>& pieces) :
|
||||
pieces(pieces) {
|
||||
|
||||
// initialize bags
|
||||
this->currentBag.clear();
|
||||
for (int i = 0; i < this->pieces.size(); i++) {
|
||||
@@ -19,7 +21,6 @@ Bag::Bag(const std::vector<Piece>& pieces) : pieces(pieces) {
|
||||
}
|
||||
|
||||
Piece Bag::lookNext() {
|
||||
// return the next piece
|
||||
return this->pieces.at(this->next);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
|
||||
|
||||
/**
|
||||
@@ -10,11 +10,11 @@
|
||||
*/
|
||||
class Bag {
|
||||
private:
|
||||
std::vector<Piece> pieces; // the pieces the bag can dispense
|
||||
int next; // the next piece to give
|
||||
std::vector<Piece> pieces; // the pieces the bag can dispense
|
||||
int next; // the next piece to give
|
||||
std::vector<int> currentBag; // the list of pieces that are still to be taken out before starting a new bag
|
||||
std::vector<int> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
|
||||
|
||||
std::vector<int> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new bag of the specified list of pieces
|
||||
@@ -22,12 +22,14 @@ class Bag {
|
||||
Bag(const std::vector<Piece>& pieces);
|
||||
|
||||
/**
|
||||
* Looks at what the next picked piece will be
|
||||
* Looks at what the next picked piece will be, without removing it from the bag
|
||||
* @return The next piece
|
||||
*/
|
||||
Piece lookNext();
|
||||
|
||||
/**
|
||||
* Picks a new piece from the current bag
|
||||
* Picks a new piece from the current bag, removing it from the bag
|
||||
* @return The next piece
|
||||
*/
|
||||
Piece getNext();
|
||||
|
||||
|
||||
@@ -2,13 +2,25 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
Board::Board(int width, int height) : width(width), height(height) {
|
||||
std::vector<Color> emptyRow;
|
||||
static constexpr std::vector<Block> getEmptyRow(int width) {
|
||||
std::vector<Block> emptyRow;
|
||||
for (int i = 0; i < width; i ++) {
|
||||
emptyRow.push_back(NOTHING);
|
||||
}
|
||||
return emptyRow;
|
||||
}
|
||||
|
||||
|
||||
Board::Board(int width, int height) :
|
||||
width(width),
|
||||
height(height) {
|
||||
|
||||
std::vector<Block> emptyRow;
|
||||
for (int i = 0; i < width; i ++) {
|
||||
emptyRow.push_back(NOTHING);
|
||||
}
|
||||
@@ -20,13 +32,13 @@ Board::Board(int width, int height) : width(width), height(height) {
|
||||
}
|
||||
}
|
||||
|
||||
void Board::addBlock(const Cell& position, Color block) {
|
||||
void Board::addBlock(const Position& position, Block block) {
|
||||
// if the block is out of bounds we discard it
|
||||
if (position.x < 0 || position.x >= this->width || position.y < 0) return;
|
||||
|
||||
// resize the grid if needed
|
||||
if (position.y >= this->grid.size()) {
|
||||
std::vector<Color> emptyRow;
|
||||
std::vector<Block> emptyRow;
|
||||
for (int i = 0; i < width; i ++) {
|
||||
emptyRow.push_back(NOTHING);
|
||||
}
|
||||
@@ -35,29 +47,26 @@ void Board::addBlock(const Cell& position, Color block) {
|
||||
}
|
||||
}
|
||||
|
||||
// change the block in the grid
|
||||
this->grid.at(position.y).at(position.x) = block;
|
||||
}
|
||||
|
||||
int Board::clearRows() {
|
||||
std::vector<Color> emptyRow;
|
||||
std::vector<Block> emptyRow;
|
||||
for (int i = 0; i < width; i ++) {
|
||||
emptyRow.push_back(NOTHING);
|
||||
}
|
||||
|
||||
// check from top to bottom
|
||||
// check from top to bottom, so that erasing lines don't screw up the looping
|
||||
int clearedLines = 0;
|
||||
for (int j = this->grid.size() - 1; j >= 0; j--) {
|
||||
// check if a line has a block on every column
|
||||
bool isFull = true;
|
||||
bool lineIsFull = true;
|
||||
for (int i = 0; i < this->width; i++) {
|
||||
if (this->grid.at(j).at(i) == NOTHING) {
|
||||
isFull = false;
|
||||
lineIsFull = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if it has, erase it and add a new row at the top
|
||||
if (isFull) {
|
||||
if (lineIsFull) {
|
||||
this->grid.erase(this->grid.begin() + j);
|
||||
if(this->grid.size() < height) this->grid.push_back(emptyRow);
|
||||
clearedLines++;
|
||||
@@ -67,18 +76,15 @@ int Board::clearRows() {
|
||||
return clearedLines;
|
||||
}
|
||||
|
||||
Color Board::getBlock(const Cell& position) const {
|
||||
// if the block is out of bounds
|
||||
if (position.x < 0 || position.x >= this->width || position.y < 0) return OUT_OF_BOUND;
|
||||
Block Board::getBlock(const Position& position) const {
|
||||
if (position.x < 0 || position.x >= this->width || position.y < 0) return OUT_OF_BOUNDS;
|
||||
|
||||
// if the block is higher than the current grid, since it can grow indefinitely we do as if it was there but empty
|
||||
if (position.y >= this->grid.size()) return NOTHING;
|
||||
|
||||
// else get the color in the grid
|
||||
return this->grid.at(position.y).at(position.x);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Color>> Board::getBlocks() const {
|
||||
std::vector<std::vector<Block>> Board::getBlocks() const {
|
||||
return this->grid;
|
||||
}
|
||||
|
||||
@@ -95,11 +101,10 @@ int Board::getWidth() const {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Board& board) {
|
||||
// print the board
|
||||
for (int y = board.grid.size() - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < board.width; x++) {
|
||||
Color block = board.grid.at(y).at(x);
|
||||
os << COLOR_CODES[block];
|
||||
Block block = board.grid.at(y).at(x);
|
||||
os << getConsoleColorCode(block);
|
||||
if (block != NOTHING) {
|
||||
os << "*";
|
||||
}
|
||||
@@ -110,8 +115,7 @@ std::ostream& operator<<(std::ostream& os, const Board& board) {
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
// reset console color
|
||||
os << COLOR_RESET;
|
||||
os << getResetConsoleColorCode();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "../Pieces/Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
*/
|
||||
class Board {
|
||||
private:
|
||||
std::vector<std::vector<Color>> grid; // the grid, (0,0) is downleft
|
||||
int width; // the width of the grid
|
||||
int height; // the base height of the grid, which can extends indefinitely
|
||||
std::vector<std::vector<Block>> grid; // the grid, (0,0) is downleft
|
||||
int width; // the width of the grid
|
||||
int height; // the base height of the grid, which can extends indefinitely
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -22,42 +22,44 @@ class Board {
|
||||
Board(int width, int height);
|
||||
|
||||
/**
|
||||
* Change the color of the specified block, if the block is out of bounds it is simply ignored
|
||||
* Change the block of the specified block, if the block is out of bounds it is simply ignored
|
||||
*/
|
||||
void addBlock(const Cell& position, Color block);
|
||||
void addBlock(const Position& position, Block block);
|
||||
|
||||
/**
|
||||
* Clears any complete row and moves down the rows on top, returns the number of cleared rows
|
||||
* Clears any complete row and moves down the rows on top
|
||||
* @return The number of cleared rows
|
||||
*/
|
||||
int clearRows();
|
||||
|
||||
/**
|
||||
* Returns the color of the block at the specified position
|
||||
* @return The block of the block at the specified position
|
||||
*/
|
||||
Color getBlock(const Cell& position) const;
|
||||
Block getBlock(const Position& position) const;
|
||||
|
||||
/**
|
||||
* Returns a copy of the grid
|
||||
* @return A copy of the grid
|
||||
*/
|
||||
std::vector<std::vector<Color>> getBlocks() const;
|
||||
std::vector<std::vector<Block>> getBlocks() const;
|
||||
|
||||
/**
|
||||
* Returns the actual height of the grid
|
||||
* @return The actual height of the grid
|
||||
*/
|
||||
int getGridHeight() const;
|
||||
|
||||
/**
|
||||
* Returns the base height of the grid
|
||||
* @return The base height of the grid
|
||||
*/
|
||||
int getBaseHeight() const;
|
||||
|
||||
/**
|
||||
* Returns the width of the grid
|
||||
* @return The width of the grid
|
||||
*/
|
||||
int getWidth() const;
|
||||
|
||||
/**
|
||||
* Stream output operator, adds a 2D grid representing the board
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const Board& board);
|
||||
};
|
||||
|
||||
@@ -4,17 +4,21 @@
|
||||
#include "GameParameters.h"
|
||||
#include "Action.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static const int SUBPX_PER_ROW = 60; // the number of position the active piece can take "between" two rows
|
||||
static const int SOFT_DROP_SCORE = 1; // the score gained by line soft dropped
|
||||
static const int HARD_DROP_SCORE = 2; // the score gained by line hard dropped
|
||||
static const int SUBPX_PER_ROW = 60; // the number of position the active piece can take "between" two rows
|
||||
static const int SOFT_DROP_SCORE = 1; // the score gained by line soft dropped
|
||||
static const int HARD_DROP_SCORE = 2; // the score gained by line hard dropped
|
||||
static const int LINE_CLEAR_BASE_SCORE = 100; // the score value of clearing a single line
|
||||
static const int B2B_SCORE_MULTIPLIER = 2; // by how much havaing B2B on multiplies the score of the line clear
|
||||
static const int B2B_MIN_LINE_NUMBER = 4; // the minimum number of lines needed to be cleared at once to gain B2B (without a spin)
|
||||
static const int B2B_SCORE_MULTIPLIER = 2; // by how much havaing B2B on multiplies the score of the line clear
|
||||
static const int B2B_MIN_LINE_NUMBER = 4; // the minimum number of lines needed to be cleared at once to gain B2B (without a spin)
|
||||
|
||||
|
||||
Game::Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardHeight, const std::vector<Piece>& bag) : parameters(gamemode, controls), board(boardWidth, boardHeight, bag, parameters.getNextQueueLength()) {
|
||||
Game::Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardHeight, const std::vector<Piece>& bag) :
|
||||
parameters(gamemode, controls),
|
||||
board(boardWidth, boardHeight, bag, parameters.getNextQueueLength()) {
|
||||
|
||||
// the game has not yet started
|
||||
this->started = false;
|
||||
this->lost = false;
|
||||
@@ -221,17 +225,17 @@ void Game::lockPiece() {
|
||||
this->parameters.clearLines(clear.lines);
|
||||
|
||||
// update B2B and score
|
||||
bool B2BConditions = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
|
||||
bool B2BConditionsAreMet = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
|
||||
if (clear.lines > 0) {
|
||||
/* clearing one more line is worth 2x more
|
||||
clearing with a spin is worth as much as clearing 2x more lines */
|
||||
long int clearScore = LINE_CLEAR_BASE_SCORE;
|
||||
clearScore = clearScore << (clear.lines << (clear.isSpin));
|
||||
|
||||
if (this->B2BChain && B2BConditions) clearScore *= B2B_SCORE_MULTIPLIER;
|
||||
if (this->B2BChain && B2BConditionsAreMet) clearScore *= B2B_SCORE_MULTIPLIER;
|
||||
this->score += clearScore;
|
||||
}
|
||||
this->B2BChain = B2BConditions;
|
||||
this->B2BChain = B2BConditionsAreMet;
|
||||
|
||||
// reset active piece
|
||||
this->subVerticalPosition = 0;
|
||||
@@ -286,7 +290,7 @@ Piece Game::getActivePiece() {
|
||||
return this->board.getActivePiece();
|
||||
}
|
||||
|
||||
Cell Game::getActivePiecePosition() {
|
||||
Position Game::getActivePiecePosition() {
|
||||
return this->board.getActivePiecePosition();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GameParameters.h"
|
||||
#include "Action.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,22 +12,22 @@
|
||||
*/
|
||||
class Game {
|
||||
private:
|
||||
GameParameters parameters; // the current parameters of the game
|
||||
GameBoard board; // the board in which the game is played
|
||||
bool started; // wheter the game has started
|
||||
bool lost; // wheter the game is lost
|
||||
long int score; // the current score
|
||||
int framesPassed; // how many frames have passed since the start of the game
|
||||
bool B2BChain; // wheter the player is currently on a B2B chain
|
||||
std::set<Action> heldActions; // the list of actions that were pressed last frame
|
||||
GameParameters parameters; // the current parameters of the game
|
||||
GameBoard board; // the board in which the game is played
|
||||
bool started; // wheter the game has started
|
||||
bool lost; // wheter the game is lost
|
||||
long int score; // the current score
|
||||
int framesPassed; // how many frames have passed since the start of the game
|
||||
bool B2BChain; // wheter the player is currently on a B2B chain
|
||||
std::set<Action> heldActions; // the list of actions that were pressed last frame
|
||||
std::set<Action> initialActions; // the list of actions that have been pressed while there was no active piece
|
||||
int heldDAS; // the number of frames DAS has been held, positive for right or negative for left
|
||||
int heldARR; // the number of frames ARR has been held
|
||||
int subVerticalPosition; // how far the active piece is to go down one line
|
||||
int leftARETime; // how many frames are left before ARE period finishes
|
||||
int totalLockDelay; // how many frames has the active piece touched the ground without moving
|
||||
int totalForcedLockDelay; // how many frames the active piece has touched the ground since the last spawned piece
|
||||
|
||||
int heldDAS; // the number of frames DAS has been held, positive for right or negative for left
|
||||
int heldARR; // the number of frames ARR has been held
|
||||
int subVerticalPosition; // how far the active piece is to go down one line
|
||||
int leftARETime; // how many frames are left before ARE period finishes
|
||||
int totalLockDelay; // how many frames has the active piece touched the ground without moving
|
||||
int totalForcedLockDelay; // how many frames the active piece has touched the ground since the last spawned piece
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize the parameters and creates a new board
|
||||
@@ -40,85 +40,85 @@ class Game {
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Advance to the next frame while excecuting the actions taken by the player,
|
||||
* Advances to the next frame while excecuting the actions taken by the player,
|
||||
* this is where the main game logic takes place
|
||||
*/
|
||||
void nextFrame(const std::set<Action>& playerActions);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Move the piece in the specified direction
|
||||
* Movse the piece in the specified direction
|
||||
*/
|
||||
void movePiece(int movement, bool resetDirection);
|
||||
|
||||
/**
|
||||
* Locks the piece, updates level and score and spawn the next piece if necessary
|
||||
* Locks the piece, updates level and score and spawns the next piece if necessary
|
||||
*/
|
||||
void lockPiece();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns wheter the player has won
|
||||
* @return If the player has won
|
||||
*/
|
||||
bool hasWon();
|
||||
|
||||
/**
|
||||
* Returns wheter the player has lost
|
||||
* @return If the player has lost
|
||||
*/
|
||||
bool hasLost();
|
||||
|
||||
/**
|
||||
* Returns the current level
|
||||
* @return The current level
|
||||
*/
|
||||
int getLevel();
|
||||
|
||||
/**
|
||||
* Returns the current number of cleared lines
|
||||
* @return The current number of cleared lines
|
||||
*/
|
||||
int getClearedLines();
|
||||
|
||||
/**
|
||||
* Returns the number of frames passed since the start of the game
|
||||
* @return The number of frames passed since the start of the game
|
||||
*/
|
||||
int getFramesPassed();
|
||||
|
||||
/**
|
||||
* Returns the current score
|
||||
* @return The current score
|
||||
*/
|
||||
int getScore();
|
||||
|
||||
/**
|
||||
* Returns wheter the player is currently on a B2B chain
|
||||
* @return If the player is currently on a B2B chain
|
||||
*/
|
||||
bool isOnB2BChain();
|
||||
|
||||
/**
|
||||
* Returns wheter all blocks are currently bone blocks
|
||||
* @return If all blocks are currently bone blocks
|
||||
*/
|
||||
bool areBlocksBones();
|
||||
|
||||
/**
|
||||
* Returns a copy of the board
|
||||
* @return A copy of the board
|
||||
*/
|
||||
Board getBoard();
|
||||
|
||||
/**
|
||||
* Returns a copy of the active piece
|
||||
* @return A copy of the active piece
|
||||
*/
|
||||
Piece getActivePiece();
|
||||
|
||||
/**
|
||||
* Returns a copy of the active piece position
|
||||
* @return A copy of the active piece position
|
||||
*/
|
||||
Cell getActivePiecePosition();
|
||||
Position getActivePiecePosition();
|
||||
|
||||
/**
|
||||
* Returns a copy of the held piece
|
||||
* @return A copy of the held piece
|
||||
*/
|
||||
Piece getHeldPiece();
|
||||
|
||||
/**
|
||||
* Return a copy of the next pieces queue
|
||||
* @return A copy of the next pieces queue
|
||||
*/
|
||||
std::vector<Piece> getNextPieces();
|
||||
};
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
#include "Bag.h"
|
||||
#include "LineClear.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
|
||||
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength) : board(boardWidth, boardHeight), generator(bag), nextQueueLength(nextQueueLength) {
|
||||
// initialize queue
|
||||
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength) :
|
||||
board(boardWidth, boardHeight),
|
||||
generator(bag),
|
||||
nextQueueLength(nextQueueLength) {
|
||||
|
||||
this->nextQueue.clear();
|
||||
for (int i = 0; i < nextQueueLength; i++) {
|
||||
this->nextQueue.push_back(this->generator.getNext());
|
||||
@@ -19,8 +22,7 @@ GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>&
|
||||
}
|
||||
|
||||
bool GameBoard::moveLeft() {
|
||||
// check if the piece can be moved one cell left
|
||||
if (this->isActivePieceInWall(Cell{-1, 0})) {
|
||||
if (this->isActivePieceInWall(Position{-1, 0})) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -31,8 +33,7 @@ bool GameBoard::moveLeft() {
|
||||
}
|
||||
|
||||
bool GameBoard::moveRight() {
|
||||
// check if the piece can be moved one cell right
|
||||
if (this->isActivePieceInWall(Cell{1, 0})) {
|
||||
if (this->isActivePieceInWall(Position{1, 0})) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -43,8 +44,7 @@ bool GameBoard::moveRight() {
|
||||
}
|
||||
|
||||
bool GameBoard::moveDown() {
|
||||
// check if the piece can be moved one cell down
|
||||
if (this->isActivePieceInWall(Cell{0, -1})) {
|
||||
if (this->isActivePieceInWall(Position{0, -1})) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -55,36 +55,35 @@ bool GameBoard::moveDown() {
|
||||
}
|
||||
|
||||
bool GameBoard::rotate(Rotation rotation) {
|
||||
// copy the original piece before rotating it
|
||||
Piece stored = *this->activePiece;
|
||||
this->rotate(rotation);
|
||||
|
||||
// check if the piece can rotate
|
||||
// before trying to kick, check if the piece can rotate without kicking
|
||||
if (!this->isActivePieceInWall()) {
|
||||
this->isLastMoveKick = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// get the list of cells that touches the original piece
|
||||
std::set<Cell> safeCells;
|
||||
for (Cell cell : stored.getPositions()) {
|
||||
Cell cellInGrid(cell + this->activePiecePosition);
|
||||
safeCells.insert(cellInGrid);
|
||||
safeCells.insert(cellInGrid + Cell{0, 1});
|
||||
safeCells.insert(cellInGrid + Cell{1, 0});
|
||||
safeCells.insert(cellInGrid + Cell{0, -1});
|
||||
safeCells.insert(cellInGrid + Cell{-1, 0});
|
||||
// get the list of positions that touches the original piece
|
||||
std::set<Position> safePositions;
|
||||
for (Position position : stored.getPositions()) {
|
||||
Position positionInGrid(position + this->activePiecePosition);
|
||||
safePositions.insert(positionInGrid);
|
||||
safePositions.insert(positionInGrid + Position{0, 1});
|
||||
safePositions.insert(positionInGrid + Position{1, 0});
|
||||
safePositions.insert(positionInGrid + Position{0, -1});
|
||||
safePositions.insert(positionInGrid + Position{-1, 0});
|
||||
}
|
||||
|
||||
// try kicking the piece down
|
||||
bool suceeded = this->tryKicking(true, safeCells);
|
||||
bool suceeded = this->tryKicking(true, safePositions);
|
||||
if (suceeded) {
|
||||
this->isLastMoveKick = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// if it doesn't work try kicking the piece up
|
||||
suceeded = this->tryKicking(false, safeCells);
|
||||
suceeded = this->tryKicking(false, safePositions);
|
||||
if (suceeded) {
|
||||
this->isLastMoveKick = true;
|
||||
return true;
|
||||
@@ -95,8 +94,8 @@ bool GameBoard::rotate(Rotation rotation) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells) {
|
||||
// we try from the original height of the piece, moving vertically as long as the kicked piece touches the original
|
||||
bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePositions) {
|
||||
// we try from the original height of the piece, moving vertically as long as the kicked piece touches the original at least once on this row
|
||||
bool overlapsVertically = true;
|
||||
int j = 0;
|
||||
do {
|
||||
@@ -107,13 +106,11 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
|
||||
do {
|
||||
// check right before right arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations
|
||||
if (overlapsRight) {
|
||||
Cell shift{+i, j};
|
||||
// the kicked position must touch the original piece
|
||||
if (!this->activePieceOverlapsOneCell(safeCells, shift)) {
|
||||
Position shift{+i, j};
|
||||
if (!this->activePieceOverlapsOnePosition(safePositions, shift)) {
|
||||
overlapsLeft = false;
|
||||
}
|
||||
else {
|
||||
// if the position is valid we place the active piece there
|
||||
if (!this->isActivePieceInWall(shift)) {
|
||||
this->activePiecePosition += shift;
|
||||
return true;
|
||||
@@ -123,8 +120,8 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
|
||||
|
||||
// do the same on the left side
|
||||
if (overlapsLeft) {
|
||||
Cell shift{-i, j};
|
||||
if (!this->activePieceOverlapsOneCell(safeCells, shift)) {
|
||||
Position shift{-i, j};
|
||||
if (!this->activePieceOverlapsOnePosition(safePositions, shift)) {
|
||||
overlapsLeft = false;
|
||||
}
|
||||
else {
|
||||
@@ -138,12 +135,10 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
|
||||
i++;
|
||||
} while (overlapsLeft && overlapsRight);
|
||||
|
||||
// test if no position touched the original piece
|
||||
if (i == 1) {
|
||||
overlapsVertically = false;
|
||||
}
|
||||
|
||||
// move one line up or down
|
||||
(testingBottom) ? j-- : j++;
|
||||
} while (overlapsVertically);
|
||||
|
||||
@@ -151,15 +146,11 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
|
||||
}
|
||||
|
||||
bool GameBoard::hold(Rotation initialRotation) {
|
||||
// swap with held piece
|
||||
std::swap(this->activePiece, this->heldPiece);
|
||||
|
||||
// if it's the first time holding try the next piece
|
||||
bool isFirstTimeHolding = false;
|
||||
if (this->activePiece == nullptr) {
|
||||
isFirstTimeHolding = true;
|
||||
|
||||
// if no pieces in next queue look at what the next would be
|
||||
bool isFirstTimeHolding = (this->activePiece == nullptr);
|
||||
if (isFirstTimeHolding) {
|
||||
// try with the next piece in queue since there is no piece in the hold box yet
|
||||
if (this->nextQueueLength == 0) {
|
||||
this->activePiece = std::make_shared<Piece>(this->generator.lookNext());
|
||||
}
|
||||
@@ -168,10 +159,8 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
}
|
||||
|
||||
// set the spawned piece to the correct position
|
||||
this->goToSpawnPosition();
|
||||
|
||||
// apply initial rotation
|
||||
Piece stored = *this->activePiece;
|
||||
this->rotate(initialRotation);
|
||||
|
||||
@@ -189,14 +178,10 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
}
|
||||
|
||||
// if it's the first time holding, confirm we keep this piece
|
||||
if (isFirstTimeHolding) {
|
||||
if (this->nextQueueLength == 0) {
|
||||
this->generator.getNext();
|
||||
}
|
||||
else {
|
||||
this->spawnNextPiece();
|
||||
}
|
||||
// confirm we keep the piece we tried with
|
||||
this->nextQueue.push_back(this->generator.getNext());
|
||||
this->nextQueue.erase(this->nextQueue.begin());
|
||||
}
|
||||
|
||||
// this piece has done nothing yet
|
||||
@@ -206,39 +191,34 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
|
||||
bool GameBoard::spawnNextPiece() {
|
||||
// add a piece to the queue
|
||||
// generate a new piece
|
||||
this->nextQueue.push_back(this->generator.getNext());
|
||||
|
||||
// get next piece from queue
|
||||
this->activePiece = std::make_shared<Piece>(this->nextQueue.front());
|
||||
this->nextQueue.erase(this->nextQueue.begin());
|
||||
|
||||
// set the spawned piece to the correct position
|
||||
this->goToSpawnPosition();
|
||||
|
||||
// this piece has done nothing yet
|
||||
this->isLastMoveKick = false;
|
||||
|
||||
// returns wheter the piece can spawn correctly
|
||||
return !this->isActivePieceInWall();
|
||||
}
|
||||
|
||||
bool GameBoard::touchesGround() {
|
||||
return this->isActivePieceInWall(Cell{0, -1});
|
||||
return this->isActivePieceInWall(Position{0, -1});
|
||||
}
|
||||
|
||||
LineClear GameBoard::lockPiece() {
|
||||
// check if the piece is locked in place
|
||||
bool isLocked = (this->isActivePieceInWall(Cell{0, 1}) && this->isActivePieceInWall(Cell{1, 0}) &&
|
||||
this->isActivePieceInWall(Cell{-1, 0}) && this->isActivePieceInWall(Cell{0, -1}));
|
||||
bool isLockedInPlace = (this->isActivePieceInWall(Position{0, 1}) && this->isActivePieceInWall(Position{1, 0})
|
||||
&& this->isActivePieceInWall(Position{-1, 0}) && this->isActivePieceInWall(Position{0, -1}));
|
||||
|
||||
// put the piece in the board
|
||||
for (Cell cell : this->activePiece->getPositions()) {
|
||||
this->board.addBlock(cell + this->activePiecePosition, this->activePiece->getColor());
|
||||
for (Position position : this->activePiece->getPositions()) {
|
||||
this->board.addBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
|
||||
}
|
||||
|
||||
// check for lines to clear
|
||||
return LineClear{this->board.clearRows(), isLocked, (!isLocked) && this->isLastMoveKick};
|
||||
return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick};
|
||||
}
|
||||
|
||||
Board GameBoard::getBoard() const {
|
||||
@@ -249,7 +229,7 @@ Piece GameBoard::getActivePiece() const {
|
||||
return *this->activePiece;
|
||||
}
|
||||
|
||||
Cell GameBoard::getActivePiecePosition() const {
|
||||
Position GameBoard::getActivePiecePosition() const {
|
||||
return this->activePiecePosition;
|
||||
}
|
||||
|
||||
@@ -261,31 +241,28 @@ std::vector<Piece> GameBoard::getNextPieces() const {
|
||||
return this->nextQueue;
|
||||
}
|
||||
|
||||
bool GameBoard::isActivePieceInWall(const Cell& shift) const {
|
||||
// check if every cell of the active piece is in an empty spot
|
||||
for (Cell cell : this->activePiece->getPositions()) {
|
||||
if (this->board.getBlock(cell + this->activePiecePosition + shift) != NOTHING) return true;
|
||||
bool GameBoard::isActivePieceInWall(const Position& shift) const {
|
||||
for (Position position : this->activePiece->getPositions()) {
|
||||
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GameBoard::activePieceOverlapsOneCell(const std::set<Cell>& safeCells, const Cell& shift) const {
|
||||
// check if one cell of the translated active piece overlaps with one cell of the given piece set
|
||||
for (Cell cell : this->activePiece->getPositions()) {
|
||||
if (safeCells.contains(cell + shift)) return true;
|
||||
bool GameBoard::activePieceOverlapsOnePosition(const std::set<Position>& safePositions, const Position& shift) const {
|
||||
for (Position position : this->activePiece->getPositions()) {
|
||||
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GameBoard::goToSpawnPosition() {
|
||||
// get the lowest cell of the piece
|
||||
int lowestCell = this->activePiece->getLength() - 1;
|
||||
for (Cell cell : this->activePiece->getPositions()) {
|
||||
if (cell.y < lowestCell) lowestCell = cell.y;
|
||||
int lowestPosition = this->activePiece->getLength() - 1;
|
||||
for (Position position : this->activePiece->getPositions()) {
|
||||
if (position.y < lowestPosition) lowestPosition = position.y;
|
||||
}
|
||||
|
||||
// set the piece one line above the board
|
||||
this->activePiecePosition.y = this->board.getBaseHeight() - lowestCell;
|
||||
this->activePiecePosition.y = this->board.getBaseHeight() - lowestPosition;
|
||||
|
||||
// center the piece horizontally, biased towards left
|
||||
this->activePiecePosition.x = (this->board.getWidth() - this->activePiece->getLength()) / 2;
|
||||
@@ -294,15 +271,13 @@ void GameBoard::goToSpawnPosition() {
|
||||
std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
|
||||
// print over the board (only the active piece if it is there)
|
||||
if (gameboard.activePiece != nullptr) {
|
||||
Block pieceBlockType = gameboard.activePiece->getBlockType();
|
||||
os << getConsoleColorCode(pieceBlockType);
|
||||
|
||||
// change to the color of the active piece
|
||||
Color pieceColor = gameboard.activePiece->getColor();
|
||||
os << COLOR_CODES[pieceColor];
|
||||
|
||||
// print only the cell were the active piece is
|
||||
// print only the position were the active piece is
|
||||
for (int y = gameboard.activePiecePosition.y + gameboard.activePiece->getLength() - 1; y >= gameboard.board.getBaseHeight(); y--) {
|
||||
for (int x = 0; x < gameboard.board.getWidth(); x++) {
|
||||
bool hasActivePiece = gameboard.activePiece->getPositions().contains(Cell{x, y} - gameboard.activePiecePosition);
|
||||
bool hasActivePiece = gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition);
|
||||
if (hasActivePiece) {
|
||||
os << "*";
|
||||
}
|
||||
@@ -315,21 +290,19 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
|
||||
}
|
||||
|
||||
// print the board
|
||||
Color pieceColor = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getColor();
|
||||
Block pieceBlockType = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getBlockType();
|
||||
for (int y = gameboard.board.getBaseHeight() - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < gameboard.board.getWidth(); x++) {
|
||||
bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->getPositions().contains(Cell{x, y} - gameboard.activePiecePosition);
|
||||
bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition);
|
||||
|
||||
// if the active piece is on this cell, print it
|
||||
// the active piece takes visual priority over the board
|
||||
if (hasActivePiece) {
|
||||
os << COLOR_CODES[pieceColor];
|
||||
os << getConsoleColorCode(pieceBlockType);
|
||||
os << "*";
|
||||
}
|
||||
|
||||
// else print the cell of the board
|
||||
else {
|
||||
Color block = gameboard.board.getBlock(Cell{x, y});
|
||||
os << COLOR_CODES[block];
|
||||
Block block = gameboard.board.getBlock(Position{x, y});
|
||||
os << getConsoleColorCode(block);
|
||||
if (block != NOTHING) {
|
||||
os << "*";
|
||||
}
|
||||
@@ -341,7 +314,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
|
||||
os << std::endl;
|
||||
}
|
||||
|
||||
// print held piece
|
||||
// print hold box
|
||||
os << "Hold:" << std::endl;
|
||||
if (!(gameboard.heldPiece == nullptr)) {
|
||||
os << *gameboard.heldPiece;
|
||||
@@ -353,8 +326,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
|
||||
os << piece;
|
||||
}
|
||||
|
||||
// reset console color
|
||||
os << COLOR_RESET;
|
||||
os << getResetConsoleColorCode();
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Bag.h"
|
||||
#include "LineClear.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
*/
|
||||
class GameBoard {
|
||||
private:
|
||||
Board board; // the board in which pieces moves, (0, 0) is downleft
|
||||
Bag generator; // the piece generator
|
||||
Board board; // the board in which pieces moves, (0, 0) is downleft
|
||||
Bag generator; // the piece generator
|
||||
std::shared_ptr<Piece> activePiece; // the piece currently in the board
|
||||
Cell activePiecePosition; // the position of the piece currently in the board
|
||||
std::shared_ptr<Piece> heldPiece; // a piece being holded
|
||||
int nextQueueLength; // the number of next pieces seeable at a time
|
||||
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
|
||||
bool isLastMoveKick; // wheter the last action the piece did was kicking
|
||||
|
||||
Position activePiecePosition; // the position of the piece currently in the board
|
||||
std::shared_ptr<Piece> heldPiece; // a piece being holded
|
||||
int nextQueueLength; // the number of next pieces seeable at a time
|
||||
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
|
||||
bool isLastMoveKick; // wheter the last action the piece did was kicking
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new board, generator, and next queue
|
||||
@@ -30,88 +30,98 @@ class GameBoard {
|
||||
GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength);
|
||||
|
||||
/**
|
||||
* Try moving the piece one cell to the left, and returns wheter it was sucessfull
|
||||
* Tries moving the piece one position to the left
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool moveLeft();
|
||||
|
||||
/**
|
||||
* Try moving the piece one cell to the right, and returns wheter it was sucessfull
|
||||
* Tries moving the piece one position to the right
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool moveRight();
|
||||
|
||||
/**
|
||||
* Try moving the piece one cell down, and returns wheter it was sucessfull
|
||||
* Tries moving the piece one position down
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool moveDown();
|
||||
|
||||
/**
|
||||
* Try rotating the piece and kicking it if necessary, and returns wheter it was sucessfull
|
||||
* Tries rotating the piece and kicking it if necessary
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool rotate(Rotation rotation);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Try kicking the piece, testing position either above or below the piece's initial position
|
||||
* Tries kicking the piece, testing position either above or below the piece's initial position
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool tryKicking(bool testingBottom, const std::set<Cell>& safeCells);
|
||||
bool tryKicking(bool testingBottom, const std::set<Position>& safePositions);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Try holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece,
|
||||
* and returns wheter it was sucessfull
|
||||
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool hold(Rotation initialRotation = NONE);
|
||||
|
||||
/**
|
||||
* Spawns the next piece from the queue, and returns wheter it spawns in a wall
|
||||
* Spawns the next piece from the queue
|
||||
* @return If it spawned in a wall
|
||||
*/
|
||||
bool spawnNextPiece();
|
||||
|
||||
/**
|
||||
* Returns wheter the active piece is touching walls directly below it
|
||||
* Checks is the active piece as a wall directly below one of its position
|
||||
* @return If it touches a ground
|
||||
*/
|
||||
bool touchesGround();
|
||||
|
||||
/**
|
||||
* Lock the active piece into the board and returns the resulting line clear
|
||||
* Locks the active piece into the board and clears lines if needed
|
||||
* @return The resulting line clear
|
||||
*/
|
||||
LineClear lockPiece();
|
||||
|
||||
/**
|
||||
* Returns a copy of the board
|
||||
* @return A copy of the board
|
||||
*/
|
||||
Board getBoard() const;
|
||||
|
||||
/**
|
||||
* Returns a copy of the active piece
|
||||
* @return A copy of the active piece
|
||||
*/
|
||||
Piece getActivePiece() const;
|
||||
|
||||
/**
|
||||
* Returns a copy of the position of the active piece
|
||||
* @return A copy of the position of the active piece
|
||||
*/
|
||||
Cell getActivePiecePosition() const;
|
||||
Position getActivePiecePosition() const;
|
||||
|
||||
/**
|
||||
* Returns a copy of the held piece
|
||||
* @return A copy of the held piece
|
||||
*/
|
||||
Piece getHeldPiece() const;
|
||||
|
||||
/**
|
||||
* Returns a copy of the next piece queue
|
||||
* @return A copy of the next piece queue
|
||||
*/
|
||||
std::vector<Piece> getNextPieces() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns wheter the translated active piece is in a wall
|
||||
* Checks if one of the active piece's positions touches a wall in the board
|
||||
* @return If the active piece is in a wall
|
||||
*/
|
||||
bool isActivePieceInWall(const Cell& shift = Cell{0, 0}) const;
|
||||
bool isActivePieceInWall(const Position& shift = Position{0, 0}) const;
|
||||
|
||||
/**
|
||||
* Returns wheter the translated active piece overlaps with at least one of the cells
|
||||
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
|
||||
* @return If the shifted active piece overlaps with one of the position
|
||||
*/
|
||||
bool activePieceOverlapsOneCell(const std::set<Cell>& safeCells, const Cell& shift = Cell{0, 0}) const;
|
||||
bool activePieceOverlapsOnePosition(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
|
||||
|
||||
/**
|
||||
* Sets the active piece to its spawn position
|
||||
@@ -121,6 +131,7 @@ class GameBoard {
|
||||
public:
|
||||
/**
|
||||
* Stream output operator, adds the board, the hold box and the next queue
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
#include "Player.h"
|
||||
|
||||
|
||||
GameParameters::GameParameters(Gamemode gamemode, const Player& controls) : gamemode(gamemode), controls(controls) {
|
||||
GameParameters::GameParameters(Gamemode gamemode, const Player& controls) :
|
||||
gamemode(gamemode),
|
||||
controls(controls) {
|
||||
|
||||
// initialize lines and level
|
||||
this->clearedLines = 0;
|
||||
switch (this->gamemode) {
|
||||
@@ -85,34 +88,37 @@ void GameParameters::updateStats() {
|
||||
}
|
||||
|
||||
/* GRAVITY */
|
||||
if (level >= 20) {
|
||||
// all levels above 20 are instant gravity
|
||||
this->gravity = 20 * 60;
|
||||
// get gravity for an assumed 20-rows board
|
||||
static const int gravityPerLevel[] = {
|
||||
0, // LVL0
|
||||
1, // 60f/line, 20s total
|
||||
2, // 30f/line, 10s total
|
||||
3, // 20f/line, 6.66s total
|
||||
4, // 15f/line, 5s total
|
||||
5, // 12f/line, 4s total
|
||||
6, // 10f/line, 3.33 total
|
||||
7, // 8.57f/line, 2.85s total
|
||||
8, // 7.5f/line, 2.5s total
|
||||
10, // 6f/line, 2s total
|
||||
12, // 5f/line, 1.66s total
|
||||
14, // 4.28f/line, 1.42s total
|
||||
17, // 3.52f/line, 1.17s total
|
||||
20, // 3f/line, 60f total
|
||||
24, // 2.5f/line, 50f total
|
||||
30, // 2f/line, 40f total
|
||||
40, // 1.5f/line, 30f total
|
||||
1 * 60, // 1line/f, 20f total
|
||||
2 * 60, // 2line/f, 10f total
|
||||
4 * 60 // 4line/f, 5f total
|
||||
};
|
||||
if (this->level < 0) {
|
||||
this->gravity = gravityPerLevel[0];
|
||||
}
|
||||
else if (this->gravity > 20) {
|
||||
this->gravity = gravityPerLevel[20];
|
||||
}
|
||||
else {
|
||||
// get gravity for an assumed 20-rows board
|
||||
switch (this->level) {
|
||||
case 1 : {this->gravity = 1; break;} // 60f/line, 20s total
|
||||
case 2 : {this->gravity = 2; break;} // 30f/line, 10s total
|
||||
case 3 : {this->gravity = 3; break;} // 20f/line, 6.66s total
|
||||
case 4 : {this->gravity = 4; break;} // 15f/line, 5s total
|
||||
case 5 : {this->gravity = 5; break;} // 12f/line, 4s total
|
||||
case 6 : {this->gravity = 6; break;} // 10f/line, 3.33 total
|
||||
case 7 : {this->gravity = 7; break;} // 8.57f/line, 2.85s total
|
||||
case 8 : {this->gravity = 8; break;} // 7.5f/line, 2.5s total
|
||||
case 9 : {this->gravity = 10; break;} // 6f/line, 2s total
|
||||
case 10 : {this->gravity = 12; break;} // 5f/line, 1.66s total
|
||||
case 11 : {this->gravity = 14; break;} // 4.28f/line, 1.42s total
|
||||
case 12 : {this->gravity = 17; break;} // 3.52f/line, 1.17s total
|
||||
case 13 : {this->gravity = 20; break;} // 3f/line, 60f total
|
||||
case 14 : {this->gravity = 24; break;} // 2.5f/line, 50f total
|
||||
case 15 : {this->gravity = 30; break;} // 2f/line, 40f total
|
||||
case 16 : {this->gravity = 40; break;} // 1.5f/line, 30f total
|
||||
case 17 : {this->gravity = 1 * 60; break;} // 1line/f, 20f total
|
||||
case 18 : {this->gravity = 2 * 60; break;} // 2line/f, 10f total
|
||||
case 19 : {this->gravity = 4 * 60; break;} // 4line/f, 5f total
|
||||
default : this->gravity = 1;
|
||||
}
|
||||
this->gravity = gravityPerLevel[this->level];
|
||||
}
|
||||
|
||||
/* LOCK DELAY */
|
||||
|
||||
@@ -10,20 +10,20 @@
|
||||
*/
|
||||
class GameParameters {
|
||||
private:
|
||||
Gamemode gamemode; // the current gamemode
|
||||
Player controls; // the player's controls
|
||||
int clearedLines; // the number of cleared lines
|
||||
int level; // the current level
|
||||
Gamemode gamemode; // the current gamemode
|
||||
Player controls; // the player's controls
|
||||
int clearedLines; // the number of cleared lines
|
||||
int level; // the current level
|
||||
int nextQueueLength; // the number of pieces visibles in the next queue
|
||||
bool boneBlocks; // wheter all blocks are bone blocks
|
||||
int gravity; // the gravity at which pieces drop
|
||||
int lockDelay; // the time before the piece lock in place
|
||||
bool boneBlocks; // wheter all blocks are bone blocks
|
||||
int gravity; // the gravity at which pieces drop
|
||||
int lockDelay; // the time before the piece lock in place
|
||||
int forcedLockDelay; // the forced time before the piece lock in place
|
||||
int ARE; // the time before the next piece spawn
|
||||
int lineARE; // the time before the next piece spawn, after clearing a line
|
||||
int DAS; // the time before the piece repeats moving
|
||||
int ARR; // the rate at which the piece repeats moving
|
||||
int SDR; // the rate at which the piece soft drops
|
||||
int ARE; // the time before the next piece spawn
|
||||
int lineARE; // the time before the next piece spawn, after clearing a line
|
||||
int DAS; // the time before the piece repeats moving
|
||||
int ARR; // the rate at which the piece repeats moving
|
||||
int SDR; // the rate at which the piece soft drops
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -32,12 +32,13 @@ class GameParameters {
|
||||
GameParameters(Gamemode gamemode, const Player& controls);
|
||||
|
||||
/**
|
||||
* Count the newly cleared lines and update level and stats if needed
|
||||
* Counts the newly cleared lines and update level and stats if needed
|
||||
*/
|
||||
void clearLines(int lineNumber);
|
||||
|
||||
/**
|
||||
* Returns wheter the game ended
|
||||
* Checks if the game ended based on the current states and time passed, accorind to the gamemode
|
||||
* @return If the player has won
|
||||
*/
|
||||
bool hasWon(int framesPassed);
|
||||
|
||||
@@ -49,17 +50,17 @@ class GameParameters {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns the current number of cleared line
|
||||
* @return The current number of cleared line
|
||||
*/
|
||||
int getClearedLines();
|
||||
|
||||
/**
|
||||
* Returns the current level
|
||||
* @return The current level
|
||||
*/
|
||||
int getLevel();
|
||||
|
||||
/**
|
||||
* Returns the length of the next queue
|
||||
* @return The length of the next queue
|
||||
*/
|
||||
int getNextQueueLength();
|
||||
|
||||
@@ -69,42 +70,42 @@ class GameParameters {
|
||||
bool getBoneBlocks();
|
||||
|
||||
/**
|
||||
* Returns the current gravity for a 20-line high board
|
||||
* @return The current gravity for a 20-line high board
|
||||
*/
|
||||
int getGravity();
|
||||
|
||||
/**
|
||||
* Returns the current lock delay
|
||||
* @return The current lock delay
|
||||
*/
|
||||
int getLockDelay();
|
||||
|
||||
/**
|
||||
* Returns the current forced lock delay
|
||||
* @return The current forced lock delay
|
||||
*/
|
||||
int getForcedLockDelay();
|
||||
|
||||
/**
|
||||
* Returns the current ARE
|
||||
* @return The current ARE
|
||||
*/
|
||||
int getARE();
|
||||
|
||||
/**
|
||||
* Returns the current line ARE
|
||||
* @return The current line ARE
|
||||
*/
|
||||
int getLineARE();
|
||||
|
||||
/**
|
||||
* Returns the current DAS
|
||||
* @return The current DAS
|
||||
*/
|
||||
int getDAS();
|
||||
|
||||
/**
|
||||
* Returns the current ARR
|
||||
* @return The current ARR
|
||||
*/
|
||||
int getARR();
|
||||
|
||||
/**
|
||||
* Returns the current SDR
|
||||
* @return The current SDR
|
||||
*/
|
||||
int getSDR();
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* Specify how many lines were cleared and how
|
||||
*/
|
||||
struct LineClear {
|
||||
int lines; // the number of lines cleared
|
||||
bool isSpin; // if the move was a spin
|
||||
int lines; // the number of lines cleared
|
||||
bool isSpin; // if the move was a spin
|
||||
bool isMiniSpin; // if the move was a spin mini
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "Player.h"
|
||||
|
||||
static const int DAS_MIN_VALUE = 0;
|
||||
static const int DAS_MAX_VALUE = 30;
|
||||
static const int ARR_MIN_VALUE = 0;
|
||||
static const int ARR_MAX_VALUE = 30;
|
||||
static const int SDR_MIN_VALUE = 0;
|
||||
static const int SDR_MAX_VALUE = 6;
|
||||
static const int DAS_MIN_VALUE = 0; // 0ms
|
||||
static const int DAS_MAX_VALUE = 30; // 500ms
|
||||
static const int ARR_MIN_VALUE = 0; // 0ms
|
||||
static const int ARR_MAX_VALUE = 30; // 500ms
|
||||
static const int SDR_MIN_VALUE = 0; // 0ms
|
||||
static const int SDR_MAX_VALUE = 6; // 100ms
|
||||
|
||||
|
||||
Player::Player() {
|
||||
|
||||
@@ -17,32 +17,35 @@ class Player {
|
||||
Player();
|
||||
|
||||
/**
|
||||
* Try setting DAS to the desired value, and returns wheter it is possible
|
||||
* Try setting DAS to the desired value
|
||||
* @return If it is possible
|
||||
*/
|
||||
bool setDAS(int DAS);
|
||||
|
||||
/**
|
||||
* Try setting ARR to the desired value, and returns wheter it is possible
|
||||
* Try setting ARR to the desired value
|
||||
* @return If it is possible
|
||||
*/
|
||||
bool setARR(int ARR);
|
||||
|
||||
/**
|
||||
* Try setting SDR to the desired value, and returns wheter it is possible
|
||||
* Try setting SDR to the desired value
|
||||
* @return If it is possible
|
||||
*/
|
||||
bool setSDR(int SDR);
|
||||
|
||||
/**
|
||||
* Returns DAS value
|
||||
* @return DAS value
|
||||
*/
|
||||
int getDAS();
|
||||
|
||||
/**
|
||||
* Returns ARR value
|
||||
* @return ARR value
|
||||
*/
|
||||
int getARR();
|
||||
|
||||
/**
|
||||
* Returns SDR value
|
||||
* @return SDR value
|
||||
*/
|
||||
int getSDR();
|
||||
};
|
||||
|
||||
37
src/Pieces/Block.h
Normal file
37
src/Pieces/Block.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* Every possible block type
|
||||
*/
|
||||
enum Block {
|
||||
NOTHING,
|
||||
OUT_OF_BOUNDS,
|
||||
GARBAGE,
|
||||
PURPLE,
|
||||
ORANGE,
|
||||
CYAN,
|
||||
PINK,
|
||||
YELLOW,
|
||||
RED,
|
||||
BLUE,
|
||||
GREEN
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the first block type a piece can be
|
||||
* @return The block type
|
||||
*/
|
||||
inline Block firstPieceBlockType() {
|
||||
return Block(PURPLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the block to the next available piece block type
|
||||
*/
|
||||
inline void nextPieceBlockType(Block& block) {
|
||||
block = (block == GREEN) ? PURPLE : Block(block + 1);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/**
|
||||
* A cell on a 2D grid
|
||||
*/
|
||||
struct Cell {
|
||||
int x; // x position
|
||||
int y; // y position
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Addition operator, returns the sums of the coordinates of both cells
|
||||
*/
|
||||
inline Cell operator+(const Cell& left, const Cell& right) {
|
||||
return Cell{left.x + right.x, left.y + right.y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Additive assignation operator, adds the coordinates of the right cell to the left one
|
||||
*/
|
||||
inline Cell& operator+=(Cell& left, const Cell& right) {
|
||||
left = left + right;
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substraction operator, returns the difference of the coordinate between the left and right cell
|
||||
*/
|
||||
inline Cell operator-(const Cell& left, const Cell& right) {
|
||||
return Cell{left.x - right.x, left.y - right.y};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Substractive assignation operator, substract the coordinates of the right cell from the left one
|
||||
*/
|
||||
inline Cell& operator-=(Cell& left, const Cell& right) {
|
||||
left = left - right;
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict inferiority operator, a cell is inferior to another if it is lower or at the same height and more to the left
|
||||
*/
|
||||
inline bool operator<(const Cell& left, const Cell& right) {
|
||||
return (left.x == right.x) ? (left.y < right.y) : (left.x < right.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality operator, two cells are equal if they have the same coordinates
|
||||
*/
|
||||
inline bool operator==(const Cell& left, const Cell& right) {
|
||||
return (left.x == right.x) && (left.y == right.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream output operator, adds the coordinates of the cell to the stream
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& os, const Cell& cell) {
|
||||
os << "x: " << cell.x << " y: " << cell.y;
|
||||
return os;
|
||||
}
|
||||
@@ -1,51 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <String>
|
||||
#include "Block.h"
|
||||
|
||||
#include "string"
|
||||
|
||||
|
||||
/**
|
||||
* Every possible colors a block can take
|
||||
* A color encoded in RGB
|
||||
*/
|
||||
enum Color {
|
||||
NOTHING,
|
||||
OUT_OF_BOUND,
|
||||
GARBAGE,
|
||||
PURPLE,
|
||||
ORANGE,
|
||||
CYAN,
|
||||
PINK,
|
||||
YELLOW,
|
||||
RED,
|
||||
BLUE,
|
||||
GREEN
|
||||
struct Color {
|
||||
unsigned char red; // the red component of the color
|
||||
unsigned char green; // the green component of the color
|
||||
unsigned char blue; // the blue component of the color
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first color a piece can take
|
||||
*/
|
||||
inline Color firstPieceColor() {
|
||||
return Color(PURPLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color to the next available piece color
|
||||
*/
|
||||
inline void nextPieceColor(Color& color) {
|
||||
color = (color == GREEN) ? PURPLE : Color(color + 1);
|
||||
}
|
||||
|
||||
static const std::string COLOR_RESET = "\033[38;2;255;255;255m"; // color code to reset the console color
|
||||
static const std::string COLOR_CODES[] = { // color codes to change the console color
|
||||
COLOR_RESET, // NOTHING
|
||||
COLOR_RESET, // OUT_OF_BOUND
|
||||
"\033[38;2;150;150;150m", // GARBAGE
|
||||
"\033[38;2;150;0;255m", // PURPLE
|
||||
"\033[38;2;255;150;0m", // ORANGE
|
||||
"\033[38;2;0;255;255m", // CYAN
|
||||
"\033[38;2;255;0;200m", // PINK
|
||||
"\033[38;2;255;255;0m", // YELLOW
|
||||
"\033[38;2;255;0;0m", // RED
|
||||
"\033[38;2;0;100;255m", // BLUE
|
||||
"\033[38;2;0;255;0m" // GREEN
|
||||
static const Color EMPTY_BLOCK_COLOR = {255, 255, 255}; // color of an empty block
|
||||
static const Color BLOCKS_COLOR[] = { // color for each block type
|
||||
EMPTY_BLOCK_COLOR, // NOTHING
|
||||
EMPTY_BLOCK_COLOR, // OUT_OF_BOUNDS
|
||||
{150, 150, 150}, // GARBAGE
|
||||
{150, 0, 255}, // PURPLE
|
||||
{255, 150, 0}, // ORANGE
|
||||
{0, 255, 255}, // CYAN
|
||||
{255, 0, 200}, // PINK
|
||||
{255, 255, 0}, // YELLOW
|
||||
{255, 0, 0}, // RED
|
||||
{0, 100, 255}, // BLUE
|
||||
{0, 255, 0} // GREEN
|
||||
};
|
||||
|
||||
/**
|
||||
* Translates the color into a color code to change the console's color
|
||||
* @return A string to print in the console
|
||||
*/
|
||||
inline std::string getConsoleColorCode(const Color& color) {
|
||||
return "\033[38;2;" + std::to_string(color.red) + ";" + std::to_string(color.green) + ";" + std::to_string(color.blue) + "m";
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the color into a color code to change the console's color
|
||||
* @return A string to print in the console
|
||||
*/
|
||||
inline std::string getConsoleColorCode(const Block block) {
|
||||
return getConsoleColorCode(BLOCKS_COLOR[block]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a color code to reset the console's color
|
||||
* @return A string to print in the console
|
||||
*/
|
||||
inline std::string getResetConsoleColorCode() {
|
||||
return getConsoleColorCode(EMPTY_BLOCK_COLOR);
|
||||
}
|
||||
|
||||
@@ -2,36 +2,33 @@
|
||||
|
||||
#include "Polyomino.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <Map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
Generator::Generator() {
|
||||
}
|
||||
|
||||
std::vector<Polyomino> Generator::generatePolyominos(unsigned int order) {
|
||||
// initialization
|
||||
std::vector<Polyomino> Generator::generatePolyominos(unsigned int polyominoSize) {
|
||||
this->validPolyominos.clear();
|
||||
this->currentTestedShape.clear();
|
||||
|
||||
// no polyomino with 0 cells
|
||||
if (order == 0) return this->validPolyominos;
|
||||
// no polyomino of size 0
|
||||
if (polyominoSize == 0) return this->validPolyominos;
|
||||
|
||||
// start generating from the monomino
|
||||
this->currentTestedShape.insert(Cell{0, 0});
|
||||
this->currentTestedShape.insert(Position{0, 0});
|
||||
|
||||
// generate polyominos
|
||||
std::map<Cell, int> candidateCells;
|
||||
this->generate(order, 0, 1, candidateCells);
|
||||
std::map<Position, int> candidatePositions;
|
||||
this->generate(polyominoSize, 0, 1, candidatePositions);
|
||||
return this->validPolyominos;
|
||||
}
|
||||
|
||||
void Generator::generate(unsigned int order, int lastAddedCellNumber, int nextAvaibleNumber, std::map<Cell, int> candidateCells) {
|
||||
void Generator::generate(unsigned int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) {
|
||||
// recursion stop
|
||||
if (order == this->currentTestedShape.size()) {
|
||||
// we test the polyomino formed by the current shape
|
||||
if (polyominoSize == this->currentTestedShape.size()) {
|
||||
Polyomino candidate(this->currentTestedShape);
|
||||
|
||||
// we sort the rotations of the polyominos
|
||||
@@ -51,35 +48,35 @@ void Generator::generate(unsigned int order, int lastAddedCellNumber, int nextAv
|
||||
return;
|
||||
}
|
||||
|
||||
// generate the list of candidate cells
|
||||
for (Cell cell : this->currentTestedShape) {
|
||||
this->tryToAddCandidateCell(Cell{cell.x, cell.y + 1}, nextAvaibleNumber, candidateCells);
|
||||
this->tryToAddCandidateCell(Cell{cell.x + 1, cell.y}, nextAvaibleNumber, candidateCells);
|
||||
this->tryToAddCandidateCell(Cell{cell.x, cell.y - 1}, nextAvaibleNumber, candidateCells);
|
||||
this->tryToAddCandidateCell(Cell{cell.x - 1, cell.y}, nextAvaibleNumber, candidateCells);
|
||||
// generate the list of candidate positions
|
||||
for (Position position : this->currentTestedShape) {
|
||||
this->tryToAddCandidatePosition(Position{position.x, position.y + 1}, nextAvaibleNumber, candidatePositions);
|
||||
this->tryToAddCandidatePosition(Position{position.x + 1, position.y}, nextAvaibleNumber, candidatePositions);
|
||||
this->tryToAddCandidatePosition(Position{position.x, position.y - 1}, nextAvaibleNumber, candidatePositions);
|
||||
this->tryToAddCandidatePosition(Position{position.x - 1, position.y}, nextAvaibleNumber, candidatePositions);
|
||||
}
|
||||
|
||||
// generate polyominos for all cells with a higher number than the last one
|
||||
for (auto [key, val] : candidateCells) {
|
||||
if (val > lastAddedCellNumber) {
|
||||
// generate polyominos for all positions with a higher number than the last one
|
||||
for (auto [key, val] : candidatePositions) {
|
||||
if (val > lastAddedPositionNumber) {
|
||||
this->currentTestedShape.insert(key);
|
||||
this->generate(order, val, nextAvaibleNumber, (order == this->currentTestedShape.size()) ? std::map<Cell, int>() : candidateCells);
|
||||
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
|
||||
this->currentTestedShape.erase(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Generator::tryToAddCandidateCell(const Cell& candidate, int& nextAvaibleNumber, std::map<Cell, int>& candidateCells) {
|
||||
// we declared the first cell as the lower-left square, since we always start with a monomino at (0,0) we can test with hard values
|
||||
void Generator::tryToAddCandidatePosition(const Position& candidate, int& nextAvaibleNumber, std::map<Position, int>& candidatepositions) {
|
||||
// we declared the first position as the lower-left square, since we always start with a monomino at (0,0) we can test with 0 directly
|
||||
if (candidate.y < 0 || (candidate.y == 0 && candidate.x < 0)) return;
|
||||
|
||||
// if the cell was already marked then we should not mark it again
|
||||
if (candidateCells.contains(candidate)) return;
|
||||
// if the position was already marked then we should not mark it again
|
||||
if (candidatepositions.contains(candidate)) return;
|
||||
|
||||
// if the candidate overlaps with the shape there is no reason to add it
|
||||
if (this->currentTestedShape.contains(candidate)) return;
|
||||
|
||||
// once all tests passed we can add the cell
|
||||
candidateCells.insert({candidate, nextAvaibleNumber});
|
||||
// once all tests passed we can add the position
|
||||
candidatepositions.insert({candidate, nextAvaibleNumber});
|
||||
nextAvaibleNumber++;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
#include "Polyomino.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <Map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
|
||||
/**
|
||||
@@ -13,27 +13,28 @@
|
||||
class Generator {
|
||||
private:
|
||||
std::vector<Polyomino> validPolyominos; // the list of already generated polyominos
|
||||
std::set<Cell> currentTestedShape; // the polyomino being created
|
||||
std::set<Position> currentTestedShape; // the polyomino being created
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes generator
|
||||
* Default constructor
|
||||
*/
|
||||
Generator();
|
||||
|
||||
/**
|
||||
* Returns the list of all one-sided polyominos of the specified size
|
||||
* Generates the list of all one-sided polyominos of the specified size
|
||||
* @return The list of polyominos
|
||||
*/
|
||||
std::vector<Polyomino> generatePolyominos(unsigned int order);
|
||||
std::vector<Polyomino> generatePolyominos(unsigned int polyominoSize);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generates all one-sided polyominos of the specified using the current tested shape
|
||||
* Generates all one-sided polyominos of the specified size using the current tested shape
|
||||
*/
|
||||
void generate(unsigned int order, int lastAddedCellNumber, int nextAvaibleNumber, std::map<Cell, int> candidateCells);
|
||||
void generate(unsigned int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions);
|
||||
|
||||
/**
|
||||
* Check wheter a candidate cell can be added to the current tested shape
|
||||
* Checks wheter a candidate position can be added to the current tested shape
|
||||
*/
|
||||
void tryToAddCandidateCell(const Cell& candidate, int& nextAvaibleNumber, std::map<Cell, int>& candidateCells);
|
||||
void tryToAddCandidatePosition(const Position& candidate, int& nextAvaibleNumber, std::map<Position, int>& candidatePositions);
|
||||
};
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
#include "Polyomino.h"
|
||||
#include "Rotation.h"
|
||||
#include "Block.h"
|
||||
#include "Color.h"
|
||||
|
||||
#include <Set>
|
||||
#include <String>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
|
||||
Piece::Piece(const Polyomino& polyomino, Color color) : polyomino(polyomino), color(color) {
|
||||
Piece::Piece(const Polyomino& polyomino, Block blockType) :
|
||||
polyomino(polyomino),
|
||||
blockType(blockType) {
|
||||
}
|
||||
|
||||
void Piece::rotate(Rotation rotation) {
|
||||
@@ -20,19 +23,19 @@ void Piece::rotate(Rotation rotation) {
|
||||
this->polyomino.rotateCCW();
|
||||
}
|
||||
|
||||
std::set<Cell> Piece::getPositions() const {
|
||||
return this->polyomino.getCells();
|
||||
std::set<Position> Piece::getPositions() const {
|
||||
return this->polyomino.getPositions();
|
||||
}
|
||||
|
||||
int Piece::getLength() const {
|
||||
return this->polyomino.getLength();
|
||||
}
|
||||
|
||||
Color Piece::getColor() const {
|
||||
return this->color;
|
||||
Block Piece::getBlockType() const {
|
||||
return this->blockType;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Piece& piece) {
|
||||
os << COLOR_CODES[piece.color] << piece.polyomino << COLOR_RESET;
|
||||
os << getConsoleColorCode(piece.blockType) << piece.polyomino << getResetConsoleColorCode();
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
#include "Polyomino.h"
|
||||
#include "Rotation.h"
|
||||
#include "Block.h"
|
||||
#include "Color.h"
|
||||
|
||||
#include <Set>
|
||||
#include <set>
|
||||
|
||||
|
||||
/**
|
||||
@@ -13,13 +14,13 @@
|
||||
class Piece {
|
||||
private:
|
||||
Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft
|
||||
Color color; // the color of the piece
|
||||
Block blockType; // the block type of the piece
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a piece with a specified shape and color
|
||||
* Creates a piece with a specified shape and block type
|
||||
*/
|
||||
Piece(const Polyomino& piece, Color color);
|
||||
Piece(const Polyomino& piece, Block blockType);
|
||||
|
||||
/**
|
||||
* Rotates the piece in the specified direction
|
||||
@@ -27,22 +28,23 @@ class Piece {
|
||||
void rotate(Rotation rotation);
|
||||
|
||||
/**
|
||||
* Returns a copy of the list of cells of the piece
|
||||
* @return A copy of the list of positions of the piece
|
||||
*/
|
||||
std::set<Cell> getPositions() const;
|
||||
std::set<Position> getPositions() const;
|
||||
|
||||
/**
|
||||
* Returns the length of the piece
|
||||
* @return The length of the piece
|
||||
*/
|
||||
int getLength() const;
|
||||
|
||||
/**
|
||||
* Returns the color of the piece
|
||||
* @return The block type of the piece
|
||||
*/
|
||||
Color getColor() const;
|
||||
Block getBlockType() const;
|
||||
|
||||
/**
|
||||
* Stream output operator, adds a 2D grid representing the piece
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const Piece& piece);
|
||||
};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include "Generator.h"
|
||||
#include "Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <String>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
@@ -14,10 +14,9 @@
|
||||
PiecesFiles::PiecesFiles() {
|
||||
}
|
||||
|
||||
bool PiecesFiles::savePieces(int order) const {
|
||||
// open pieces file
|
||||
bool PiecesFiles::savePieces(int polyominoSize) const {
|
||||
std::string filePath;
|
||||
if (!this->getFilePath(order, filePath)) {
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
||||
@@ -25,46 +24,44 @@ bool PiecesFiles::savePieces(int order) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// generates the polyominos
|
||||
Generator generator;
|
||||
std::vector<Polyomino> nMinos = generator.generatePolyominos(order);
|
||||
std::vector<Polyomino> nMinos = generator.generatePolyominos(polyominoSize);
|
||||
|
||||
// set the polyominos to their spawn position
|
||||
// sorting the polyominos is done after setting spawn position to ensure the order is always the same
|
||||
for (Polyomino& nMino : nMinos) {
|
||||
nMino.goToSpawnPosition();
|
||||
}
|
||||
|
||||
// sort the polyominos, is done after setting spawn position to ensure the order is always the same
|
||||
std::sort(nMinos.begin(), nMinos.end());
|
||||
|
||||
// write pieces
|
||||
Color pieceColor = firstPieceColor();
|
||||
for (int i = 0; i < order; i++) nextPieceColor(pieceColor);
|
||||
Block pieceblock = firstPieceBlockType();
|
||||
for (int i = 0; i < polyominoSize; i++) {
|
||||
nextPieceBlockType(pieceblock);
|
||||
}
|
||||
|
||||
for (const Polyomino& nMino : nMinos) {
|
||||
// write polyomino length
|
||||
char lengthByte = nMino.getLength();
|
||||
piecesFile.write(&lengthByte, 1);
|
||||
|
||||
// write the type and color of the piece
|
||||
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + pieceColor;
|
||||
nextPieceColor(pieceColor);
|
||||
// write the type and block of the piece
|
||||
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + pieceblock;
|
||||
nextPieceBlockType(pieceblock);
|
||||
piecesFile.write(&infoByte, 1);
|
||||
|
||||
// write the cells of the piece
|
||||
char cellByte;
|
||||
for (Cell cell : nMino.getCells()) {
|
||||
cellByte = (cell.x << 4) + cell.y;
|
||||
piecesFile.write(&cellByte, 1);
|
||||
// write the positions of the piece
|
||||
char positionByte;
|
||||
for (Position position : nMino.getPositions()) {
|
||||
positionByte = (position.x << 4) + position.y;
|
||||
piecesFile.write(&positionByte, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
||||
// open pieces file
|
||||
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
||||
std::string filePath;
|
||||
if (!this->getFilePath(order, filePath)) {
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
std::ifstream piecesFile(filePath, std::ios::binary);
|
||||
@@ -72,22 +69,20 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
|
||||
return false;
|
||||
}
|
||||
|
||||
// get empty vectors
|
||||
pieces.clear();
|
||||
convexPieces.clear();
|
||||
holelessPieces.clear();
|
||||
otherPieces.clear();
|
||||
|
||||
// set up masks
|
||||
char convexMask = 0b1000'0000;
|
||||
char holeMask = 0b0100'0000;
|
||||
char colorMask = 0b0011'1111;
|
||||
char blockMask = 0b0011'1111;
|
||||
char xMask = 0b1111'0000;
|
||||
char yMask = 0b0000'1111;
|
||||
|
||||
// read the pieces
|
||||
char lengthByte;
|
||||
int i = 0;
|
||||
// read piece length
|
||||
while (piecesFile.get(lengthByte)) {
|
||||
if (piecesFile.eof()) break;
|
||||
|
||||
@@ -96,23 +91,21 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
|
||||
piecesFile.get(infoByte);
|
||||
bool isConvex = (infoByte & convexMask) >> 7;
|
||||
bool hasHole = (infoByte & holeMask) >> 6;
|
||||
Color color = Color(infoByte & colorMask);
|
||||
Block block = Block(infoByte & blockMask);
|
||||
|
||||
// read cells
|
||||
std::set<Cell> pieceCells;
|
||||
char cellByte;
|
||||
for (int i = 0; i < order; i++) {
|
||||
piecesFile.get(cellByte);
|
||||
int x = (cellByte & xMask) >> 4;
|
||||
int y = cellByte & yMask;
|
||||
pieceCells.insert(Cell{x, y});
|
||||
// read positions
|
||||
std::set<Position> piecepositions;
|
||||
char positionByte;
|
||||
for (int i = 0; i < polyominoSize; i++) {
|
||||
piecesFile.get(positionByte);
|
||||
int x = (positionByte & xMask) >> 4;
|
||||
int y = positionByte & yMask;
|
||||
piecepositions.insert(Position{x, y});
|
||||
}
|
||||
|
||||
// create piece
|
||||
Piece readPiece(Polyomino(pieceCells, lengthByte), color);
|
||||
Piece readPiece(Polyomino(piecepositions, lengthByte), block);
|
||||
pieces.push_back(readPiece);
|
||||
|
||||
// link it to its type
|
||||
if (isConvex) {
|
||||
convexPieces.push_back(i);
|
||||
}
|
||||
@@ -129,14 +122,12 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PiecesFiles::getFilePath(int order, std::string& filePath) const {
|
||||
// verify that the data folder exists
|
||||
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
|
||||
std::string dataFolderPath = "data/pieces/";
|
||||
if (!std::filesystem::is_directory(dataFolderPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// return the file path
|
||||
filePath = dataFolderPath + std::to_string(order) + "minos.bin";
|
||||
filePath = dataFolderPath + std::to_string(polyominoSize) + "minos.bin";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
#include "Piece.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <String>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,26 +12,26 @@
|
||||
class PiecesFiles {
|
||||
public:
|
||||
/**
|
||||
* Initializes file manager
|
||||
* Default constructor
|
||||
*/
|
||||
PiecesFiles();
|
||||
|
||||
/**
|
||||
* Generate a file containing all the pieces of the specified size,
|
||||
* returns false if the file couldn't be created
|
||||
* Generate a file containing all the pieces of the specified size
|
||||
* @return If the file could be created
|
||||
*/
|
||||
bool savePieces(int order) const;
|
||||
bool savePieces(int polyominoSize) const;
|
||||
|
||||
/**
|
||||
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched,
|
||||
* returns false if the file wasn't found
|
||||
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched
|
||||
* @return If the file was found
|
||||
*/
|
||||
bool loadPieces(int order, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
||||
bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched,
|
||||
* returns false if the data folder wasn't found
|
||||
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched
|
||||
* @return If the data folder was found
|
||||
*/
|
||||
bool getFilePath(int order, std::string& filePath) const;
|
||||
bool getFilePath(int polyominoSize, std::string& filePath) const;
|
||||
};
|
||||
|
||||
@@ -1,83 +1,82 @@
|
||||
#include "Polyomino.h"
|
||||
|
||||
#include "Cell.h"
|
||||
#include "Position.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
Polyomino::Polyomino(const std::set<Cell>& cells) {
|
||||
Polyomino::Polyomino(const std::set<Position>& positions) {
|
||||
// find min/max
|
||||
int minX = INT_MAX;
|
||||
int maxX = INT_MIN;
|
||||
int minY = INT_MAX;
|
||||
int maxY = INT_MIN;
|
||||
for (Cell cell : cells) {
|
||||
if (cell.x < minX) minX = cell.x;
|
||||
if (cell.x > maxX) maxX = cell.x;
|
||||
if (cell.y < minY) minY = cell.y;
|
||||
if (cell.y > maxY) maxY = cell.y;
|
||||
for (Position position : positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.x > maxX) maxX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
if (position.y > maxY) maxY = position.y;
|
||||
}
|
||||
|
||||
// normalize
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : cells) {
|
||||
newCells.insert(Cell{cell.x - minX, cell.y - minY});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : positions) {
|
||||
newPositions.insert(Position{position.x - minX, position.y - minY});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
|
||||
// set polyomino length
|
||||
this->length = std::max(maxX - minX + 1, maxY - minY + 1);
|
||||
}
|
||||
|
||||
Polyomino::Polyomino(const std::set<Cell>& cells, int length) : cells(cells), length(length) {
|
||||
Polyomino::Polyomino(const std::set<Position>& positions, int length) :
|
||||
positions(positions),
|
||||
length(length) {
|
||||
}
|
||||
|
||||
void Polyomino::normalize() {
|
||||
// find min values
|
||||
int minX = INT_MAX;
|
||||
int minY = INT_MAX;
|
||||
for (Cell cell : this->cells) {
|
||||
if (cell.x < minX) minX = cell.x;
|
||||
if (cell.y < minY) minY = cell.y;
|
||||
for (Position position : this->positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
}
|
||||
|
||||
// translate the polyomino to the lowest unsigned values
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : this->cells) {
|
||||
newCells.insert(Cell{cell.x - minX, cell.y - minY});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
newPositions.insert(Position{position.x - minX, position.y - minY});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
}
|
||||
|
||||
void Polyomino::rotateCW() {
|
||||
// rotate 90° clockwise
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : this->cells) {
|
||||
newCells.insert(Cell{cell.y, (length - 1) - (cell.x)});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
newPositions.insert(Position{position.y, (length - 1) - (position.x)});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
}
|
||||
|
||||
void Polyomino::rotate180() {
|
||||
// rotate 180°
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : this->cells) {
|
||||
newCells.insert(Cell{(length - 1) - (cell.x), (length - 1) - (cell.y)});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
}
|
||||
|
||||
void Polyomino::rotateCCW() {
|
||||
// rotate 90° counter-clockwise
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : this->cells) {
|
||||
newCells.insert(Cell{(length - 1) - (cell.y), cell.x});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
newPositions.insert(Position{(length - 1) - (position.y), position.x});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
}
|
||||
|
||||
void Polyomino::goToSpawnPosition() {
|
||||
@@ -91,15 +90,15 @@ void Polyomino::goToSpawnPosition() {
|
||||
linesCompleteness.push_back(empty);
|
||||
}
|
||||
|
||||
// calculates amount of cells per rows and columns
|
||||
for (Cell cell : this->cells) {
|
||||
linesCompleteness.at(0).at(cell.y) += 1; // 0 = bottom to top = no rotation
|
||||
linesCompleteness.at(1).at((length - 1) - cell.x) += 1; // 1 = right to left = CW
|
||||
linesCompleteness.at(2).at((length - 1) - cell.y) += 1; // 2 = top to bottom = 180
|
||||
linesCompleteness.at(3).at(cell.x) += 1; // 3 = left to right = CCW
|
||||
// calculates amount of squares per rows and columns
|
||||
for (Position position : this->positions) {
|
||||
linesCompleteness.at(0).at(position.y) += 1; // 0 = bottom to top = no rotation
|
||||
linesCompleteness.at(1).at((length - 1) - position.x) += 1; // 1 = right to left = CW
|
||||
linesCompleteness.at(2).at((length - 1) - position.y) += 1; // 2 = top to bottom = 180
|
||||
linesCompleteness.at(3).at(position.x) += 1; // 3 = left to right = CCW
|
||||
}
|
||||
|
||||
// checks for empty lines
|
||||
// count empty lines
|
||||
int horizontalEmptyLines = 0;
|
||||
int verticalEmptyLines = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -157,26 +156,24 @@ void Polyomino::goToSpawnPosition() {
|
||||
// find min
|
||||
int minX = INT_MAX;
|
||||
int minY = INT_MAX;
|
||||
for (Cell cell : this->cells) {
|
||||
if (cell.x < minX) minX = cell.x;
|
||||
if (cell.y < minY) minY = cell.y;
|
||||
for (Position position : this->positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
}
|
||||
|
||||
// center the piece with an up bias if it is assymetric
|
||||
if (sideToBeOn % 2 == 1) {
|
||||
std::swap(verticalEmptyLines, horizontalEmptyLines);
|
||||
}
|
||||
std::set<Cell> newCells;
|
||||
for (Cell cell : cells) {
|
||||
newCells.insert(Cell{(cell.x - minX) + (verticalEmptyLines / 2), (cell.y - minY) + ((horizontalEmptyLines + 1) / 2)});
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : positions) {
|
||||
newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)});
|
||||
}
|
||||
this->cells = newCells;
|
||||
this->positions = newPositions;
|
||||
}
|
||||
|
||||
void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesCompleteness, bool currentFlattestSides[4], int& sideToBeOn, bool checkLeftSide) const {
|
||||
// for each line
|
||||
for (int j = 0; j < this->length; j++) {
|
||||
// we check which sides are the flattest
|
||||
int max = 0;
|
||||
std::set<int> maxOwners;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -200,7 +197,7 @@ void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesC
|
||||
}
|
||||
}
|
||||
|
||||
// if there's no tie we choose this side
|
||||
// if there's no tie we choose the only side
|
||||
if (maxOwners.size() == 1) {
|
||||
sideToBeOn = *maxOwners.begin();
|
||||
return;
|
||||
@@ -216,7 +213,7 @@ void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesC
|
||||
}
|
||||
|
||||
bool Polyomino::isConvex() const {
|
||||
// for each line and column we check if every cells are adjacent to each others
|
||||
// for each line and column we check if every squares are adjacent to each others
|
||||
for (int j = 0; j < this->length; j++) {
|
||||
bool startedLine = false;
|
||||
bool completedLine = false;
|
||||
@@ -224,7 +221,7 @@ bool Polyomino::isConvex() const {
|
||||
bool completedColumn = false;
|
||||
for (int i = 0; i < this->length; i++) {
|
||||
// line check
|
||||
if (this->cells.contains(Cell{i, j})) {
|
||||
if (this->positions.contains(Position{i, j})) {
|
||||
if (completedLine) return false;
|
||||
else startedLine = true;
|
||||
}
|
||||
@@ -232,7 +229,7 @@ bool Polyomino::isConvex() const {
|
||||
if (startedLine) completedLine = true;
|
||||
}
|
||||
// column check
|
||||
if (this->cells.contains(Cell{j, i})) {
|
||||
if (this->positions.contains(Position{j, i})) {
|
||||
if (completedColumn) return false;
|
||||
else startedColumn = true;
|
||||
}
|
||||
@@ -245,71 +242,65 @@ bool Polyomino::isConvex() const {
|
||||
}
|
||||
|
||||
bool Polyomino::hasHole() const {
|
||||
// add every outer cells of the square containing the polyomino
|
||||
std::set<Cell> emptyCells;
|
||||
// add every empty square on the outer of the box containing the polyomino
|
||||
std::set<Position> emptyPositions;
|
||||
for (int i = 0; i < this->length - 1; i++) {
|
||||
this->tryToInsertCell(emptyCells, Cell{i, 0}); // up row
|
||||
this->tryToInsertCell(emptyCells, Cell{this->length - 1, i}); // rigth column
|
||||
this->tryToInsertCell(emptyCells, Cell{this->length - 1 - i, this->length - 1}); // bottom row
|
||||
this->tryToInsertCell(emptyCells, Cell{0, this->length - 1 - i}); // left column
|
||||
this->tryToInsertPosition(emptyPositions, Position{i, 0}); // up row
|
||||
this->tryToInsertPosition(emptyPositions, Position{this->length - 1, i}); // rigth column
|
||||
this->tryToInsertPosition(emptyPositions, Position{this->length - 1 - i, this->length - 1}); // bottom row
|
||||
this->tryToInsertPosition(emptyPositions, Position{0, this->length - 1 - i}); // left column
|
||||
}
|
||||
|
||||
// if we didn't reached all empty cells in the square then there was some contained within the polyomino, i.e. there was a hole
|
||||
return (emptyCells.size() < (this->length * this->length) - this->cells.size());
|
||||
// if we didn't reached all empty squares in the box then there was some contained within the polyomino, i.e. there was a hole
|
||||
return (emptyPositions.size() < (this->length * this->length) - this->positions.size());
|
||||
}
|
||||
|
||||
void Polyomino::tryToInsertCell(std::set<Cell>& emptyCells, const Cell& candidate) const {
|
||||
// check if the cell is in the square containing the polyomino
|
||||
void Polyomino::tryToInsertPosition(std::set<Position>& emptyPositions, const Position& candidate) const {
|
||||
if (candidate.x >= this->length || candidate.x < 0 || candidate.y >= this->length || candidate.y < 0) return;
|
||||
if (this->positions.contains(candidate) || emptyPositions.contains(candidate)) return;
|
||||
|
||||
// check if the cell is empty and hasn't already been tested
|
||||
if (this->cells.contains(candidate) || emptyCells.contains(candidate)) return;
|
||||
|
||||
// adds the cell to the list of empty cells and try its neighbors
|
||||
emptyCells.insert(candidate);
|
||||
tryToInsertCell(emptyCells, Cell{candidate.x, candidate.y + 1});
|
||||
tryToInsertCell(emptyCells, Cell{candidate.x + 1, candidate.y});
|
||||
tryToInsertCell(emptyCells, Cell{candidate.x, candidate.y - 1});
|
||||
tryToInsertCell(emptyCells, Cell{candidate.x - 1, candidate.y});
|
||||
// if it's a new empty square, try its neighbors
|
||||
emptyPositions.insert(candidate);
|
||||
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y + 1});
|
||||
tryToInsertPosition(emptyPositions, Position{candidate.x + 1, candidate.y});
|
||||
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y - 1});
|
||||
tryToInsertPosition(emptyPositions, Position{candidate.x - 1, candidate.y});
|
||||
}
|
||||
|
||||
std::set<Cell> Polyomino::getCells() const {
|
||||
return this->cells;
|
||||
std::set<Position> Polyomino::getPositions() const {
|
||||
return this->positions;
|
||||
}
|
||||
|
||||
int Polyomino::getLength() const {
|
||||
return this->length;
|
||||
}
|
||||
|
||||
int Polyomino::getPolyominoOrder() const {
|
||||
return this->cells.size();
|
||||
int Polyomino::getPolyominoSize() const {
|
||||
return this->positions.size();
|
||||
}
|
||||
|
||||
bool Polyomino::operator<(const Polyomino& other) const {
|
||||
// if one has an inferior length then it is deemed inferior
|
||||
if (this->length != other.length) return this->length < other.length;
|
||||
|
||||
// else we check for all cells from left to right and top to bottom, until one has a cell that the other doesn't
|
||||
// we check for all positions from left to right and top to bottom, until one has a square that the other doesn't
|
||||
for (int y = this->length - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < this->length; x++) {
|
||||
bool hasThisCell = this->cells.contains(Cell{x, y});
|
||||
bool hasOtherCell = other.cells.contains(Cell{x, y});
|
||||
if (hasThisCell != hasOtherCell) return hasThisCell;
|
||||
bool hasThisposition = this->positions.contains(Position{x, y});
|
||||
bool hasOtherposition = other.positions.contains(Position{x, y});
|
||||
if (hasThisposition != hasOtherposition) return hasThisposition;
|
||||
}
|
||||
}
|
||||
|
||||
// if they are equal
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyomino::operator ==(const Polyomino& other) const {
|
||||
return this->cells == other.cells;
|
||||
return this->positions == other.positions;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) {
|
||||
for (int y = polyomino.length - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < polyomino.length; x++) {
|
||||
if (polyomino.cells.contains(Cell{x, y})) {
|
||||
if (polyomino.positions.contains(Position{x, y})) {
|
||||
os << "*";
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cell.h"
|
||||
#include "Position.h"
|
||||
|
||||
#include <Vector>
|
||||
#include <Set>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
*/
|
||||
class Polyomino {
|
||||
private:
|
||||
std::set<Cell> cells; // the squares composing the polyomino, (0,0) is downleft
|
||||
int length; // the size of the smallest square in which the polyomino can fit on any rotation
|
||||
std::set<Position> positions; // the squares composing the polyomino, (0,0) is downleft
|
||||
int length; // the size of the smallest square box in which the polyomino can fit on any rotation
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a polyomino with the specified cells and normalizes it, wheter it is actually a polyonimo is not checked
|
||||
* Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked
|
||||
*/
|
||||
Polyomino(const std::set<Cell>& cells);
|
||||
Polyomino(const std::set<Position>& positions);
|
||||
|
||||
/**
|
||||
* Creates a polyomino with the specified cells and length, wheter it is actually a polyonimo of this length is not checked
|
||||
* Creates a polyomino with the specified positions and length, wheter it is actually a polyonimo of this length is not checked
|
||||
*/
|
||||
Polyomino(const std::set<Cell>& cells, int length);
|
||||
Polyomino(const std::set<Position>& positions, int length);
|
||||
|
||||
/**
|
||||
* Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0)
|
||||
@@ -32,17 +32,17 @@ class Polyomino {
|
||||
void normalize();
|
||||
|
||||
/**
|
||||
* Rotates the polyomino 90° clockwise, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
|
||||
* Rotates the polyomino 90° clockwise, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
|
||||
*/
|
||||
void rotateCW();
|
||||
|
||||
/**
|
||||
* Rotates the polyomino 180°, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
|
||||
* Rotates the polyomino 180°, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
|
||||
*/
|
||||
void rotate180();
|
||||
|
||||
/**
|
||||
* Rotates the polyomino 90° counter-clockwise, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
|
||||
* Rotates the polyomino 90° counter-clockwise, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
|
||||
*/
|
||||
void rotateCCW();
|
||||
|
||||
@@ -59,12 +59,14 @@ class Polyomino {
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns wheter the polyomino is convex, that is if every line and column has at most one continuous line of cells
|
||||
* Check if the polyomino is convex, that is if every line and column has at most one continuous line of positions
|
||||
* @return If the polyomino is convex
|
||||
*/
|
||||
bool isConvex() const;
|
||||
|
||||
/**
|
||||
* Returns wheter the polyomino has at least one hole
|
||||
* Check if the polyomino has at least one hole
|
||||
* @return If the polyomino has at least one hole
|
||||
*/
|
||||
bool hasHole() const;
|
||||
|
||||
@@ -72,37 +74,40 @@ class Polyomino {
|
||||
/**
|
||||
* Auxiliary method of hasHole()
|
||||
*/
|
||||
void tryToInsertCell(std::set<Cell>& emptyCells, const Cell& candidate) const;
|
||||
void tryToInsertPosition(std::set<Position>& emptypositions, const Position& candidate) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns a copy of the cells of the polyomino
|
||||
* @return A copy of the positions of the polyomino
|
||||
*/
|
||||
std::set<Cell> getCells() const;
|
||||
std::set<Position> getPositions() const;
|
||||
|
||||
/**
|
||||
* Returns the length of the polyomino
|
||||
* @return The length of the polyomino
|
||||
*/
|
||||
int getLength() const;
|
||||
|
||||
/**
|
||||
* Returns the number of squares in the polyomino
|
||||
* @return The number of squares in the polyomino
|
||||
*/
|
||||
int getPolyominoOrder() const;
|
||||
int getPolyominoSize() const;
|
||||
|
||||
/**
|
||||
* Strict inferiority operator, a polyomino is inferior than another if it has a smaller length, or if they are the same length,
|
||||
* while checking from left to right and top to bottom, is the first which has a cell while the other doesn't
|
||||
* while checking from left to right and top to bottom, is the first which has a square while the other don't
|
||||
* @return If the polyomino is inferior than another
|
||||
*/
|
||||
bool operator<(const Polyomino& other) const;
|
||||
|
||||
/**
|
||||
* Equality operator, two polyominos are equal if they overlap, that means two polyominos of the same shape but different positions will not be equal
|
||||
* Equality operator, two polyominos are equal if their positions are the same, that means two polyominos of the same shape at different places will not be equal
|
||||
* @return If the polyomino is equal to another
|
||||
*/
|
||||
bool operator ==(const Polyomino& other) const;
|
||||
|
||||
/**
|
||||
* Stream output operator, adds a 2D grid representing the polyomino
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
friend std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino);
|
||||
};
|
||||
|
||||
72
src/Pieces/Position.h
Normal file
72
src/Pieces/Position.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/**
|
||||
* A position on a 2D grid
|
||||
*/
|
||||
struct Position {
|
||||
int x; // x position
|
||||
int y; // y position
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Addition operator
|
||||
* @return The sums of the coordinates of both positions
|
||||
*/
|
||||
inline Position operator+(const Position& left, const Position& right) {
|
||||
return Position{left.x + right.x, left.y + right.y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Additive assignation operator, adds the coordinates of the right position to the left one
|
||||
* @return A reference to the left position
|
||||
*/
|
||||
inline Position& operator+=(Position& left, const Position& right) {
|
||||
left = left + right;
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substraction operator
|
||||
* @return The difference of the coordinate between the left and right position
|
||||
*/
|
||||
inline Position operator-(const Position& left, const Position& right) {
|
||||
return Position{left.x - right.x, left.y - right.y};
|
||||
}
|
||||
|
||||
/**
|
||||
* Substractive assignation operator, substracts the coordinates of the right position from the left one
|
||||
* @return A reference to the left position
|
||||
*/
|
||||
inline Position& operator-=(Position& left, const Position& right) {
|
||||
left = left + right;
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strict inferiority operator, a position is inferior to another if it is lower or at the same height and more to the left
|
||||
* @return If the left position is inferior to the right position
|
||||
*/
|
||||
inline bool operator<(const Position& left, const Position& right) {
|
||||
return (left.x == right.x) ? (left.y < right.y) : (left.x < right.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality operator, two positions are equal if they have the same coordinates
|
||||
* @return If the two positions are equals
|
||||
*/
|
||||
inline bool operator==(const Position& left, const Position& right) {
|
||||
return (left.x == right.x) && (left.y == right.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream output operator, adds the coordinates of the position to the stream
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& os, const Position& position) {
|
||||
os << "x: " << position.x << " y: " << position.y;
|
||||
return os;
|
||||
}
|
||||
@@ -13,14 +13,16 @@ enum Rotation {
|
||||
|
||||
|
||||
/**
|
||||
* Addition operator, returns a rotation corresponding to doing both rotations
|
||||
* Addition operator
|
||||
* @return A rotation corresponding to doing both rotations
|
||||
*/
|
||||
inline Rotation operator+(const Rotation& left, const Rotation& right) {
|
||||
return Rotation((left + right) % 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Additive assignation operator, rotate the left rotation by the right rotation
|
||||
* Additive assignation operator, rotates the left rotation by the right rotation
|
||||
* @return A reference to the left rotation
|
||||
*/
|
||||
inline Rotation& operator+=(Rotation& left, const Rotation& right) {
|
||||
left = left + right;
|
||||
|
||||
Reference in New Issue
Block a user