diff --git a/src/Core/GameBoard.cpp b/src/Core/GameBoard.cpp index c57784c..1b79b20 100644 --- a/src/Core/GameBoard.cpp +++ b/src/Core/GameBoard.cpp @@ -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) { diff --git a/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp b/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp index 0245e0d..26c383e 100644 --- a/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp +++ b/src/GraphicalUI/AppMenus/GamePlayingAppMenu.cpp @@ -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 6068e19..4b6e45c 100644 --- a/src/Pieces/Generator.cpp +++ b/src/Pieces/Generator.cpp @@ -29,7 +29,7 @@ std::vector&& Generator::generatePolyominoes(int polyominoSize) { void Generator::generate(int 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; diff --git a/src/Pieces/Piece.cpp b/src/Pieces/Piece.cpp index 7023cf7..431c818 100644 --- a/src/Pieces/Piece.cpp +++ b/src/Pieces/Piece.cpp @@ -11,9 +11,9 @@ Piece::Piece(const Polyomino& polyomino, Block blockType) : polyomino(polyomino), - blockType(blockType) { - - this->rotationState = NONE; + blockType(blockType), + rotationState(NONE), + positions(polyomino.getPositions()) { } void Piece::rotate(Rotation rotation) { @@ -25,6 +25,7 @@ void Piece::rotate(Rotation rotation) { this->polyomino.rotateCCW(); this->rotationState += rotation; + this->positions = polyomino.getPositions(); } void Piece::defaultRotation() { @@ -36,10 +37,11 @@ void Piece::defaultRotation() { this->polyomino.rotateCW(); this->rotationState = NONE; + this->positions = polyomino.getPositions(); } -const std::set& Piece::getPositions() const { - return this->polyomino.getPositions(); +const std::vector& Piece::getPositions() const { + return this->positions; } int Piece::getLength() const { @@ -54,3 +56,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..99146f5 100644 --- a/src/Pieces/Piece.h +++ b/src/Pieces/Piece.h @@ -16,6 +16,7 @@ class Piece { Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft Block blockType; // the block type of the piece Rotation rotationState; // the current rotation of the piece + std::vector positions; // cache positions for easier use (particularly UI) public: /** @@ -36,7 +37,13 @@ class Piece { /** * @return The list of positions of the piece */ - const std::set& getPositions() const; + const std::vector& 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 6cc2fb6..77c701a 100644 --- a/src/Pieces/PiecesFiles.cpp +++ b/src/Pieces/PiecesFiles.cpp @@ -119,7 +119,7 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector& pieces, std: } // create piece - Piece readPiece(Polyomino(piecePositions, length), pieceBlock); + Piece readPiece(Polyomino(std::move(piecePositions), length), pieceBlock); nextPieceBlockType(pieceBlock); pieces.push_back(readPiece); diff --git a/src/Pieces/Polyomino.cpp b/src/Pieces/Polyomino.cpp index 0acbb83..3e4fd1c 100644 --- a/src/Pieces/Polyomino.cpp +++ b/src/Pieces/Polyomino.cpp @@ -9,8 +9,7 @@ #include #include - -Polyomino::Polyomino(const std::set& positions) { +Polyomino::Polyomino(std::set&& positions) { int minX = INT_MAX; int maxX = INT_MIN; int minY = INT_MAX; @@ -25,55 +24,90 @@ 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 temp(PolyominoData{}, this->length); for (Position position : positions) { - newPositions.insert(Position(position.x - minX, position.y - minY)); + temp.insert(Position(position.x - minX, position.y - minY)); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } -Polyomino::Polyomino(const std::set& positions, std::int8_t length) : - positions(positions), +Polyomino::Polyomino(std::set&& positions, std::int8_t length) : positions(), length(length){ + for (Position position : positions) { + insert(position); + } +} + +Polyomino::Polyomino(PolyominoData&& positions) : positions(positions) { + int minX = INT_MAX; + int maxX = INT_MIN; + int minY = INT_MAX; + int maxY = INT_MIN; + + // tout s'appelle positions, osekour ! + std::vector tempPositions = getPositions(); + + for (const Position position : tempPositions) { + 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; + } + + 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 + Polyomino temp(PolyominoData{}, this->length); + for (Position position : tempPositions) { + temp.insert(Position(position.x - minX, position.y - minY)); + } + this->positions = std::move(temp.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) { + + std::vector tempPositions = getPositions(); + + for (const Position position : tempPositions) { 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 temp(PolyominoData{}, this->length); + for (const Position position : tempPositions) { + temp.insert(Position(position.x - minX, position.y - minY)); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::rotateCW() { - std::set newPositions; - for (const Position position : this->positions) { - newPositions.insert(Position(position.y, (length - 1) - (position.x))); + Polyomino temp(PolyominoData{}, this->length); + for (const Position position : getPositions()) { + 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(PolyominoData{}, this->length); + for (const Position position : getPositions()) { + 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(PolyominoData{}, this->length); + for (const Position position : getPositions()) { + temp.insert(Position((length - 1) - (position.y), position.x)); } - this->positions = std::move(newPositions); + this->positions = std::move(temp.positions); } void Polyomino::goToSpawnPosition() { @@ -89,7 +123,7 @@ void Polyomino::goToSpawnPosition() { } // calculates amount of squares per rows and columns - for (const Position position : this->positions) { + for (const Position position : getPositions()) { 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 +189,22 @@ void Polyomino::goToSpawnPosition() { if (sideToBeOn % 2 == 1) { std::swap(verticalEmptyLines, horizontalEmptyLines); } + + std::vector tempPositions = getPositions(); int minX = INT_MAX; int minY = INT_MAX; - for (const Position position : this->positions) { + for (const Position position : tempPositions) { 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(PolyominoData{}, this->length); + for (const Position position : tempPositions) { + 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 +252,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 +260,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; } @@ -252,7 +288,7 @@ bool Polyomino::hasHole() const { void Polyomino::tryToInsertPosition(std::set& 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); @@ -262,10 +298,25 @@ void Polyomino::tryToInsertPosition(std::set& emptyPositions, const Po tryToInsertPosition(emptyPositions, Position(candidate.x - 1, candidate.y)); } -const std::set& Polyomino::getPositions() const { +const PolyominoData& Polyomino::getPositionsData() const { return this->positions; } +std::vector Polyomino::getPositions() const { + std::vector result; + for (int y = 0; y < this->length; y++) { + for (int x = 0; x < this->length; x++) { + int posIndex = y * this->length + x; + int longIndex = posIndex / (sizeof(std::uint64_t) * 8); + int bitIndex = posIndex % (sizeof(std::uint64_t) * 8); + if (this->positions[longIndex] & static_cast(1) << (sizeof(std::uint64_t) * 8 - bitIndex)) { + result.push_back(Position(x, y)); + } + } + } + return result; +} + int Polyomino::getLength() const { return this->length; } @@ -277,13 +328,12 @@ 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; + for (int i = 0; i < this->positions.size(); i++) { + if (this->positions[i] != other.positions[i]) { + return this->positions[i] < other.positions[i]; } } + return false; } @@ -294,7 +344,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 +355,17 @@ std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) { } return os; } + +bool Polyomino::contains(const Position& position) const { + int posIndex = position.y * this->length + position.x; + int longIndex = posIndex / (sizeof(std::uint64_t) * 8); + int bitIndex = posIndex % (sizeof(std::uint64_t) * 8); + return this->positions[longIndex] & static_cast(1) << (sizeof(std::uint64_t) * 8 - bitIndex); +} + +void Polyomino::insert(const Position& position) { + int posIndex = position.y * this->length + position.x; + int longIndex = posIndex / (sizeof(std::uint64_t) * 8); + int bitIndex = posIndex % (sizeof(std::uint64_t) * 8); + this->positions[longIndex] |= static_cast(1) << (sizeof(std::uint64_t) * 8 - bitIndex); +} \ No newline at end of file diff --git a/src/Pieces/Polyomino.h b/src/Pieces/Polyomino.h index e759f80..32eabac 100644 --- a/src/Pieces/Polyomino.h +++ b/src/Pieces/Polyomino.h @@ -5,26 +5,32 @@ #include #include #include +#include +using PolyominoData = std::array; /** * 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 - std::int8_t length; // the size of the smallest square box in which the polyomino can fit on any rotation + 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 normalizes it, wheter it is actually a polyonimo is not checked */ - Polyomino(const std::set& positions); + Polyomino(PolyominoData&& positions); /** * 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, std::int8_t length); + Polyomino(PolyominoData&& positions, std::int8_t length); + + // this is temporary. They are here for compatibility reasons for now + Polyomino(std::set&& positions); + Polyomino(std::set&& positions, std::int8_t length); /** * Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0) @@ -70,7 +76,7 @@ class Polyomino { */ bool hasHole() const; - private : + private: /** * Auxiliary method of hasHole() */ @@ -78,9 +84,14 @@ class Polyomino { 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 positions of the polyomino (deduces it from the binary representation) + */ + std::vector getPositions() const; /** * @return The length of the polyomino @@ -110,4 +121,15 @@ 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; + + private: + /** + * @brief Insert a square at the position + */ + void insert(const Position& position); };