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/Pieces/Piece.cpp b/src/Pieces/Piece.cpp index bfff2f9..28fc372 100644 --- a/src/Pieces/Piece.cpp +++ b/src/Pieces/Piece.cpp @@ -12,8 +12,7 @@ Piece::Piece(Polyomino&& polyomino, Block blockType) : polyomino(polyomino), blockType(blockType), - rotationState(NONE), - positions(polyomino.getPositions()) { + rotationState(NONE) { } void Piece::rotate(Rotation rotation) { @@ -25,7 +24,6 @@ void Piece::rotate(Rotation rotation) { this->polyomino.rotateCCW(); this->rotationState += rotation; - this->positions = polyomino.getPositions(); } void Piece::defaultRotation() { @@ -37,11 +35,10 @@ void Piece::defaultRotation() { this->polyomino.rotateCW(); this->rotationState = NONE; - this->positions = polyomino.getPositions(); } -const std::vector& Piece::getPositions() const { - return this->positions; +const Polyomino& Piece::getPositions() const { + return this->polyomino; } int Piece::getLength() const { diff --git a/src/Pieces/Piece.h b/src/Pieces/Piece.h index 04a7074..012d94d 100644 --- a/src/Pieces/Piece.h +++ b/src/Pieces/Piece.h @@ -16,7 +16,6 @@ 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: /** @@ -37,7 +36,7 @@ class Piece { /** * @return The list of positions of the piece */ - const std::vector& getPositions() const; + const Polyomino& getPositions() const; /** * @param the position of the square diff --git a/src/Pieces/PiecesFiles.cpp b/src/Pieces/PiecesFiles.cpp index 6357eab..2881335 100644 --- a/src/Pieces/PiecesFiles.cpp +++ b/src/Pieces/PiecesFiles.cpp @@ -53,14 +53,15 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector& polyomin 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 for (int i = 0; i < bytesNeeded; i++) { - buffer << static_cast( - ( - polyomino.getPositionsData()[i / sizeof(std::uint64_t)] >> - (56 - ((i % sizeof(std::uint64_t)) * 8)) - ) - & 0xFF); + Polyomino::PolyominoData pData = polyominoData >> (i * 8); + unsigned long data = + (pData & byteMask).to_ulong(); + buffer << static_cast(data); } } @@ -121,8 +122,8 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector& pieces, std: for (int j = 0; j < bytesNeeded; j++) { buffer >> positionByte; - polyominoData[j / sizeof(std::uint64_t)] |= - (static_cast(positionByte) << (56 - ((j % sizeof(std::uint64_t)) * 8))); + Polyomino::PolyominoData tempByte(positionByte); + polyominoData |= (tempByte << (j * 8)); } pieces.emplace_back(Polyomino(std::move(polyominoData), length), pieceBlock); diff --git a/src/Pieces/Polyomino.cpp b/src/Pieces/Polyomino.cpp index c771d2f..444140c 100644 --- a/src/Pieces/Polyomino.cpp +++ b/src/Pieces/Polyomino.cpp @@ -8,8 +8,9 @@ #include #include #include +#include -Polyomino::Polyomino(std::set&& positions) { +Polyomino::Polyomino(std::set&& positions) : positions(0) { int minX = INT_MAX; int maxX = INT_MIN; int minY = INT_MAX; @@ -24,11 +25,11 @@ Polyomino::Polyomino(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 - Polyomino temp(PolyominoData{}, this->length); + Polyomino newPolyomino({}, this->length); for (Position position : positions) { - temp.insert(Position(position.x - minX, position.y - minY)); + newPolyomino.insert(Position(position.x - minX, position.y - minY)); } - this->positions = std::move(temp.positions); + this->positions = std::move(newPolyomino.positions); } Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) : @@ -39,24 +40,21 @@ Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) : void Polyomino::normalize() { int minX = INT_MAX; int minY = INT_MAX; - - std::vector tempPositions = getPositions(); - - for (const Position position : tempPositions) { + for (const Position position : *this) { if (position.x < minX) minX = position.x; if (position.y < minY) minY = position.y; } - Polyomino temp({}, this->length); - for (const Position position : tempPositions) { - temp.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(temp.positions); + this->positions = std::move(newPolyomino.positions); } void Polyomino::rotateCW() { Polyomino temp({}, this->length); - for (const Position position : getPositions()) { + for (const Position position : *this) { temp.insert(Position(position.y, (length - 1) - (position.x))); } this->positions = std::move(temp.positions); @@ -64,7 +62,7 @@ void Polyomino::rotateCW() { void Polyomino::rotate180() { Polyomino temp({}, this->length); - for (const Position position : getPositions()) { + for (const Position position : *this) { temp.insert(Position((length - 1) - (position.x), (length - 1) - (position.y))); } this->positions = std::move(temp.positions); @@ -72,7 +70,7 @@ void Polyomino::rotate180() { void Polyomino::rotateCCW() { Polyomino temp({}, this->length); - for (const Position position : getPositions()) { + for (const Position position : *this) { temp.insert(Position((length - 1) - (position.y), position.x)); } this->positions = std::move(temp.positions); @@ -91,7 +89,7 @@ void Polyomino::goToSpawnPosition() { } // calculates amount of squares per rows and columns - for (const Position position : getPositions()) { + 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 @@ -158,18 +156,17 @@ void Polyomino::goToSpawnPosition() { std::swap(verticalEmptyLines, horizontalEmptyLines); } - std::vector tempPositions = getPositions(); int minX = INT_MAX; int minY = INT_MAX; - for (const Position position : tempPositions) { + 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 Polyomino temp({}, this->length); - for (const Position position : tempPositions) { + for (const Position position : *this) { temp.insert(Position((position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2))); } this->positions = std::move(temp.positions); @@ -242,19 +239,19 @@ 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->contains(candidate) || emptyPositions.contains(candidate)) return; @@ -270,21 +267,6 @@ const Polyomino::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 - 1)) { - result.push_back(Position(x, y)); - } - } - } - return result; -} - int Polyomino::getLength() const { return this->length; } @@ -296,9 +278,16 @@ int Polyomino::getPolyominoSize() const { bool Polyomino::operator<(const Polyomino& other) const { if (this->length != other.length) return this->length < other.length; - for (std::size_t i = 0; i < this->positions.size(); i++) { - if (this->positions[i] != other.positions[i]) { - return this->positions[i] < other.positions[i]; + 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; } } @@ -324,23 +313,30 @@ 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 { - 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 - 1); + return this->positions[getBitIndex(position)]; } 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 - 1); + this->positions.set(getBitIndex(position), true); } void Polyomino::erase(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 - 1)); + 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 c1c2086..d2f963a 100644 --- a/src/Pieces/Polyomino.h +++ b/src/Pieces/Polyomino.h @@ -6,6 +6,7 @@ #include #include #include +#include /** @@ -13,8 +14,44 @@ */ class Polyomino { public: - static const std::size_t POLYOMINO_DATA_SIZE = 4; - using PolyominoData = std::array; + 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 @@ -79,7 +116,7 @@ class Polyomino { /** * Auxiliary method of hasHole() */ - void tryToInsertPosition(std::set& emptypositions, const Position& candidate) const; + void tryToInsertPosition(Polyomino& emptypositions, const Position& candidate) const; public: /** @@ -87,16 +124,21 @@ class Polyomino { */ 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 */ int getLength() const; + /** + * + */ + int getSize() const; + + /** + * + */ + std::size_t getBitIndex(const Position& position) const; + /** * @return The number of squares in the polyomino */ @@ -135,4 +177,8 @@ class Polyomino { * @brief Removes a square at the position */ void erase(const Position& position); + + ConstIterator begin() const; + + ConstIterator end() const; }; diff --git a/xmake.lua b/xmake.lua index 9b0c35e..d579661 100644 --- a/xmake.lua +++ b/xmake.lua @@ -2,8 +2,6 @@ add_rules("mode.debug", "mode.release") includes("xmake/bin2c.lua") -set_warnings("all") - add_requires("sfml 3.0.0", "zlib") set_languages("c++20")