diff --git a/README.md b/README.md index d324fa2..7e1e320 100644 --- a/README.md +++ b/README.md @@ -115,21 +115,21 @@ To package the executable manually properly, follow theses steps: | n | Number | Generation | File storing | File retrieving | File size | | -: | -: | :-: | :-: | :-: | -: | -| 1 | 1 | 0s 0.005471ms | 0s 0.14436ms | 0s 0.022223ms | 3 bytes | -| 2 | 1 | 0s 0.006979ms | 0s 0.036624ms | 0s 0.011424ms | 4 bytes | -| 3 | 2 | 0s 0.018718ms | 0s 0.035885ms | 0s 0.013246ms | 9 bytes | -| 4 | 7 | 0s 0.060544ms | 0s 0.056277ms | 0s 0.019395ms | 36 bytes | -| 5 | 18 | 0s 0.220348ms | 0s 0.166593ms | 0s 0.036526ms | 76 bytes | -| 6 | 60 | 0s 0.773924ms | 0s 0.283423ms | 0s 0.063492ms | 186 bytes | -| 7 | 196 | 0s 3.00331ms | 0s 0.827344ms | 0s 0.163653ms | 546 bytes | -| 8 | 704 | 0s 13.142ms | 0s 3.68255ms | 0s 0.630044ms | 1 898 bytes | -| 9 | 2500 | 0s 50.9272ms | 0s 16.1929ms | 0s 2.35157ms | 6889 bytes | -| 10 | 9189 | 0s 204.031ms | 0s 87.1819ms | 0s 10.5841ms | 25302 bytes | -| 11 | 33896 | 0s 832.82ms | 0s 412.466ms | 0s 57.6399ms | 93711 bytes | -| 12 | 126759 | 3s 425.907ms | 1s 982.715ms | 0s 226.816ms | 350325 bytes | -| 13 | 476270 | 14s 570.595ms | 9s 945.511ms | 0s 972.036ms | 1327156 bytes | -| 14 | 1802312 | 56s 394.426ms | 41s 675.672ms | 4s 79.0436ms | 5035148 bytes | -| 15 | 6849777 | 258s 219.666ms | 223s 386.329ms | 16s 483.426ms | 19392417 bytes | +| 1 | 1 | 0s 0.006496ms | 0s 0.18186ms | 0s 0.064298ms | 3 bytes | +| 2 | 1 | 0s 0.003848ms | 0s 0.167537ms | 0s 0.013054ms | 3 bytes | +| 3 | 2 | 0s 0.010133ms | 0s 0.076844ms | 0s 0.011991ms | 6 bytes | +| 4 | 7 | 0s 0.027356ms | 0s 0.050884ms | 0s 0.016362ms | 22 bytes | +| 5 | 18 | 0s 0.082079ms | 0s 0.088215ms | 0s 0.015016ms | 63 bytes | +| 6 | 60 | 0s 0.292599ms | 0s 0.254024ms | 0s 0.034325ms | 201 bytes | +| 7 | 196 | 0s 1.15671ms | 0s 0.424141ms | 0s 0.061736ms | 538 bytes | +| 8 | 704 | 0s 7.32448ms | 0s 2.00197ms | 0s 0.161199ms | 1803 bytes | +| 9 | 2500 | 0s 23.1679ms | 0s 4.30285ms | 0s 0.496256ms | 6291 bytes | +| 10 | 9189 | 0s 76.8277ms | 0s 20.5346ms | 0s 1.99335ms | 22396 bytes | +| 11 | 33896 | 0s 292.303ms | 0s 84.7814ms | 0s 7.11621ms | 79040 bytes | +| 12 | 126759 | 1s 368.505ms | 0s 407.837ms | 0s 33.5164ms | 279497 bytes | +| 13 | 476270 | 5s 963.297ms | 1s 709.14ms | 0s 171.402ms | 1010672 bytes | +| 14 | 1802312 | 25s 319.931ms | 3s 853.451ms | 0s 580.736ms | 3750531 bytes | +| 15 | 6849777 | 102s 886.385ms | 21s 198.445ms | 1s 973.051ms | 14162217 bytes | _File storing includes type checking and sorting all polyominoes before writing them to the file._ The files are compressed, they used to be about 5x as large. diff --git a/src/Core/Bag.cpp b/src/Core/Bag.cpp index 1f5479d..c419300 100644 --- a/src/Core/Bag.cpp +++ b/src/Core/Bag.cpp @@ -35,7 +35,7 @@ Bag::Bag(const std::shared_ptr& piecesList) : } for (const auto& piece : this->selectedPieces) { - int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().size(); + int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().getSize(); this->currentBags.at(pieceSize).push_back(piece); } } diff --git a/src/Core/Board.cpp b/src/Core/Board.cpp index 7006d51..848736d 100644 --- a/src/Core/Board.cpp +++ b/src/Core/Board.cpp @@ -20,10 +20,10 @@ Board::Board(int width, int height) : } void Board::changeBlock(const Position& position, Block block) { - if (position.x < 0 || position.x >= this->width || position.y < 0) return; + if (position.x < 0 || static_cast(position.x) >= this->width || position.y < 0) return; // resize the grid if needed - if (position.y >= this->grid.size()) { + if (static_cast(position.y) >= this->grid.size()) { for (int j = this->grid.size(); j <= position.y; j++) { this->grid.push_back(this->emptyRow); } @@ -34,7 +34,7 @@ void Board::changeBlock(const Position& position, Block block) { void Board::insertRow(int height, int holePosition, Block blockType) { std::vector insertedRow; - for (int i = 0; i < this->width; i++) { + for (unsigned i = 0; i < this->width; i++) { if (i == holePosition) { insertedRow.push_back(NOTHING); } @@ -51,7 +51,7 @@ int Board::clearRows() { int clearedLines = 0; for (int j = this->grid.size() - 1; j >= 0; j--) { bool lineIsFull = true; - int i = 0; + unsigned i = 0; while (lineIsFull && (i < width)) { if (this->grid.at(j).at(i) == NOTHING) { lineIsFull = false; diff --git a/src/Core/Board.h b/src/Core/Board.h index fcd48fa..cc5f107 100644 --- a/src/Core/Board.h +++ b/src/Core/Board.h @@ -13,8 +13,8 @@ class Board { private: std::vector> grid; // the grid, (0,0) is downleft std::vector emptyRow; // an empty row of blocks - int width; // the width of the grid - int height; // the base height of the grid, which can extend indefinitely + unsigned width; // the width of the grid + unsigned height; // the base height of the grid, which can extend indefinitely public: /** diff --git a/src/Core/GameBoard.cpp b/src/Core/GameBoard.cpp index 75a4ab1..1b79b20 100644 --- a/src/Core/GameBoard.cpp +++ b/src/Core/GameBoard.cpp @@ -44,7 +44,7 @@ void GameBoard::initialize() { bool GameBoard::moveLeft() { this->movedLeftLast = true; - if (this->activePieceInWall(Position{-1, 0})) { + if (this->activePieceInWall(Position(-1, 0))) { return false; } else { @@ -57,7 +57,7 @@ bool GameBoard::moveLeft() { bool GameBoard::moveRight() { this->movedLeftLast = false; - if (this->activePieceInWall(Position{1, 0})) { + if (this->activePieceInWall(Position(1, 0))) { return false; } else { @@ -68,7 +68,7 @@ bool GameBoard::moveRight() { } bool GameBoard::moveDown() { - if (this->activePieceInWall(Position{0, -1})) { + if (this->activePieceInWall(Position(0, -1))) { return false; } else { @@ -100,10 +100,10 @@ bool GameBoard::rotate(Rotation rotation) { 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}); + safePositions.insert(positionInGrid + Position(0, 1)); + safePositions.insert(positionInGrid + Position(1, 0)); + safePositions.insert(positionInGrid + Position(0, -1)); + safePositions.insert(positionInGrid + Position(-1, 0)); } // first try kicking the piece down @@ -147,18 +147,18 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set& safePos // we first check the side to which the player moved last if (movedLeftLast) { if (overlapsLeft) { - if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true; + if (this->tryFittingKickedPiece(safePositions, Position(-i, j), overlapsLeft)) return true; } if (overlapsRight) { - if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true; + if (this->tryFittingKickedPiece(safePositions, Position(+i, j), overlapsRight)) return true; } } else { if (overlapsRight) { - if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true; + if (this->tryFittingKickedPiece(safePositions, Position(+i, j), overlapsRight)) return true; } if (overlapsLeft) { - if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true; + if (this->tryFittingKickedPiece(safePositions, Position(-i, j), overlapsLeft)) return true; } } @@ -265,11 +265,11 @@ bool GameBoard::activePieceInWall(const Position& shift) const { } bool GameBoard::touchesGround() const { - return this->activePieceInWall(Position{0, -1}); + return this->activePieceInWall(Position(0, -1)); } Position GameBoard::lowestPosition() const { - Position shift = Position{0, -1}; + Position shift = Position(0, -1); while (!activePieceInWall(shift)) { shift.y -= 1; } @@ -278,8 +278,8 @@ Position GameBoard::lowestPosition() const { } LineClear GameBoard::lockPiece() { - bool isLockedInPlace = (this->activePieceInWall(Position{0, 1}) && this->activePieceInWall(Position{1, 0}) - && this->activePieceInWall(Position{-1, 0}) && this->activePieceInWall(Position{0, -1})); + bool isLockedInPlace = (this->activePieceInWall(Position(0, 1)) && this->activePieceInWall(Position(1, 0)) + && this->activePieceInWall(Position(-1, 0)) && this->activePieceInWall(Position(0, -1))); for (Position position : this->activePiece->getPositions()) { this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType()); @@ -345,7 +345,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) { // 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(Position{x, y} - gameboard.activePiecePosition); + bool hasActivePiece = gameboard.activePiece->containsSquare(Position(x, y) - gameboard.activePiecePosition); if (hasActivePiece) { os << "*"; } @@ -361,7 +361,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) { 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(Position{x, y} - gameboard.activePiecePosition); + bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->containsSquare(Position(x, y) - gameboard.activePiecePosition); // the active piece takes visual priority over the board if (hasActivePiece) { @@ -369,7 +369,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) { os << "*"; } else { - Block block = gameboard.board.getBlock(Position{x, y}); + Block block = gameboard.board.getBlock(Position(x, y)); os << getConsoleColorCode(block); if (block != NOTHING) { os << "*"; diff --git a/src/Core/GameBoard.h b/src/Core/GameBoard.h index 19a0ae3..58480de 100644 --- a/src/Core/GameBoard.h +++ b/src/Core/GameBoard.h @@ -84,7 +84,7 @@ class GameBoard { * 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 activePieceOverlaps(const std::set& safePositions, const Position& shift = Position{0, 0}) const; + bool activePieceOverlaps(const std::set& safePositions, const Position& shift = Position(0, 0)) const; public: /** @@ -103,7 +103,7 @@ class GameBoard { * 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 activePieceInWall(const Position& shift = Position{0, 0}) const; + bool activePieceInWall(const Position& shift = Position(0, 0)) const; /** * Checks is the active piece as a wall directly below one of its position diff --git a/src/Core/PiecesList.cpp b/src/Core/PiecesList.cpp index a6bd0d3..614fd11 100644 --- a/src/Core/PiecesList.cpp +++ b/src/Core/PiecesList.cpp @@ -27,12 +27,12 @@ PiecesList::PiecesList() { this->pushBackEmptyVectors(); } -bool PiecesList::loadPieces(int max_size) { +bool PiecesList::loadPieces(unsigned max_size) { if (max_size < 1) return false; if (max_size <= this->highestLoadedSize) return true; PiecesFiles piecesFiles; - for (int i = this->highestLoadedSize + 1; i <= max_size; i++) { + for (unsigned i = this->highestLoadedSize + 1; i <= max_size; i++) { if (!piecesFiles.loadPieces(i, this->loadedPieces.at(i), this->convexPieces.at(i), this->holelessPieces.at(i), this->otherPieces.at(i))) { return false; } @@ -45,14 +45,14 @@ bool PiecesList::loadPieces(int max_size) { return true; } -bool PiecesList::selectPiece(int size, int number) { +bool PiecesList::selectPiece(unsigned size, unsigned number) { if (size < 1 || size > this->highestLoadedSize || number >= this->loadedPieces.at(size).size()) return false; this->selectedPieces.push_back(std::pair(size, number)); return true; } -bool PiecesList::selectAllPieces(int size) { +bool PiecesList::selectAllPieces(unsigned size) { if (size < 1 || size > this->highestLoadedSize) return false; for (int i = 0; i < this->loadedPieces.at(size).size(); i++) { diff --git a/src/Core/PiecesList.h b/src/Core/PiecesList.h index 8c4991f..2150d0c 100644 --- a/src/Core/PiecesList.h +++ b/src/Core/PiecesList.h @@ -13,7 +13,7 @@ */ class PiecesList { private: - int highestLoadedSize; // the highest size of pieces currently loaded + unsigned highestLoadedSize; // the highest size of pieces currently loaded std::vector> loadedPieces; // every loaded pieces by size std::vector> convexPieces; // the list of convex loaded pieces by size std::vector> holelessPieces; // the list of holeless loaded pieces by size @@ -33,19 +33,19 @@ class PiecesList { * Makes the list load all pieces up to the specified size * @return If all pieces up to the specified size are correctly loaded */ - [[nodiscard]] bool loadPieces(int max_size); + [[nodiscard]] bool loadPieces(unsigned max_size); /** * Selects the specified piece * @return If the piece could be selected */ - bool selectPiece(int size, int number); + bool selectPiece(unsigned size, unsigned number); /** * Selects all pieces of the specified size * @return If the pieces could be selected */ - bool selectAllPieces(int size); + bool selectAllPieces(unsigned size); /** * Selects all convex pieces of the specified size diff --git a/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp b/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp index 09772df..26c383e 100644 --- a/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp +++ b/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp @@ -117,7 +117,7 @@ void GamePlayingAppMenu::drawFrame() const { // board for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) { for (int x = 0; x < this->game.getBoard().getWidth(); x++) { - Block block = this->game.getBoard().getBlock(Position{x, y}); + Block block = this->game.getBoard().getBlock(Position(x, y)); if (isBoardInvisible) block = NOTHING; sf::RectangleShape cell(cellSize); @@ -196,7 +196,7 @@ void GamePlayingAppMenu::drawFrame() const { for (int y = 0; y < this->game.getNextPieces().at(i).getLength(); y++) { for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) { sf::RectangleShape cell(nextCellSize); - if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) { + if (this->game.getNextPieces().at(i).containsSquare(Position(x, y))) { cell.setFillColor(pieceColor); lowestRank = y; } @@ -223,7 +223,7 @@ void GamePlayingAppMenu::drawFrame() const { for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) { for (int x = 0; x < this->game.getHeldPiece()->getLength(); x++) { sf::RectangleShape cell(holdCellSize); - if (this->game.getHeldPiece()->getPositions().contains(Position{x, y})) { + if (this->game.getHeldPiece()->containsSquare(Position(x, y))) { cell.setFillColor(color); } else { diff --git a/src/Pieces/Generator.cpp b/src/Pieces/Generator.cpp index 34d4af0..dd12183 100644 --- a/src/Pieces/Generator.cpp +++ b/src/Pieces/Generator.cpp @@ -11,25 +11,25 @@ Generator::Generator() { } -std::vector Generator::generatePolyominoes(int polyominoSize) { +std::vector&& Generator::generatePolyominoes(int polyominoSize) { this->validPolyominoes.clear(); this->currentTestedShape.clear(); // a polyomino has at least 1 square - if (polyominoSize < 1) return this->validPolyominoes; + if (polyominoSize < 1) return std::move(this->validPolyominoes); // always place the first cell at (0, 0) - this->currentTestedShape.insert(Position{0, 0}); + this->currentTestedShape.insert(Position(0, 0)); std::map candidatePositions; this->generate(polyominoSize, 0, 1, candidatePositions); - return this->validPolyominoes; + return std::move(this->validPolyominoes); } -void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map candidatePositions) { +void Generator::generate(unsigned polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map candidatePositions) { // recursion stop if (polyominoSize == this->currentTestedShape.size()) { - Polyomino candidate(this->currentTestedShape); + Polyomino candidate(std::move(this->currentTestedShape)); // we sort the rotations of the polyominoes std::vector candidateRotations; @@ -51,14 +51,14 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex // generate the list of candidate positions for (const 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); + 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); } // try adding a square only to positions with a higher number than the last one - for (const auto [key, val] : candidatePositions) { + for (const auto& [key, val] : candidatePositions) { if (val > lastAddedPositionNumber) { this->currentTestedShape.insert(key); this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map() : candidatePositions); diff --git a/src/Pieces/Generator.h b/src/Pieces/Generator.h index 47de7d5..339c5f3 100644 --- a/src/Pieces/Generator.h +++ b/src/Pieces/Generator.h @@ -25,13 +25,13 @@ class Generator { * Generates the list of all one-sided polyominoes of the specified size * @return The list of polyominoes */ - std::vector generatePolyominoes(int polyominoSize); + std::vector&& generatePolyominoes(int polyominoSize); private: /** * Generates all one-sided polyominoes of the specified size using the current tested shape */ - void generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map candidatePositions); + void generate(unsigned polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map candidatePositions); /** * Checks wheter a candidate position can be added to the current tested shape diff --git a/src/Pieces/Piece.cpp b/src/Pieces/Piece.cpp index 7023cf7..28fc372 100644 --- a/src/Pieces/Piece.cpp +++ b/src/Pieces/Piece.cpp @@ -9,11 +9,10 @@ #include -Piece::Piece(const Polyomino& polyomino, Block blockType) : +Piece::Piece(Polyomino&& polyomino, Block blockType) : polyomino(polyomino), - blockType(blockType) { - - this->rotationState = NONE; + blockType(blockType), + rotationState(NONE) { } void Piece::rotate(Rotation rotation) { @@ -38,8 +37,8 @@ void Piece::defaultRotation() { this->rotationState = NONE; } -const std::set& Piece::getPositions() const { - return this->polyomino.getPositions(); +const Polyomino& Piece::getPositions() const { + return this->polyomino; } int Piece::getLength() const { @@ -54,3 +53,7 @@ std::ostream& operator<<(std::ostream& os, const Piece& piece) { os << getConsoleColorCode(piece.blockType) << piece.polyomino << getResetConsoleColorCode(); return os; } + +bool Piece::containsSquare(const Position& position) const { + return polyomino.contains(position); +} \ No newline at end of file diff --git a/src/Pieces/Piece.h b/src/Pieces/Piece.h index cd74eb1..012d94d 100644 --- a/src/Pieces/Piece.h +++ b/src/Pieces/Piece.h @@ -21,7 +21,7 @@ class Piece { /** * Creates a piece with a specified shape and block type */ - Piece(const Polyomino& piece, Block blockType); + Piece(Polyomino&& piece, Block blockType); /** * Rotates the piece in the specified direction @@ -36,7 +36,13 @@ class Piece { /** * @return The list of positions of the piece */ - const std::set& getPositions() const; + const Polyomino& getPositions() const; + + /** + * @param the position of the square + * @return false if there is a hole + */ + bool containsSquare(const Position& position) const; /** * @return The length of the piece diff --git a/src/Pieces/PiecesFiles.cpp b/src/Pieces/PiecesFiles.cpp index 6500f41..2881335 100644 --- a/src/Pieces/PiecesFiles.cpp +++ b/src/Pieces/PiecesFiles.cpp @@ -50,11 +50,18 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector& polyomin std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength(); buffer << infoByte; + const int bitsNeeded = polyomino.getLength() * polyomino.getLength(); + const int bytesNeeded = bitsNeeded / 8 + 1; + + static const Polyomino::PolyominoData byteMask = 0xFF; + const Polyomino::PolyominoData& polyominoData = polyomino.getPositionsData(); + // write the positions of the piece - std::uint8_t positionByte; - for (const Position position : polyomino.getPositions()) { - positionByte = (position.x << 4) + position.y; - buffer << positionByte; + for (int i = 0; i < bytesNeeded; i++) { + Polyomino::PolyominoData pData = polyominoData >> (i * 8); + unsigned long data = + (pData & byteMask).to_ulong(); + buffer << static_cast(data); } } @@ -91,13 +98,12 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector& pieces, std: nextPieceBlockType(pieceBlock); } - char convexMask = 0b1000'0000; - char holeMask = 0b0100'0000; - char lengthMask = 0b0011'1111; - char xMask = 0b1111'0000; - char yMask = 0b0000'1111; + constexpr char convexMask = 0b1000'0000; + constexpr char holeMask = 0b0100'0000; + constexpr char lengthMask = 0b0011'1111; std::uint8_t infoByte; + std::uint8_t positionByte; int i = 0; while (!buffer.IsFinished()) { // if (piecesFile.eof()) break; @@ -108,21 +114,22 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector& pieces, std: bool hasHole = (infoByte & holeMask) >> 6; int length = (infoByte & lengthMask); + const int bitsNeeded = length * length; + const int bytesNeeded = bitsNeeded / 8 + 1; + // read positions - std::set piecePositions; - std::uint8_t positionByte; - for (int i = 0; i < polyominoSize; i++) { + Polyomino::PolyominoData polyominoData{}; + + for (int j = 0; j < bytesNeeded; j++) { buffer >> positionByte; - int x = ((unsigned char) positionByte & xMask) >> 4; - int y = positionByte & yMask; - piecePositions.insert(Position{x, y}); + Polyomino::PolyominoData tempByte(positionByte); + polyominoData |= (tempByte << (j * 8)); } - // create piece - Piece readPiece(Polyomino(piecePositions, length), pieceBlock); + pieces.emplace_back(Polyomino(std::move(polyominoData), length), pieceBlock); + nextPieceBlockType(pieceBlock); - - pieces.push_back(readPiece); + if (isConvex) { convexPieces.push_back(i); } diff --git a/src/Pieces/Polyomino.cpp b/src/Pieces/Polyomino.cpp index a9fe094..81f0698 100644 --- a/src/Pieces/Polyomino.cpp +++ b/src/Pieces/Polyomino.cpp @@ -8,9 +8,9 @@ #include #include #include +#include - -Polyomino::Polyomino(const std::set& positions) { +Polyomino::Polyomino(std::set&& positions) : positions(0) { int minX = INT_MAX; int maxX = INT_MIN; int minY = INT_MAX; @@ -25,71 +25,64 @@ Polyomino::Polyomino(const std::set& positions) { this->length = std::max(maxX - minX + 1, maxY - minY + 1); // we normalize here instead of calling this->normalize() to reduce the number of calculations for the generation algorithm - std::set newPositions; + Polyomino newPolyomino({}, this->length); for (Position position : positions) { - newPositions.insert(Position{position.x - minX, position.y - minY}); + newPolyomino.insert(Position(position.x - minX, position.y - minY)); } - this->positions = std::move(newPositions); + this->positions = std::move(newPolyomino.positions); } -Polyomino::Polyomino(const std::set& positions, int length) : - positions(positions), +Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) : + positions(std::move(positions)), length(length) { } void Polyomino::normalize() { int minX = INT_MAX; int minY = INT_MAX; - for (const Position position : this->positions) { + for (const Position position : *this) { if (position.x < minX) minX = position.x; if (position.y < minY) minY = position.y; } - std::set newPositions; - for (const Position position : this->positions) { - newPositions.insert(Position{position.x - minX, position.y - minY}); + Polyomino newPolyomino({}, this->length); + for (const Position position : *this) { + newPolyomino.insert(Position(position.x - minX, position.y - minY)); } - this->positions = std::move(newPositions); + this->positions = std::move(newPolyomino.positions); } void Polyomino::rotateCW() { - std::set newPositions; - for (const Position position : this->positions) { - newPositions.insert(Position{position.y, (length - 1) - (position.x)}); + Polyomino temp({}, this->length); + for (const Position position : *this) { + temp.insert(Position(position.y, (length - 1) - (position.x))); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::rotate180() { - std::set newPositions; - for (const Position position : this->positions) { - newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)}); + Polyomino temp({}, this->length); + for (const Position position : *this) { + temp.insert(Position((length - 1) - (position.x), (length - 1) - (position.y))); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::rotateCCW() { - std::set newPositions; - for (const Position position : this->positions) { - newPositions.insert(Position{(length - 1) - (position.y), position.x}); + Polyomino temp({}, this->length); + for (const Position position : *this) { + temp.insert(Position((length - 1) - (position.y), position.x)); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::goToSpawnPosition() { // initialize array - std::vector> linesCompleteness; - linesCompleteness.reserve(4); - std::vector empty; - for (int j = 0; j < this->length; j++) { - empty.push_back(0); - } - for (int i = 0; i < 4; i++) { - linesCompleteness.push_back(empty); - } + std::vector empty(this->length, 0); + std::vector> linesCompleteness(4, empty); // calculates amount of squares per rows and columns - for (const Position position : this->positions) { + for (const Position position : *this) { 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 @@ -155,20 +148,21 @@ void Polyomino::goToSpawnPosition() { if (sideToBeOn % 2 == 1) { std::swap(verticalEmptyLines, horizontalEmptyLines); } + int minX = INT_MAX; int minY = INT_MAX; - for (const Position position : this->positions) { + for (const Position position : *this) { if (position.x < minX) minX = position.x; if (position.y < minY) minY = position.y; } // center the piece with an up bias - std::set newPositions; - for (const Position position : positions) { - newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)}); + Polyomino temp({}, this->length); + for (const Position position : *this) { + temp.insert(Position((position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2))); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::checkForFlattestSide(const std::vector>& linesCompleteness, bool currentFlattestSides[4], int& sideToBeOn, bool checkLeftSide) const { @@ -216,7 +210,7 @@ bool Polyomino::isConvex() const { bool startedColumn = false; bool completedColumn = false; for (int i = 0; i < this->length; i++) { - if (this->positions.contains(Position{i, j})) { + if (this->contains(Position(i, j))) { if (completedLine) return false; else startedLine = true; } @@ -224,7 +218,7 @@ bool Polyomino::isConvex() const { if (startedLine) completedLine = true; } - if (this->positions.contains(Position{j, i})) { + if (this->contains(Position(j, i))) { if (completedColumn) return false; else startedColumn = true; } @@ -238,31 +232,31 @@ bool Polyomino::isConvex() const { bool Polyomino::hasHole() const { // add every empty square on the outer of the box containing the polyomino - std::set emptyPositions; + Polyomino temp({}, this->length); for (int i = 0; i < this->length - 1; i++) { - 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 + this->tryToInsertPosition(temp, Position(i, 0)); // up row + this->tryToInsertPosition(temp, Position(this->length - 1, i)); // rigth column + this->tryToInsertPosition(temp, Position(this->length - 1 - i, this->length - 1)); // bottom row + this->tryToInsertPosition(temp, Position(0, this->length - 1 - i)); // left column } // 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()); + return (temp.getSize() < (this->length * this->length) - this->positions.size()); } -void Polyomino::tryToInsertPosition(std::set& emptyPositions, const Position& candidate) const { +void Polyomino::tryToInsertPosition(Polyomino& 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; + if (this->contains(candidate) || emptyPositions.contains(candidate)) return; // 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}); + 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)); } -const std::set& Polyomino::getPositions() const { +const Polyomino::PolyominoData& Polyomino::getPositionsData() const { return this->positions; } @@ -277,13 +271,19 @@ int Polyomino::getPolyominoSize() const { bool Polyomino::operator<(const Polyomino& other) const { if (this->length != other.length) return this->length < other.length; - for (int y = this->length - 1; y >= 0; y--) { - for (int x = 0; x < this->length; x++) { - bool hasThisPosition = this->positions.contains(Position{x, y}); - bool hasOtherPosition = other.positions.contains(Position{x, y}); - if (hasThisPosition != hasOtherPosition) return hasThisPosition; + assert(other.positions.any() && "The other polyomino is empty !"); + + static const PolyominoData longMask = 0xFFFFFFFFFFFFFFFF; + const int longsNeeded = this->length * this->length / 64 + 1; + + for (int i = 0; i < longsNeeded; i++) { + unsigned long l1 = (this->positions >> (i * sizeof(std::uint64_t)) & longMask).to_ulong(); + unsigned long l2 = (other.positions >> (i * sizeof(std::uint64_t)) & longMask).to_ulong(); + if (l1 != l2) { + return l1 < l2; } } + return false; } @@ -294,7 +294,7 @@ bool Polyomino::operator==(const Polyomino& other) const { 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.positions.contains(Position{x, y})) { + if (polyomino.contains(Position(x, y))) { os << "*"; } else { @@ -305,3 +305,31 @@ std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) { } return os; } + +int Polyomino::getSize() const { + return positions.count(); +} + +bool Polyomino::contains(const Position& position) const { + return this->positions[getBitIndex(position)]; +} + +void Polyomino::insert(const Position& position) { + this->positions.set(getBitIndex(position), true); +} + +void Polyomino::erase(const Position& position) { + this->positions.set(getBitIndex(position), false); +} + +std::size_t Polyomino::getBitIndex(const Position& position) const { + return position.y * this->length + position.x; +} + +Polyomino::ConstIterator Polyomino::begin() const { + return ConstIterator(*this, positions._Find_first()); +} + +Polyomino::ConstIterator Polyomino::end() const { + return ConstIterator(*this, positions.size()); +} \ No newline at end of file diff --git a/src/Pieces/Polyomino.h b/src/Pieces/Polyomino.h index c7ed76a..d2f963a 100644 --- a/src/Pieces/Polyomino.h +++ b/src/Pieces/Polyomino.h @@ -5,26 +5,68 @@ #include #include #include +#include +#include /** * A mathematical object consisting of touching squares on a 2D grid */ class Polyomino { - private: - std::set 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 positions and normalizes it, wheter it is actually a polyonimo is not checked - */ - Polyomino(const std::set& positions); + static const std::size_t MAX_LENGTH = 16; + using PolyominoData = std::bitset; + class ConstIterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Position; + using const_pointer = const Position*; // or also value_type* + using const_reference = const Position&; // or also value_type& + public: + ConstIterator(const Polyomino& polyomino, std::size_t index) : m_Polyomino(polyomino), m_Index(index) { + updatePosition(); + } + + const_reference operator*() const { return m_Position; } + const_pointer operator->() { return &m_Position; } + + // Prefix increment + ConstIterator& operator++() { + m_Index = m_Polyomino.getPositionsData()._Find_next(m_Index); + updatePosition(); + return *this; + } + + + friend bool operator== (const ConstIterator& a, const ConstIterator& b) { return a.m_Index == b.m_Index; }; + friend bool operator!= (const ConstIterator& a, const ConstIterator& b) { return a.m_Index != b.m_Index; }; + + private: + void updatePosition() { + m_Position = Position(m_Index % m_Polyomino.getLength(), m_Index / m_Polyomino.getLength()); + } + + const Polyomino& m_Polyomino; + std::size_t m_Index; + Position m_Position; + }; + + private: + PolyominoData positions; // the squares composing the polyomino, stored in binary. MSB is downleft + std::int8_t length; // the size of the smallest square box in which the polyomino can fit on any rotation + + public: /** * 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& positions, int length); + Polyomino(PolyominoData&& positions, std::int8_t length); + + /** + * Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked + */ + Polyomino(std::set&& positions); /** * Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0) @@ -70,23 +112,33 @@ class Polyomino { */ bool hasHole() const; - private : + private: /** * Auxiliary method of hasHole() */ - void tryToInsertPosition(std::set& emptypositions, const Position& candidate) const; + void tryToInsertPosition(Polyomino& emptypositions, const Position& candidate) const; public: /** - * @return The positions of the polyomino + * @return The positions data of the polyomino */ - const std::set& getPositions() const; + const PolyominoData& getPositionsData() const; /** * @return The length of the polyomino */ int getLength() const; + /** + * + */ + int getSize() const; + + /** + * + */ + std::size_t getBitIndex(const Position& position) const; + /** * @return The number of squares in the polyomino */ @@ -110,4 +162,23 @@ class Polyomino { * @return A reference to the output stream */ friend std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino); + + /** + * @return True if it contains the position + */ + bool contains(const Position& position) const; + + /** + * @brief Insert a square at the position + */ + void insert(const Position& position); + + /** + * @brief Removes a square at the position + */ + void erase(const Position& position); + + ConstIterator begin() const; + + ConstIterator end() const; }; diff --git a/src/Pieces/Position.h b/src/Pieces/Position.h index 710d0ac..5048853 100644 --- a/src/Pieces/Position.h +++ b/src/Pieces/Position.h @@ -1,14 +1,14 @@ #pragma once #include - +#include /** * A position on a 2D grid */ struct Position { - int x; // x position - int y; // y position + std::int8_t x; // x position + std::int8_t y; // y position }; @@ -17,7 +17,7 @@ struct Position { * @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}; + return Position(left.x + right.x, left.y + right.y); } /** @@ -34,7 +34,7 @@ inline Position& operator+=(Position& left, const Position& right) { * @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}; + return Position(left.x - right.x, left.y - right.y); } /** diff --git a/src/TextUI/TextApp.cpp b/src/TextUI/TextApp.cpp index f7b8796..096f52e 100644 --- a/src/TextUI/TextApp.cpp +++ b/src/TextUI/TextApp.cpp @@ -250,9 +250,9 @@ void TextApp::printGame(const Game& game) const { for (int y = maxHeight; y >= 0; y--) { for (int x = 0; x < game.getBoard().getWidth(); x++) { /* BOARD PRINTING */ - bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition())); - bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getGhostPiecePosition())); - Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y}); + bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getActivePiecePosition())); + bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getGhostPiecePosition())); + Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position(x, y)); if (isActivePieceHere || isGhostPieceHere) { std::cout << getConsoleColorCode(block); @@ -294,7 +294,7 @@ void TextApp::printGame(const Game& game) const { } else { for (int i = 0; i < game.getHeldPiece()->getLength(); i++) { - if (game.getHeldPiece()->getPositions().contains(Position{i, printedPieceLineHeight})) { + if (game.getHeldPiece()->getPositions().contains(Position(i, printedPieceLineHeight))) { std::cout << getConsoleColorCode(game.getHeldPiece()->getBlockType()) << "*"; } else { @@ -316,7 +316,7 @@ void TextApp::printGame(const Game& game) const { } else { for (int i = 0; i < game.getNextPieces().at(nextQueuePrintedPiece).getLength(); i++) { - if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position{i, printedPieceLineHeight})) { + if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position(i, printedPieceLineHeight))) { std::cout << getConsoleColorCode(game.getNextPieces().at(nextQueuePrintedPiece).getBlockType()) << "*"; } else {