84 lines
3.3 KiB
C++
84 lines
3.3 KiB
C++
#include "Generator.h"
|
|
|
|
#include "Polyomino.h"
|
|
|
|
#include <vector>
|
|
#include <set>
|
|
#include <map>
|
|
#include <algorithm>
|
|
|
|
|
|
Generator::Generator() {
|
|
}
|
|
|
|
std::vector<Polyomino> Generator::generatePolyominoes(int polyominoSize) {
|
|
this->validPolyominoes.clear();
|
|
this->currentTestedShape.clear();
|
|
|
|
// a polyomino has at least 1 square
|
|
if (polyominoSize < 1) return this->validPolyominoes;
|
|
|
|
// always place the first cell at (0, 0)
|
|
this->currentTestedShape.insert(Position{0, 0});
|
|
|
|
std::map<Position, int> candidatePositions;
|
|
this->generate(polyominoSize, 0, 1, candidatePositions);
|
|
return this->validPolyominoes;
|
|
}
|
|
|
|
void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) {
|
|
// recursion stop
|
|
if (polyominoSize == this->currentTestedShape.size()) {
|
|
Polyomino candidate(this->currentTestedShape);
|
|
|
|
// we sort the rotations of the polyominoes
|
|
std::vector<Polyomino> candidateRotations;
|
|
candidateRotations.reserve(4);
|
|
for (int i = 0; i < 4; i++) {
|
|
candidate.normalize();
|
|
candidateRotations.push_back(candidate);
|
|
if (!(i==3)) candidate.rotateCW();
|
|
}
|
|
std::sort(candidateRotations.begin(), candidateRotations.end());
|
|
|
|
// we keep the polyomino only if it was generated in its lowest rotation
|
|
if (candidate == candidateRotations.at(0)) {
|
|
this->validPolyominoes.push_back(candidate);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// generate the list of candidate positions
|
|
for (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);
|
|
}
|
|
|
|
// try adding a square only to positions with a higher number than the last one
|
|
for (auto [key, val] : candidatePositions) {
|
|
if (val > lastAddedPositionNumber) {
|
|
this->currentTestedShape.insert(key);
|
|
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
|
|
this->currentTestedShape.erase(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Generator::tryToAddCandidatePosition(const Position& candidate, int& nextAvaibleNumber, std::map<Position, int>& candidatepositions) {
|
|
// we declared the first position as the lower-left square, since we always start with a monomino at (0,0) we can test with 0 directly
|
|
if (candidate.y < 0 || (candidate.y == 0 && candidate.x < 0)) return;
|
|
|
|
// if the position was already marked then we should not mark it again
|
|
if (candidatepositions.contains(candidate)) return;
|
|
|
|
// if the candidate overlaps with the shape there is no reason to add it
|
|
if (this->currentTestedShape.contains(candidate)) return;
|
|
|
|
// once all tests passed we can add the position
|
|
candidatepositions.insert({candidate, nextAvaibleNumber});
|
|
nextAvaibleNumber++;
|
|
}
|