use bitsets
All checks were successful
Linux arm64 / Build (push) Successful in 2m13s

This commit is contained in:
2025-07-21 16:06:27 +02:00
parent 4095103843
commit 07ba9619ed
7 changed files with 120 additions and 83 deletions

View File

@@ -35,7 +35,7 @@ Bag::Bag(const std::shared_ptr<PiecesList>& piecesList) :
} }
for (const auto& piece : this->selectedPieces) { 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); this->currentBags.at(pieceSize).push_back(piece);
} }
} }

View File

@@ -12,8 +12,7 @@
Piece::Piece(Polyomino&& polyomino, Block blockType) : Piece::Piece(Polyomino&& polyomino, Block blockType) :
polyomino(polyomino), polyomino(polyomino),
blockType(blockType), blockType(blockType),
rotationState(NONE), rotationState(NONE) {
positions(polyomino.getPositions()) {
} }
void Piece::rotate(Rotation rotation) { void Piece::rotate(Rotation rotation) {
@@ -25,7 +24,6 @@ void Piece::rotate(Rotation rotation) {
this->polyomino.rotateCCW(); this->polyomino.rotateCCW();
this->rotationState += rotation; this->rotationState += rotation;
this->positions = polyomino.getPositions();
} }
void Piece::defaultRotation() { void Piece::defaultRotation() {
@@ -37,11 +35,10 @@ void Piece::defaultRotation() {
this->polyomino.rotateCW(); this->polyomino.rotateCW();
this->rotationState = NONE; this->rotationState = NONE;
this->positions = polyomino.getPositions();
} }
const std::vector<Position>& Piece::getPositions() const { const Polyomino& Piece::getPositions() const {
return this->positions; return this->polyomino;
} }
int Piece::getLength() const { int Piece::getLength() const {

View File

@@ -16,7 +16,6 @@ class Piece {
Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft
Block blockType; // the block type of the piece Block blockType; // the block type of the piece
Rotation rotationState; // the current rotation of the piece Rotation rotationState; // the current rotation of the piece
std::vector<Position> positions; // cache positions for easier use (particularly UI)
public: public:
/** /**
@@ -37,7 +36,7 @@ class Piece {
/** /**
* @return The list of positions of the piece * @return The list of positions of the piece
*/ */
const std::vector<Position>& getPositions() const; const Polyomino& getPositions() const;
/** /**
* @param the position of the square * @param the position of the square

View File

@@ -53,14 +53,15 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyomin
const int bitsNeeded = polyomino.getLength() * polyomino.getLength(); const int bitsNeeded = polyomino.getLength() * polyomino.getLength();
const int bytesNeeded = bitsNeeded / 8 + 1; const int bytesNeeded = bitsNeeded / 8 + 1;
static const Polyomino::PolyominoData byteMask = 0xFF;
const Polyomino::PolyominoData& polyominoData = polyomino.getPositionsData();
// write the positions of the piece // write the positions of the piece
for (int i = 0; i < bytesNeeded; i++) { for (int i = 0; i < bytesNeeded; i++) {
buffer << static_cast<std::uint8_t>( Polyomino::PolyominoData pData = polyominoData >> (i * 8);
( unsigned long data =
polyomino.getPositionsData()[i / sizeof(std::uint64_t)] >> (pData & byteMask).to_ulong();
(56 - ((i % sizeof(std::uint64_t)) * 8)) buffer << static_cast<std::uint8_t>(data);
)
& 0xFF);
} }
} }
@@ -121,8 +122,8 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
for (int j = 0; j < bytesNeeded; j++) { for (int j = 0; j < bytesNeeded; j++) {
buffer >> positionByte; buffer >> positionByte;
polyominoData[j / sizeof(std::uint64_t)] |= Polyomino::PolyominoData tempByte(positionByte);
(static_cast<std::uint64_t>(positionByte) << (56 - ((j % sizeof(std::uint64_t)) * 8))); polyominoData |= (tempByte << (j * 8));
} }
pieces.emplace_back(Polyomino(std::move(polyominoData), length), pieceBlock); pieces.emplace_back(Polyomino(std::move(polyominoData), length), pieceBlock);

View File

@@ -8,8 +8,9 @@
#include <climits> #include <climits>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <cassert>
Polyomino::Polyomino(std::set<Position>&& positions) { Polyomino::Polyomino(std::set<Position>&& positions) : positions(0) {
int minX = INT_MAX; int minX = INT_MAX;
int maxX = INT_MIN; int maxX = INT_MIN;
int minY = INT_MAX; int minY = INT_MAX;
@@ -24,11 +25,11 @@ Polyomino::Polyomino(std::set<Position>&& positions) {
this->length = std::max(maxX - minX + 1, maxY - minY + 1); 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 // 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) { 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) : Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) :
@@ -39,24 +40,21 @@ Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) :
void Polyomino::normalize() { void Polyomino::normalize() {
int minX = INT_MAX; int minX = INT_MAX;
int minY = INT_MAX; int minY = INT_MAX;
for (const Position position : *this) {
std::vector<Position> tempPositions = getPositions();
for (const Position position : tempPositions) {
if (position.x < minX) minX = position.x; if (position.x < minX) minX = position.x;
if (position.y < minY) minY = position.y; if (position.y < minY) minY = position.y;
} }
Polyomino temp({}, this->length); Polyomino newPolyomino({}, this->length);
for (const Position position : tempPositions) { for (const Position position : *this) {
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);
} }
void Polyomino::rotateCW() { void Polyomino::rotateCW() {
Polyomino temp({}, this->length); Polyomino temp({}, this->length);
for (const Position position : getPositions()) { for (const Position position : *this) {
temp.insert(Position(position.y, (length - 1) - (position.x))); temp.insert(Position(position.y, (length - 1) - (position.x)));
} }
this->positions = std::move(temp.positions); this->positions = std::move(temp.positions);
@@ -64,7 +62,7 @@ void Polyomino::rotateCW() {
void Polyomino::rotate180() { void Polyomino::rotate180() {
Polyomino temp({}, this->length); 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))); temp.insert(Position((length - 1) - (position.x), (length - 1) - (position.y)));
} }
this->positions = std::move(temp.positions); this->positions = std::move(temp.positions);
@@ -72,7 +70,7 @@ void Polyomino::rotate180() {
void Polyomino::rotateCCW() { void Polyomino::rotateCCW() {
Polyomino temp({}, this->length); Polyomino temp({}, this->length);
for (const Position position : getPositions()) { for (const Position position : *this) {
temp.insert(Position((length - 1) - (position.y), position.x)); temp.insert(Position((length - 1) - (position.y), position.x));
} }
this->positions = std::move(temp.positions); this->positions = std::move(temp.positions);
@@ -91,7 +89,7 @@ void Polyomino::goToSpawnPosition() {
} }
// calculates amount of squares per rows and columns // 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(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(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(2).at((length - 1) - position.y) += 1; // 2 = top to bottom = 180
@@ -158,18 +156,17 @@ void Polyomino::goToSpawnPosition() {
std::swap(verticalEmptyLines, horizontalEmptyLines); std::swap(verticalEmptyLines, horizontalEmptyLines);
} }
std::vector<Position> tempPositions = getPositions();
int minX = INT_MAX; int minX = INT_MAX;
int minY = 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.x < minX) minX = position.x;
if (position.y < minY) minY = position.y; if (position.y < minY) minY = position.y;
} }
// center the piece with an up bias // center the piece with an up bias
Polyomino temp({}, this->length); 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))); temp.insert(Position((position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)));
} }
this->positions = std::move(temp.positions); this->positions = std::move(temp.positions);
@@ -242,19 +239,19 @@ bool Polyomino::isConvex() const {
bool Polyomino::hasHole() const { bool Polyomino::hasHole() const {
// add every empty square on the outer of the box containing the polyomino // add every empty square on the outer of the box containing the polyomino
std::set<Position> emptyPositions; Polyomino temp({}, this->length);
for (int i = 0; i < this->length - 1; i++) { for (int i = 0; i < this->length - 1; i++) {
this->tryToInsertPosition(emptyPositions, Position(i, 0)); // up row this->tryToInsertPosition(temp, Position(i, 0)); // up row
this->tryToInsertPosition(emptyPositions, Position(this->length - 1, i)); // rigth column this->tryToInsertPosition(temp, Position(this->length - 1, i)); // rigth column
this->tryToInsertPosition(emptyPositions, Position(this->length - 1 - i, this->length - 1)); // bottom row this->tryToInsertPosition(temp, 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(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 // 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<Position>& 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 (candidate.x >= this->length || candidate.x < 0 || candidate.y >= this->length || candidate.y < 0) return;
if (this->contains(candidate) || emptyPositions.contains(candidate)) return; if (this->contains(candidate) || emptyPositions.contains(candidate)) return;
@@ -270,21 +267,6 @@ const Polyomino::PolyominoData& Polyomino::getPositionsData() const {
return this->positions; return this->positions;
} }
std::vector<Position> Polyomino::getPositions() const {
std::vector<Position> 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<std::uint64_t>(1) << (sizeof(std::uint64_t) * 8 - bitIndex - 1)) {
result.push_back(Position(x, y));
}
}
}
return result;
}
int Polyomino::getLength() const { int Polyomino::getLength() const {
return this->length; return this->length;
} }
@@ -296,9 +278,16 @@ int Polyomino::getPolyominoSize() const {
bool Polyomino::operator<(const Polyomino& other) const { bool Polyomino::operator<(const Polyomino& other) const {
if (this->length != other.length) return this->length < other.length; if (this->length != other.length) return this->length < other.length;
for (std::size_t i = 0; i < this->positions.size(); i++) { assert(other.positions.any() && "The other polyomino is empty !");
if (this->positions[i] != other.positions[i]) {
return this->positions[i] < other.positions[i]; 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; return os;
} }
int Polyomino::getSize() const {
return positions.count();
}
bool Polyomino::contains(const Position& position) const { bool Polyomino::contains(const Position& position) const {
int posIndex = position.y * this->length + position.x; return this->positions[getBitIndex(position)];
int longIndex = posIndex / (sizeof(std::uint64_t) * 8);
int bitIndex = posIndex % (sizeof(std::uint64_t) * 8);
return this->positions[longIndex] & static_cast<std::uint64_t>(1) << (sizeof(std::uint64_t) * 8 - bitIndex - 1);
} }
void Polyomino::insert(const Position& position) { void Polyomino::insert(const Position& position) {
int posIndex = position.y * this->length + position.x; this->positions.set(getBitIndex(position), true);
int longIndex = posIndex / (sizeof(std::uint64_t) * 8);
int bitIndex = posIndex % (sizeof(std::uint64_t) * 8);
this->positions[longIndex] |= static_cast<std::uint64_t>(1) << (sizeof(std::uint64_t) * 8 - bitIndex - 1);
} }
void Polyomino::erase(const Position& position) { void Polyomino::erase(const Position& position) {
int posIndex = position.y * this->length + position.x; this->positions.set(getBitIndex(position), false);
int longIndex = posIndex / (sizeof(std::uint64_t) * 8); }
int bitIndex = posIndex % (sizeof(std::uint64_t) * 8);
this->positions[longIndex] &= ~(static_cast<std::uint64_t>(1) << (sizeof(std::uint64_t) * 8 - bitIndex - 1)); 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());
} }

View File

@@ -6,6 +6,7 @@
#include <set> #include <set>
#include <iostream> #include <iostream>
#include <array> #include <array>
#include <bitset>
/** /**
@@ -13,8 +14,44 @@
*/ */
class Polyomino { class Polyomino {
public: public:
static const std::size_t POLYOMINO_DATA_SIZE = 4; static const std::size_t MAX_LENGTH = 16;
using PolyominoData = std::array<std::uint64_t, POLYOMINO_DATA_SIZE>; using PolyominoData = std::bitset<MAX_LENGTH * MAX_LENGTH>;
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: private:
PolyominoData positions; // the squares composing the polyomino, stored in binary. MSB is downleft PolyominoData positions; // the squares composing the polyomino, stored in binary. MSB is downleft
@@ -79,7 +116,7 @@ class Polyomino {
/** /**
* Auxiliary method of hasHole() * Auxiliary method of hasHole()
*/ */
void tryToInsertPosition(std::set<Position>& emptypositions, const Position& candidate) const; void tryToInsertPosition(Polyomino& emptypositions, const Position& candidate) const;
public: public:
/** /**
@@ -87,16 +124,21 @@ class Polyomino {
*/ */
const PolyominoData& getPositionsData() const; const PolyominoData& getPositionsData() const;
/**
* @return The positions of the polyomino (deduces it from the binary representation)
*/
std::vector<Position> getPositions() const;
/** /**
* @return The length of the polyomino * @return The length of the polyomino
*/ */
int getLength() const; int getLength() const;
/**
*
*/
int getSize() const;
/**
*
*/
std::size_t getBitIndex(const Position& position) const;
/** /**
* @return The number of squares in the polyomino * @return The number of squares in the polyomino
*/ */
@@ -135,4 +177,8 @@ class Polyomino {
* @brief Removes a square at the position * @brief Removes a square at the position
*/ */
void erase(const Position& position); void erase(const Position& position);
ConstIterator begin() const;
ConstIterator end() const;
}; };

View File

@@ -2,8 +2,6 @@ add_rules("mode.debug", "mode.release")
includes("xmake/bin2c.lua") includes("xmake/bin2c.lua")
set_warnings("all")
add_requires("sfml 3.0.0", "zlib") add_requires("sfml 3.0.0", "zlib")
set_languages("c++20") set_languages("c++20")