#include "Generator.h" #include "Polyomino.h" #include #include #include #include Generator::Generator() { } std::vector 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 candidatePositions; this->generate(polyominoSize, 0, 1, candidatePositions); return this->validPolyominoes; } void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map candidatePositions) { // recursion stop if (polyominoSize == this->currentTestedShape.size()) { Polyomino candidate(this->currentTestedShape); // we sort the rotations of the polyominoes std::vector 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 (const 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 (const auto [key, val] : candidatePositions) { if (val > lastAddedPositionNumber) { this->currentTestedShape.insert(key); this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map() : candidatePositions); this->currentTestedShape.erase(key); } } } void Generator::tryToAddCandidatePosition(const Position& candidate, int& nextAvaibleNumber, std::map& 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++; }