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) {
int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().size();
int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().getSize();
this->currentBags.at(pieceSize).push_back(piece);
}
}

View File

@@ -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<Position>& Piece::getPositions() const {
return this->positions;
const Polyomino& Piece::getPositions() const {
return this->polyomino;
}
int Piece::getLength() const {

View File

@@ -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<Position> 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<Position>& getPositions() const;
const Polyomino& getPositions() const;
/**
* @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 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<std::uint8_t>(
(
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<std::uint8_t>(data);
}
}
@@ -121,8 +122,8 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
for (int j = 0; j < bytesNeeded; j++) {
buffer >> positionByte;
polyominoData[j / sizeof(std::uint64_t)] |=
(static_cast<std::uint64_t>(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);

View File

@@ -8,8 +8,9 @@
#include <climits>
#include <algorithm>
#include <utility>
#include <cassert>
Polyomino::Polyomino(std::set<Position>&& positions) {
Polyomino::Polyomino(std::set<Position>&& positions) : positions(0) {
int minX = INT_MAX;
int maxX = INT_MIN;
int minY = INT_MAX;
@@ -24,11 +25,11 @@ Polyomino::Polyomino(std::set<Position>&& 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<Position> 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<Position> 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<Position> 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<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 (this->contains(candidate) || emptyPositions.contains(candidate)) return;
@@ -270,21 +267,6 @@ const Polyomino::PolyominoData& Polyomino::getPositionsData() const {
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 {
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<std::uint64_t>(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<std::uint64_t>(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<std::uint64_t>(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());
}

View File

@@ -6,6 +6,7 @@
#include <set>
#include <iostream>
#include <array>
#include <bitset>
/**
@@ -13,8 +14,44 @@
*/
class Polyomino {
public:
static const std::size_t POLYOMINO_DATA_SIZE = 4;
using PolyominoData = std::array<std::uint64_t, POLYOMINO_DATA_SIZE>;
static const std::size_t MAX_LENGTH = 16;
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:
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<Position>& 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<Position> 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;
};