mis en place la PR de simon sur les couleurs

This commit is contained in:
2025-02-28 18:42:04 +01:00
parent f0f391ade6
commit 26f501f7e8
29 changed files with 713 additions and 665 deletions

37
src/Pieces/Block.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <string>
/**
* Every possible block type
*/
enum Block {
NOTHING,
OUT_OF_BOUNDS,
GARBAGE,
PURPLE,
ORANGE,
CYAN,
PINK,
YELLOW,
RED,
BLUE,
GREEN
};
/**
* Gets the first block type a piece can be
* @return The block type
*/
inline Block firstPieceBlockType() {
return Block(PURPLE);
}
/**
* Sets the block to the next available piece block type
*/
inline void nextPieceBlockType(Block& block) {
block = (block == GREEN) ? PURPLE : Block(block + 1);
}

View File

@@ -1,66 +0,0 @@
#pragma once
#include <iostream>
/**
* A cell on a 2D grid
*/
struct Cell {
int x; // x position
int y; // y position
};
/**
* Addition operator, returns the sums of the coordinates of both cells
*/
inline Cell operator+(const Cell& left, const Cell& right) {
return Cell{left.x + right.x, left.y + right.y};
}
/**
* Additive assignation operator, adds the coordinates of the right cell to the left one
*/
inline Cell& operator+=(Cell& left, const Cell& right) {
left = left + right;
return left;
}
/**
* Substraction operator, returns the difference of the coordinate between the left and right cell
*/
inline Cell operator-(const Cell& left, const Cell& right) {
return Cell{left.x - right.x, left.y - right.y};
}
/**
* Substractive assignation operator, substract the coordinates of the right cell from the left one
*/
inline Cell& operator-=(Cell& left, const Cell& right) {
left = left - right;
return left;
}
/**
* Strict inferiority operator, a cell is inferior to another if it is lower or at the same height and more to the left
*/
inline bool operator<(const Cell& left, const Cell& right) {
return (left.x == right.x) ? (left.y < right.y) : (left.x < right.x);
}
/**
* Equality operator, two cells are equal if they have the same coordinates
*/
inline bool operator==(const Cell& left, const Cell& right) {
return (left.x == right.x) && (left.y == right.y);
}
/**
* Stream output operator, adds the coordinates of the cell to the stream
*/
inline std::ostream& operator<<(std::ostream& os, const Cell& cell) {
os << "x: " << cell.x << " y: " << cell.y;
return os;
}

View File

@@ -1,51 +1,55 @@
#pragma once
#include <String>
#include "Block.h"
#include "string"
/**
* Every possible colors a block can take
* A color encoded in RGB
*/
enum Color {
NOTHING,
OUT_OF_BOUND,
GARBAGE,
PURPLE,
ORANGE,
CYAN,
PINK,
YELLOW,
RED,
BLUE,
GREEN
struct Color {
unsigned char red; // the red component of the color
unsigned char green; // the green component of the color
unsigned char blue; // the blue component of the color
};
/**
* Returns the first color a piece can take
*/
inline Color firstPieceColor() {
return Color(PURPLE);
}
/**
* Sets the color to the next available piece color
*/
inline void nextPieceColor(Color& color) {
color = (color == GREEN) ? PURPLE : Color(color + 1);
}
static const std::string COLOR_RESET = "\033[38;2;255;255;255m"; // color code to reset the console color
static const std::string COLOR_CODES[] = { // color codes to change the console color
COLOR_RESET, // NOTHING
COLOR_RESET, // OUT_OF_BOUND
"\033[38;2;150;150;150m", // GARBAGE
"\033[38;2;150;0;255m", // PURPLE
"\033[38;2;255;150;0m", // ORANGE
"\033[38;2;0;255;255m", // CYAN
"\033[38;2;255;0;200m", // PINK
"\033[38;2;255;255;0m", // YELLOW
"\033[38;2;255;0;0m", // RED
"\033[38;2;0;100;255m", // BLUE
"\033[38;2;0;255;0m" // GREEN
static const Color EMPTY_BLOCK_COLOR = {255, 255, 255}; // color of an empty block
static const Color BLOCKS_COLOR[] = { // color for each block type
EMPTY_BLOCK_COLOR, // NOTHING
EMPTY_BLOCK_COLOR, // OUT_OF_BOUNDS
{150, 150, 150}, // GARBAGE
{150, 0, 255}, // PURPLE
{255, 150, 0}, // ORANGE
{0, 255, 255}, // CYAN
{255, 0, 200}, // PINK
{255, 255, 0}, // YELLOW
{255, 0, 0}, // RED
{0, 100, 255}, // BLUE
{0, 255, 0} // GREEN
};
/**
* Translates the color into a color code to change the console's color
* @return A string to print in the console
*/
inline std::string getConsoleColorCode(const Color& color) {
return "\033[38;2;" + std::to_string(color.red) + ";" + std::to_string(color.green) + ";" + std::to_string(color.blue) + "m";
}
/**
* Translates the color into a color code to change the console's color
* @return A string to print in the console
*/
inline std::string getConsoleColorCode(const Block block) {
return getConsoleColorCode(BLOCKS_COLOR[block]);
}
/**
* Gets a color code to reset the console's color
* @return A string to print in the console
*/
inline std::string getResetConsoleColorCode() {
return getConsoleColorCode(EMPTY_BLOCK_COLOR);
}

View File

@@ -2,36 +2,33 @@
#include "Polyomino.h"
#include <Vector>
#include <Set>
#include <Map>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
Generator::Generator() {
}
std::vector<Polyomino> Generator::generatePolyominos(unsigned int order) {
// initialization
std::vector<Polyomino> Generator::generatePolyominos(unsigned int polyominoSize) {
this->validPolyominos.clear();
this->currentTestedShape.clear();
// no polyomino with 0 cells
if (order == 0) return this->validPolyominos;
// no polyomino of size 0
if (polyominoSize == 0) return this->validPolyominos;
// start generating from the monomino
this->currentTestedShape.insert(Cell{0, 0});
this->currentTestedShape.insert(Position{0, 0});
// generate polyominos
std::map<Cell, int> candidateCells;
this->generate(order, 0, 1, candidateCells);
std::map<Position, int> candidatePositions;
this->generate(polyominoSize, 0, 1, candidatePositions);
return this->validPolyominos;
}
void Generator::generate(unsigned int order, int lastAddedCellNumber, int nextAvaibleNumber, std::map<Cell, int> candidateCells) {
void Generator::generate(unsigned int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) {
// recursion stop
if (order == this->currentTestedShape.size()) {
// we test the polyomino formed by the current shape
if (polyominoSize == this->currentTestedShape.size()) {
Polyomino candidate(this->currentTestedShape);
// we sort the rotations of the polyominos
@@ -51,35 +48,35 @@ void Generator::generate(unsigned int order, int lastAddedCellNumber, int nextAv
return;
}
// generate the list of candidate cells
for (Cell cell : this->currentTestedShape) {
this->tryToAddCandidateCell(Cell{cell.x, cell.y + 1}, nextAvaibleNumber, candidateCells);
this->tryToAddCandidateCell(Cell{cell.x + 1, cell.y}, nextAvaibleNumber, candidateCells);
this->tryToAddCandidateCell(Cell{cell.x, cell.y - 1}, nextAvaibleNumber, candidateCells);
this->tryToAddCandidateCell(Cell{cell.x - 1, cell.y}, nextAvaibleNumber, candidateCells);
// 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);
}
// generate polyominos for all cells with a higher number than the last one
for (auto [key, val] : candidateCells) {
if (val > lastAddedCellNumber) {
// generate polyominos for all positions with a higher number than the last one
for (auto [key, val] : candidatePositions) {
if (val > lastAddedPositionNumber) {
this->currentTestedShape.insert(key);
this->generate(order, val, nextAvaibleNumber, (order == this->currentTestedShape.size()) ? std::map<Cell, int>() : candidateCells);
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
this->currentTestedShape.erase(key);
}
}
}
void Generator::tryToAddCandidateCell(const Cell& candidate, int& nextAvaibleNumber, std::map<Cell, int>& candidateCells) {
// we declared the first cell as the lower-left square, since we always start with a monomino at (0,0) we can test with hard values
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 cell was already marked then we should not mark it again
if (candidateCells.contains(candidate)) 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 cell
candidateCells.insert({candidate, nextAvaibleNumber});
// once all tests passed we can add the position
candidatepositions.insert({candidate, nextAvaibleNumber});
nextAvaibleNumber++;
}

View File

@@ -2,9 +2,9 @@
#include "Polyomino.h"
#include <Vector>
#include <Set>
#include <Map>
#include <vector>
#include <set>
#include <map>
/**
@@ -13,27 +13,28 @@
class Generator {
private:
std::vector<Polyomino> validPolyominos; // the list of already generated polyominos
std::set<Cell> currentTestedShape; // the polyomino being created
std::set<Position> currentTestedShape; // the polyomino being created
public:
/**
* Initializes generator
* Default constructor
*/
Generator();
/**
* Returns the list of all one-sided polyominos of the specified size
* Generates the list of all one-sided polyominos of the specified size
* @return The list of polyominos
*/
std::vector<Polyomino> generatePolyominos(unsigned int order);
std::vector<Polyomino> generatePolyominos(unsigned int polyominoSize);
private:
/**
* Generates all one-sided polyominos of the specified using the current tested shape
* Generates all one-sided polyominos of the specified size using the current tested shape
*/
void generate(unsigned int order, int lastAddedCellNumber, int nextAvaibleNumber, std::map<Cell, int> candidateCells);
void generate(unsigned int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions);
/**
* Check wheter a candidate cell can be added to the current tested shape
* Checks wheter a candidate position can be added to the current tested shape
*/
void tryToAddCandidateCell(const Cell& candidate, int& nextAvaibleNumber, std::map<Cell, int>& candidateCells);
void tryToAddCandidatePosition(const Position& candidate, int& nextAvaibleNumber, std::map<Position, int>& candidatePositions);
};

View File

@@ -2,13 +2,16 @@
#include "Polyomino.h"
#include "Rotation.h"
#include "Block.h"
#include "Color.h"
#include <Set>
#include <String>
#include <set>
#include <string>
Piece::Piece(const Polyomino& polyomino, Color color) : polyomino(polyomino), color(color) {
Piece::Piece(const Polyomino& polyomino, Block blockType) :
polyomino(polyomino),
blockType(blockType) {
}
void Piece::rotate(Rotation rotation) {
@@ -20,19 +23,19 @@ void Piece::rotate(Rotation rotation) {
this->polyomino.rotateCCW();
}
std::set<Cell> Piece::getPositions() const {
return this->polyomino.getCells();
std::set<Position> Piece::getPositions() const {
return this->polyomino.getPositions();
}
int Piece::getLength() const {
return this->polyomino.getLength();
}
Color Piece::getColor() const {
return this->color;
Block Piece::getBlockType() const {
return this->blockType;
}
std::ostream& operator<<(std::ostream& os, const Piece& piece) {
os << COLOR_CODES[piece.color] << piece.polyomino << COLOR_RESET;
os << getConsoleColorCode(piece.blockType) << piece.polyomino << getResetConsoleColorCode();
return os;
}

View File

@@ -2,9 +2,10 @@
#include "Polyomino.h"
#include "Rotation.h"
#include "Block.h"
#include "Color.h"
#include <Set>
#include <set>
/**
@@ -13,13 +14,13 @@
class Piece {
private:
Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft
Color color; // the color of the piece
Block blockType; // the block type of the piece
public:
/**
* Creates a piece with a specified shape and color
* Creates a piece with a specified shape and block type
*/
Piece(const Polyomino& piece, Color color);
Piece(const Polyomino& piece, Block blockType);
/**
* Rotates the piece in the specified direction
@@ -27,22 +28,23 @@ class Piece {
void rotate(Rotation rotation);
/**
* Returns a copy of the list of cells of the piece
* @return A copy of the list of positions of the piece
*/
std::set<Cell> getPositions() const;
std::set<Position> getPositions() const;
/**
* Returns the length of the piece
* @return The length of the piece
*/
int getLength() const;
/**
* Returns the color of the piece
* @return The block type of the piece
*/
Color getColor() const;
Block getBlockType() const;
/**
* Stream output operator, adds a 2D grid representing the piece
* @return A reference to the output stream
*/
friend std::ostream& operator<<(std::ostream& os, const Piece& piece);
};

View File

@@ -3,8 +3,8 @@
#include "Generator.h"
#include "Piece.h"
#include <Vector>
#include <String>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <filesystem>
@@ -14,10 +14,9 @@
PiecesFiles::PiecesFiles() {
}
bool PiecesFiles::savePieces(int order) const {
// open pieces file
bool PiecesFiles::savePieces(int polyominoSize) const {
std::string filePath;
if (!this->getFilePath(order, filePath)) {
if (!this->getFilePath(polyominoSize, filePath)) {
return false;
}
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
@@ -25,46 +24,44 @@ bool PiecesFiles::savePieces(int order) const {
return false;
}
// generates the polyominos
Generator generator;
std::vector<Polyomino> nMinos = generator.generatePolyominos(order);
std::vector<Polyomino> nMinos = generator.generatePolyominos(polyominoSize);
// set the polyominos to their spawn position
// sorting the polyominos is done after setting spawn position to ensure the order is always the same
for (Polyomino& nMino : nMinos) {
nMino.goToSpawnPosition();
}
// sort the polyominos, is done after setting spawn position to ensure the order is always the same
std::sort(nMinos.begin(), nMinos.end());
// write pieces
Color pieceColor = firstPieceColor();
for (int i = 0; i < order; i++) nextPieceColor(pieceColor);
Block pieceblock = firstPieceBlockType();
for (int i = 0; i < polyominoSize; i++) {
nextPieceBlockType(pieceblock);
}
for (const Polyomino& nMino : nMinos) {
// write polyomino length
char lengthByte = nMino.getLength();
piecesFile.write(&lengthByte, 1);
// write the type and color of the piece
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + pieceColor;
nextPieceColor(pieceColor);
// write the type and block of the piece
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + pieceblock;
nextPieceBlockType(pieceblock);
piecesFile.write(&infoByte, 1);
// write the cells of the piece
char cellByte;
for (Cell cell : nMino.getCells()) {
cellByte = (cell.x << 4) + cell.y;
piecesFile.write(&cellByte, 1);
// write the positions of the piece
char positionByte;
for (Position position : nMino.getPositions()) {
positionByte = (position.x << 4) + position.y;
piecesFile.write(&positionByte, 1);
}
}
return true;
}
bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
// open pieces file
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
std::string filePath;
if (!this->getFilePath(order, filePath)) {
if (!this->getFilePath(polyominoSize, filePath)) {
return false;
}
std::ifstream piecesFile(filePath, std::ios::binary);
@@ -72,22 +69,20 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
return false;
}
// get empty vectors
pieces.clear();
convexPieces.clear();
holelessPieces.clear();
otherPieces.clear();
// set up masks
char convexMask = 0b1000'0000;
char holeMask = 0b0100'0000;
char colorMask = 0b0011'1111;
char blockMask = 0b0011'1111;
char xMask = 0b1111'0000;
char yMask = 0b0000'1111;
// read the pieces
char lengthByte;
int i = 0;
// read piece length
while (piecesFile.get(lengthByte)) {
if (piecesFile.eof()) break;
@@ -96,23 +91,21 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
piecesFile.get(infoByte);
bool isConvex = (infoByte & convexMask) >> 7;
bool hasHole = (infoByte & holeMask) >> 6;
Color color = Color(infoByte & colorMask);
Block block = Block(infoByte & blockMask);
// read cells
std::set<Cell> pieceCells;
char cellByte;
for (int i = 0; i < order; i++) {
piecesFile.get(cellByte);
int x = (cellByte & xMask) >> 4;
int y = cellByte & yMask;
pieceCells.insert(Cell{x, y});
// read positions
std::set<Position> piecepositions;
char positionByte;
for (int i = 0; i < polyominoSize; i++) {
piecesFile.get(positionByte);
int x = (positionByte & xMask) >> 4;
int y = positionByte & yMask;
piecepositions.insert(Position{x, y});
}
// create piece
Piece readPiece(Polyomino(pieceCells, lengthByte), color);
Piece readPiece(Polyomino(piecepositions, lengthByte), block);
pieces.push_back(readPiece);
// link it to its type
if (isConvex) {
convexPieces.push_back(i);
}
@@ -129,14 +122,12 @@ bool PiecesFiles::loadPieces(int order, std::vector<Piece>& pieces, std::vector<
return true;
}
bool PiecesFiles::getFilePath(int order, std::string& filePath) const {
// verify that the data folder exists
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
std::string dataFolderPath = "data/pieces/";
if (!std::filesystem::is_directory(dataFolderPath)) {
return false;
}
// return the file path
filePath = dataFolderPath + std::to_string(order) + "minos.bin";
filePath = dataFolderPath + std::to_string(polyominoSize) + "minos.bin";
return true;
}

View File

@@ -2,8 +2,8 @@
#include "Piece.h"
#include <Vector>
#include <String>
#include <vector>
#include <string>
/**
@@ -12,26 +12,26 @@
class PiecesFiles {
public:
/**
* Initializes file manager
* Default constructor
*/
PiecesFiles();
/**
* Generate a file containing all the pieces of the specified size,
* returns false if the file couldn't be created
* Generate a file containing all the pieces of the specified size
* @return If the file could be created
*/
bool savePieces(int order) const;
bool savePieces(int polyominoSize) const;
/**
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched,
* returns false if the file wasn't found
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched
* @return If the file was found
*/
bool loadPieces(int order, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
private:
/**
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched,
* returns false if the data folder wasn't found
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched
* @return If the data folder was found
*/
bool getFilePath(int order, std::string& filePath) const;
bool getFilePath(int polyominoSize, std::string& filePath) const;
};

View File

@@ -1,83 +1,82 @@
#include "Polyomino.h"
#include "Cell.h"
#include "Position.h"
#include <Vector>
#include <Set>
#include <vector>
#include <set>
#include <iostream>
#include <climits>
#include <algorithm>
Polyomino::Polyomino(const std::set<Cell>& cells) {
Polyomino::Polyomino(const std::set<Position>& positions) {
// find min/max
int minX = INT_MAX;
int maxX = INT_MIN;
int minY = INT_MAX;
int maxY = INT_MIN;
for (Cell cell : cells) {
if (cell.x < minX) minX = cell.x;
if (cell.x > maxX) maxX = cell.x;
if (cell.y < minY) minY = cell.y;
if (cell.y > maxY) maxY = cell.y;
for (Position position : positions) {
if (position.x < minX) minX = position.x;
if (position.x > maxX) maxX = position.x;
if (position.y < minY) minY = position.y;
if (position.y > maxY) maxY = position.y;
}
// normalize
std::set<Cell> newCells;
for (Cell cell : cells) {
newCells.insert(Cell{cell.x - minX, cell.y - minY});
std::set<Position> newPositions;
for (Position position : positions) {
newPositions.insert(Position{position.x - minX, position.y - minY});
}
this->cells = newCells;
this->positions = newPositions;
// set polyomino length
this->length = std::max(maxX - minX + 1, maxY - minY + 1);
}
Polyomino::Polyomino(const std::set<Cell>& cells, int length) : cells(cells), length(length) {
Polyomino::Polyomino(const std::set<Position>& positions, int length) :
positions(positions),
length(length) {
}
void Polyomino::normalize() {
// find min values
int minX = INT_MAX;
int minY = INT_MAX;
for (Cell cell : this->cells) {
if (cell.x < minX) minX = cell.x;
if (cell.y < minY) minY = cell.y;
for (Position position : this->positions) {
if (position.x < minX) minX = position.x;
if (position.y < minY) minY = position.y;
}
// translate the polyomino to the lowest unsigned values
std::set<Cell> newCells;
for (Cell cell : this->cells) {
newCells.insert(Cell{cell.x - minX, cell.y - minY});
std::set<Position> newPositions;
for (Position position : this->positions) {
newPositions.insert(Position{position.x - minX, position.y - minY});
}
this->cells = newCells;
this->positions = newPositions;
}
void Polyomino::rotateCW() {
// rotate 90° clockwise
std::set<Cell> newCells;
for (Cell cell : this->cells) {
newCells.insert(Cell{cell.y, (length - 1) - (cell.x)});
std::set<Position> newPositions;
for (Position position : this->positions) {
newPositions.insert(Position{position.y, (length - 1) - (position.x)});
}
this->cells = newCells;
this->positions = newPositions;
}
void Polyomino::rotate180() {
// rotate 180°
std::set<Cell> newCells;
for (Cell cell : this->cells) {
newCells.insert(Cell{(length - 1) - (cell.x), (length - 1) - (cell.y)});
std::set<Position> newPositions;
for (Position position : this->positions) {
newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)});
}
this->cells = newCells;
this->positions = newPositions;
}
void Polyomino::rotateCCW() {
// rotate 90° counter-clockwise
std::set<Cell> newCells;
for (Cell cell : this->cells) {
newCells.insert(Cell{(length - 1) - (cell.y), cell.x});
std::set<Position> newPositions;
for (Position position : this->positions) {
newPositions.insert(Position{(length - 1) - (position.y), position.x});
}
this->cells = newCells;
this->positions = newPositions;
}
void Polyomino::goToSpawnPosition() {
@@ -91,15 +90,15 @@ void Polyomino::goToSpawnPosition() {
linesCompleteness.push_back(empty);
}
// calculates amount of cells per rows and columns
for (Cell cell : this->cells) {
linesCompleteness.at(0).at(cell.y) += 1; // 0 = bottom to top = no rotation
linesCompleteness.at(1).at((length - 1) - cell.x) += 1; // 1 = right to left = CW
linesCompleteness.at(2).at((length - 1) - cell.y) += 1; // 2 = top to bottom = 180
linesCompleteness.at(3).at(cell.x) += 1; // 3 = left to right = CCW
// calculates amount of squares per rows and columns
for (Position position : this->positions) {
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
linesCompleteness.at(3).at(position.x) += 1; // 3 = left to right = CCW
}
// checks for empty lines
// count empty lines
int horizontalEmptyLines = 0;
int verticalEmptyLines = 0;
for (int i = 0; i < 4; i++) {
@@ -157,26 +156,24 @@ void Polyomino::goToSpawnPosition() {
// find min
int minX = INT_MAX;
int minY = INT_MAX;
for (Cell cell : this->cells) {
if (cell.x < minX) minX = cell.x;
if (cell.y < minY) minY = cell.y;
for (Position position : this->positions) {
if (position.x < minX) minX = position.x;
if (position.y < minY) minY = position.y;
}
// center the piece with an up bias if it is assymetric
if (sideToBeOn % 2 == 1) {
std::swap(verticalEmptyLines, horizontalEmptyLines);
}
std::set<Cell> newCells;
for (Cell cell : cells) {
newCells.insert(Cell{(cell.x - minX) + (verticalEmptyLines / 2), (cell.y - minY) + ((horizontalEmptyLines + 1) / 2)});
std::set<Position> newPositions;
for (Position position : positions) {
newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)});
}
this->cells = newCells;
this->positions = newPositions;
}
void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesCompleteness, bool currentFlattestSides[4], int& sideToBeOn, bool checkLeftSide) const {
// for each line
for (int j = 0; j < this->length; j++) {
// we check which sides are the flattest
int max = 0;
std::set<int> maxOwners;
for (int i = 0; i < 4; i++) {
@@ -200,7 +197,7 @@ void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesC
}
}
// if there's no tie we choose this side
// if there's no tie we choose the only side
if (maxOwners.size() == 1) {
sideToBeOn = *maxOwners.begin();
return;
@@ -216,7 +213,7 @@ void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesC
}
bool Polyomino::isConvex() const {
// for each line and column we check if every cells are adjacent to each others
// for each line and column we check if every squares are adjacent to each others
for (int j = 0; j < this->length; j++) {
bool startedLine = false;
bool completedLine = false;
@@ -224,7 +221,7 @@ bool Polyomino::isConvex() const {
bool completedColumn = false;
for (int i = 0; i < this->length; i++) {
// line check
if (this->cells.contains(Cell{i, j})) {
if (this->positions.contains(Position{i, j})) {
if (completedLine) return false;
else startedLine = true;
}
@@ -232,7 +229,7 @@ bool Polyomino::isConvex() const {
if (startedLine) completedLine = true;
}
// column check
if (this->cells.contains(Cell{j, i})) {
if (this->positions.contains(Position{j, i})) {
if (completedColumn) return false;
else startedColumn = true;
}
@@ -245,71 +242,65 @@ bool Polyomino::isConvex() const {
}
bool Polyomino::hasHole() const {
// add every outer cells of the square containing the polyomino
std::set<Cell> emptyCells;
// add every empty square on the outer of the box containing the polyomino
std::set<Position> emptyPositions;
for (int i = 0; i < this->length - 1; i++) {
this->tryToInsertCell(emptyCells, Cell{i, 0}); // up row
this->tryToInsertCell(emptyCells, Cell{this->length - 1, i}); // rigth column
this->tryToInsertCell(emptyCells, Cell{this->length - 1 - i, this->length - 1}); // bottom row
this->tryToInsertCell(emptyCells, Cell{0, this->length - 1 - i}); // left column
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
}
// if we didn't reached all empty cells in the square then there was some contained within the polyomino, i.e. there was a hole
return (emptyCells.size() < (this->length * this->length) - this->cells.size());
// 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());
}
void Polyomino::tryToInsertCell(std::set<Cell>& emptyCells, const Cell& candidate) const {
// check if the cell is in the square containing the polyomino
void Polyomino::tryToInsertPosition(std::set<Position>& emptyPositions, const Position& candidate) const {
if (candidate.x >= this->length || candidate.x < 0 || candidate.y >= this->length || candidate.y < 0) return;
if (this->positions.contains(candidate) || emptyPositions.contains(candidate)) return;
// check if the cell is empty and hasn't already been tested
if (this->cells.contains(candidate) || emptyCells.contains(candidate)) return;
// adds the cell to the list of empty cells and try its neighbors
emptyCells.insert(candidate);
tryToInsertCell(emptyCells, Cell{candidate.x, candidate.y + 1});
tryToInsertCell(emptyCells, Cell{candidate.x + 1, candidate.y});
tryToInsertCell(emptyCells, Cell{candidate.x, candidate.y - 1});
tryToInsertCell(emptyCells, Cell{candidate.x - 1, candidate.y});
// if it's a new empty square, try its neighbors
emptyPositions.insert(candidate);
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y + 1});
tryToInsertPosition(emptyPositions, Position{candidate.x + 1, candidate.y});
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y - 1});
tryToInsertPosition(emptyPositions, Position{candidate.x - 1, candidate.y});
}
std::set<Cell> Polyomino::getCells() const {
return this->cells;
std::set<Position> Polyomino::getPositions() const {
return this->positions;
}
int Polyomino::getLength() const {
return this->length;
}
int Polyomino::getPolyominoOrder() const {
return this->cells.size();
int Polyomino::getPolyominoSize() const {
return this->positions.size();
}
bool Polyomino::operator<(const Polyomino& other) const {
// if one has an inferior length then it is deemed inferior
if (this->length != other.length) return this->length < other.length;
// else we check for all cells from left to right and top to bottom, until one has a cell that the other doesn't
// we check for all positions from left to right and top to bottom, until one has a square that the other doesn't
for (int y = this->length - 1; y >= 0; y--) {
for (int x = 0; x < this->length; x++) {
bool hasThisCell = this->cells.contains(Cell{x, y});
bool hasOtherCell = other.cells.contains(Cell{x, y});
if (hasThisCell != hasOtherCell) return hasThisCell;
bool hasThisposition = this->positions.contains(Position{x, y});
bool hasOtherposition = other.positions.contains(Position{x, y});
if (hasThisposition != hasOtherposition) return hasThisposition;
}
}
// if they are equal
return false;
}
bool Polyomino::operator ==(const Polyomino& other) const {
return this->cells == other.cells;
return this->positions == other.positions;
}
std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) {
for (int y = polyomino.length - 1; y >= 0; y--) {
for (int x = 0; x < polyomino.length; x++) {
if (polyomino.cells.contains(Cell{x, y})) {
if (polyomino.positions.contains(Position{x, y})) {
os << "*";
}
else {

View File

@@ -1,9 +1,9 @@
#pragma once
#include "Cell.h"
#include "Position.h"
#include <Vector>
#include <Set>
#include <vector>
#include <set>
#include <iostream>
@@ -12,19 +12,19 @@
*/
class Polyomino {
private:
std::set<Cell> cells; // the squares composing the polyomino, (0,0) is downleft
int length; // the size of the smallest square in which the polyomino can fit on any rotation
std::set<Position> positions; // the squares composing the polyomino, (0,0) is downleft
int length; // the size of the smallest square box in which the polyomino can fit on any rotation
public:
/**
* Creates a polyomino with the specified cells and normalizes it, wheter it is actually a polyonimo is not checked
* Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked
*/
Polyomino(const std::set<Cell>& cells);
Polyomino(const std::set<Position>& positions);
/**
* Creates a polyomino with the specified cells and length, wheter it is actually a polyonimo of this length is not checked
* Creates a polyomino with the specified positions and length, wheter it is actually a polyonimo of this length is not checked
*/
Polyomino(const std::set<Cell>& cells, int length);
Polyomino(const std::set<Position>& positions, int length);
/**
* Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0)
@@ -32,17 +32,17 @@ class Polyomino {
void normalize();
/**
* Rotates the polyomino 90° clockwise, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
* Rotates the polyomino 90° clockwise, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
*/
void rotateCW();
/**
* Rotates the polyomino 180°, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
* Rotates the polyomino 180°, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
*/
void rotate180();
/**
* Rotates the polyomino 90° counter-clockwise, the center of rotation being the middle of the square going from (0,0) to (length-1, length-1)
* Rotates the polyomino 90° counter-clockwise, the center of rotation being the middle of the box going from (0,0) to (length-1, length-1)
*/
void rotateCCW();
@@ -59,12 +59,14 @@ class Polyomino {
public:
/**
* Returns wheter the polyomino is convex, that is if every line and column has at most one continuous line of cells
* Check if the polyomino is convex, that is if every line and column has at most one continuous line of positions
* @return If the polyomino is convex
*/
bool isConvex() const;
/**
* Returns wheter the polyomino has at least one hole
* Check if the polyomino has at least one hole
* @return If the polyomino has at least one hole
*/
bool hasHole() const;
@@ -72,37 +74,40 @@ class Polyomino {
/**
* Auxiliary method of hasHole()
*/
void tryToInsertCell(std::set<Cell>& emptyCells, const Cell& candidate) const;
void tryToInsertPosition(std::set<Position>& emptypositions, const Position& candidate) const;
public:
/**
* Returns a copy of the cells of the polyomino
* @return A copy of the positions of the polyomino
*/
std::set<Cell> getCells() const;
std::set<Position> getPositions() const;
/**
* Returns the length of the polyomino
* @return The length of the polyomino
*/
int getLength() const;
/**
* Returns the number of squares in the polyomino
* @return The number of squares in the polyomino
*/
int getPolyominoOrder() const;
int getPolyominoSize() const;
/**
* Strict inferiority operator, a polyomino is inferior than another if it has a smaller length, or if they are the same length,
* while checking from left to right and top to bottom, is the first which has a cell while the other doesn't
* while checking from left to right and top to bottom, is the first which has a square while the other don't
* @return If the polyomino is inferior than another
*/
bool operator<(const Polyomino& other) const;
/**
* Equality operator, two polyominos are equal if they overlap, that means two polyominos of the same shape but different positions will not be equal
* Equality operator, two polyominos are equal if their positions are the same, that means two polyominos of the same shape at different places will not be equal
* @return If the polyomino is equal to another
*/
bool operator ==(const Polyomino& other) const;
/**
* Stream output operator, adds a 2D grid representing the polyomino
* @return A reference to the output stream
*/
friend std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino);
};

72
src/Pieces/Position.h Normal file
View File

@@ -0,0 +1,72 @@
#pragma once
#include <iostream>
/**
* A position on a 2D grid
*/
struct Position {
int x; // x position
int y; // y position
};
/**
* Addition operator
* @return The sums of the coordinates of both positions
*/
inline Position operator+(const Position& left, const Position& right) {
return Position{left.x + right.x, left.y + right.y};
}
/**
* Additive assignation operator, adds the coordinates of the right position to the left one
* @return A reference to the left position
*/
inline Position& operator+=(Position& left, const Position& right) {
left = left + right;
return left;
}
/**
* Substraction operator
* @return The difference of the coordinate between the left and right position
*/
inline Position operator-(const Position& left, const Position& right) {
return Position{left.x - right.x, left.y - right.y};
}
/**
* Substractive assignation operator, substracts the coordinates of the right position from the left one
* @return A reference to the left position
*/
inline Position& operator-=(Position& left, const Position& right) {
left = left + right;
return left;
}
/**
* Strict inferiority operator, a position is inferior to another if it is lower or at the same height and more to the left
* @return If the left position is inferior to the right position
*/
inline bool operator<(const Position& left, const Position& right) {
return (left.x == right.x) ? (left.y < right.y) : (left.x < right.x);
}
/**
* Equality operator, two positions are equal if they have the same coordinates
* @return If the two positions are equals
*/
inline bool operator==(const Position& left, const Position& right) {
return (left.x == right.x) && (left.y == right.y);
}
/**
* Stream output operator, adds the coordinates of the position to the stream
* @return A reference to the output stream
*/
inline std::ostream& operator<<(std::ostream& os, const Position& position) {
os << "x: " << position.x << " y: " << position.y;
return os;
}

View File

@@ -13,14 +13,16 @@ enum Rotation {
/**
* Addition operator, returns a rotation corresponding to doing both rotations
* Addition operator
* @return A rotation corresponding to doing both rotations
*/
inline Rotation operator+(const Rotation& left, const Rotation& right) {
return Rotation((left + right) % 4);
}
/**
* Additive assignation operator, rotate the left rotation by the right rotation
* Additive assignation operator, rotates the left rotation by the right rotation
* @return A reference to the left rotation
*/
inline Rotation& operator+=(Rotation& left, const Rotation& right) {
left = left + right;