9 Commits

Author SHA1 Message Date
de63cd22b6 optimize vector initialization
All checks were successful
Linux arm64 / Build (push) Successful in 2m13s
2025-07-28 09:51:02 +02:00
07ba9619ed use bitsets
All checks were successful
Linux arm64 / Build (push) Successful in 2m13s
2025-07-21 16:06:27 +02:00
4095103843 fix some compiler warnings
All checks were successful
Linux arm64 / Build (push) Successful in 2m27s
2025-07-20 23:02:45 +02:00
ecc035c972 fix polyominos
All checks were successful
Linux arm64 / Build (push) Successful in 2m29s
2025-07-20 22:32:33 +02:00
dd6da58642 decrease file size
All checks were successful
Linux arm64 / Build (push) Successful in 2m27s
2025-07-20 21:39:10 +02:00
a3ef52c7a1 decrease file retrieving time
All checks were successful
Linux arm64 / Build (push) Successful in 2m29s
2025-07-20 21:08:36 +02:00
72e9f420ab add missing include
All checks were successful
Linux arm64 / Build (push) Successful in 2m32s
2025-07-19 23:53:27 +02:00
46b9b8dd65 begin optimize Generator
Some checks failed
Linux arm64 / Build (push) Failing after 2m4s
2025-07-19 23:39:22 +02:00
d5b51213c8 change Position and Polynomio structs (less memory usage)
Some checks failed
Linux arm64 / Build (push) Failing after 2m9s
2025-07-19 20:50:36 +02:00
30 changed files with 431 additions and 281 deletions

5
.gitignore vendored
View File

@@ -17,8 +17,3 @@ doc/mockups/*
data/pieces/*.bin data/pieces/*.bin
data/config/*.bin data/config/*.bin
data/config/keybinds/*.bin data/config/keybinds/*.bin
# flatpak files
flatpak/.flatpak-builder
flatpak/builddir
flatpak/repo

View File

@@ -1,2 +0,0 @@
#!/bin/sh
JMINOS_DATA="/app/share" /app/bin/graph

View File

@@ -1,8 +0,0 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Jminos
Exec=/usr/bin/graph
Terminal=false
Categories=Games;
Comment=Amazing stacker game by the J

View File

@@ -1,40 +0,0 @@
id: org.zulianc.jminos
runtime: org.freedesktop.Platform
runtime-version: "24.08"
sdk: org.freedesktop.Sdk
command: FlatpakLaunch.sh
finish-args:
- --socket=x11
- --device=dri
modules:
- name: xmake
buildsystem: simple
no-autogen: true
cleanup: ['*']
build-commands:
- ./configure --prefix=/app
- make -j $(nproc)
- prefix=/app ./scripts/get.sh __local__ __install_only__
sources:
- type: git
url: https://github.com/xmake-io/xmake.git
tag: v3.0.1
- name: jminos
buildsystem: simple
build-options:
build-args:
- --share=network
build-commands:
- xmake f --policies=package.install_locally -m release -y
- xmake
- xmake install -o output
- install -D output/bin/* ${FLATPAK_DEST}/bin
- cp -r output/data ${FLATPAK_DEST}/share
- install -D flatpak/FlatpakLaunch.sh ${FLATPAK_DEST}/bin
- install -D flatpak/org.zulianc.jminos.desktop ${FLATPAK_DEST}/share/applications
sources:
- type: dir
path: ..

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

@@ -20,10 +20,10 @@ Board::Board(int width, int height) :
} }
void Board::changeBlock(const Position& position, Block block) { void Board::changeBlock(const Position& position, Block block) {
if (position.x < 0 || position.x >= this->width || position.y < 0) return; if (position.x < 0 || static_cast<unsigned>(position.x) >= this->width || position.y < 0) return;
// resize the grid if needed // resize the grid if needed
if (position.y >= this->grid.size()) { if (static_cast<unsigned>(position.y) >= this->grid.size()) {
for (int j = this->grid.size(); j <= position.y; j++) { for (int j = this->grid.size(); j <= position.y; j++) {
this->grid.push_back(this->emptyRow); this->grid.push_back(this->emptyRow);
} }
@@ -34,7 +34,7 @@ void Board::changeBlock(const Position& position, Block block) {
void Board::insertRow(int height, int holePosition, Block blockType) { void Board::insertRow(int height, int holePosition, Block blockType) {
std::vector<Block> insertedRow; std::vector<Block> insertedRow;
for (int i = 0; i < this->width; i++) { for (unsigned i = 0; i < this->width; i++) {
if (i == holePosition) { if (i == holePosition) {
insertedRow.push_back(NOTHING); insertedRow.push_back(NOTHING);
} }
@@ -51,7 +51,7 @@ int Board::clearRows() {
int clearedLines = 0; int clearedLines = 0;
for (int j = this->grid.size() - 1; j >= 0; j--) { for (int j = this->grid.size() - 1; j >= 0; j--) {
bool lineIsFull = true; bool lineIsFull = true;
int i = 0; unsigned i = 0;
while (lineIsFull && (i < width)) { while (lineIsFull && (i < width)) {
if (this->grid.at(j).at(i) == NOTHING) { if (this->grid.at(j).at(i) == NOTHING) {
lineIsFull = false; lineIsFull = false;

View File

@@ -13,8 +13,8 @@ class Board {
private: private:
std::vector<std::vector<Block>> grid; // the grid, (0,0) is downleft std::vector<std::vector<Block>> grid; // the grid, (0,0) is downleft
std::vector<Block> emptyRow; // an empty row of blocks std::vector<Block> emptyRow; // an empty row of blocks
int width; // the width of the grid unsigned width; // the width of the grid
int height; // the base height of the grid, which can extend indefinitely unsigned height; // the base height of the grid, which can extend indefinitely
public: public:
/** /**

View File

@@ -44,7 +44,7 @@ void GameBoard::initialize() {
bool GameBoard::moveLeft() { bool GameBoard::moveLeft() {
this->movedLeftLast = true; this->movedLeftLast = true;
if (this->activePieceInWall(Position{-1, 0})) { if (this->activePieceInWall(Position(-1, 0))) {
return false; return false;
} }
else { else {
@@ -57,7 +57,7 @@ bool GameBoard::moveLeft() {
bool GameBoard::moveRight() { bool GameBoard::moveRight() {
this->movedLeftLast = false; this->movedLeftLast = false;
if (this->activePieceInWall(Position{1, 0})) { if (this->activePieceInWall(Position(1, 0))) {
return false; return false;
} }
else { else {
@@ -68,7 +68,7 @@ bool GameBoard::moveRight() {
} }
bool GameBoard::moveDown() { bool GameBoard::moveDown() {
if (this->activePieceInWall(Position{0, -1})) { if (this->activePieceInWall(Position(0, -1))) {
return false; return false;
} }
else { else {
@@ -100,10 +100,10 @@ bool GameBoard::rotate(Rotation rotation) {
for (Position position : stored.getPositions()) { for (Position position : stored.getPositions()) {
Position positionInGrid(position + this->activePiecePosition); Position positionInGrid(position + this->activePiecePosition);
safePositions.insert(positionInGrid); safePositions.insert(positionInGrid);
safePositions.insert(positionInGrid + Position{0, 1}); safePositions.insert(positionInGrid + Position(0, 1));
safePositions.insert(positionInGrid + Position{1, 0}); safePositions.insert(positionInGrid + Position(1, 0));
safePositions.insert(positionInGrid + Position{0, -1}); safePositions.insert(positionInGrid + Position(0, -1));
safePositions.insert(positionInGrid + Position{-1, 0}); safePositions.insert(positionInGrid + Position(-1, 0));
} }
// first try kicking the piece down // first try kicking the piece down
@@ -147,18 +147,18 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
// we first check the side to which the player moved last // we first check the side to which the player moved last
if (movedLeftLast) { if (movedLeftLast) {
if (overlapsLeft) { if (overlapsLeft) {
if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true; if (this->tryFittingKickedPiece(safePositions, Position(-i, j), overlapsLeft)) return true;
} }
if (overlapsRight) { if (overlapsRight) {
if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true; if (this->tryFittingKickedPiece(safePositions, Position(+i, j), overlapsRight)) return true;
} }
} }
else { else {
if (overlapsRight) { if (overlapsRight) {
if (this->tryFittingKickedPiece(safePositions, Position({+i, j}), overlapsRight)) return true; if (this->tryFittingKickedPiece(safePositions, Position(+i, j), overlapsRight)) return true;
} }
if (overlapsLeft) { if (overlapsLeft) {
if (this->tryFittingKickedPiece(safePositions, Position({-i, j}), overlapsLeft)) return true; if (this->tryFittingKickedPiece(safePositions, Position(-i, j), overlapsLeft)) return true;
} }
} }
@@ -265,11 +265,11 @@ bool GameBoard::activePieceInWall(const Position& shift) const {
} }
bool GameBoard::touchesGround() const { bool GameBoard::touchesGround() const {
return this->activePieceInWall(Position{0, -1}); return this->activePieceInWall(Position(0, -1));
} }
Position GameBoard::lowestPosition() const { Position GameBoard::lowestPosition() const {
Position shift = Position{0, -1}; Position shift = Position(0, -1);
while (!activePieceInWall(shift)) { while (!activePieceInWall(shift)) {
shift.y -= 1; shift.y -= 1;
} }
@@ -278,8 +278,8 @@ Position GameBoard::lowestPosition() const {
} }
LineClear GameBoard::lockPiece() { LineClear GameBoard::lockPiece() {
bool isLockedInPlace = (this->activePieceInWall(Position{0, 1}) && this->activePieceInWall(Position{1, 0}) bool isLockedInPlace = (this->activePieceInWall(Position(0, 1)) && this->activePieceInWall(Position(1, 0))
&& this->activePieceInWall(Position{-1, 0}) && this->activePieceInWall(Position{0, -1})); && this->activePieceInWall(Position(-1, 0)) && this->activePieceInWall(Position(0, -1)));
for (Position position : this->activePiece->getPositions()) { for (Position position : this->activePiece->getPositions()) {
this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType()); this->board.changeBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
@@ -345,7 +345,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
// print only the position were the active piece is // print only the position were the active piece is
for (int y = gameboard.activePiecePosition.y + gameboard.activePiece->getLength() - 1; y >= gameboard.board.getBaseHeight(); y--) { for (int y = gameboard.activePiecePosition.y + gameboard.activePiece->getLength() - 1; y >= gameboard.board.getBaseHeight(); y--) {
for (int x = 0; x < gameboard.board.getWidth(); x++) { for (int x = 0; x < gameboard.board.getWidth(); x++) {
bool hasActivePiece = gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition); bool hasActivePiece = gameboard.activePiece->containsSquare(Position(x, y) - gameboard.activePiecePosition);
if (hasActivePiece) { if (hasActivePiece) {
os << "*"; os << "*";
} }
@@ -361,7 +361,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
Block pieceBlockType = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getBlockType(); Block pieceBlockType = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getBlockType();
for (int y = gameboard.board.getBaseHeight() - 1; y >= 0; y--) { for (int y = gameboard.board.getBaseHeight() - 1; y >= 0; y--) {
for (int x = 0; x < gameboard.board.getWidth(); x++) { for (int x = 0; x < gameboard.board.getWidth(); x++) {
bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition); bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->containsSquare(Position(x, y) - gameboard.activePiecePosition);
// the active piece takes visual priority over the board // the active piece takes visual priority over the board
if (hasActivePiece) { if (hasActivePiece) {
@@ -369,7 +369,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
os << "*"; os << "*";
} }
else { else {
Block block = gameboard.board.getBlock(Position{x, y}); Block block = gameboard.board.getBlock(Position(x, y));
os << getConsoleColorCode(block); os << getConsoleColorCode(block);
if (block != NOTHING) { if (block != NOTHING) {
os << "*"; os << "*";

View File

@@ -84,7 +84,7 @@ class GameBoard {
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions * Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
* @return If the shifted active piece overlaps with one of the position * @return If the shifted active piece overlaps with one of the position
*/ */
bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const; bool activePieceOverlaps(const std::set<Position>& safePositions, const Position& shift = Position(0, 0)) const;
public: public:
/** /**
@@ -103,7 +103,7 @@ class GameBoard {
* Checks if one of the active piece's positions touches a wall in the board * Checks if one of the active piece's positions touches a wall in the board
* @return If the active piece is in a wall * @return If the active piece is in a wall
*/ */
bool activePieceInWall(const Position& shift = Position{0, 0}) const; bool activePieceInWall(const Position& shift = Position(0, 0)) const;
/** /**
* Checks is the active piece as a wall directly below one of its position * Checks is the active piece as a wall directly below one of its position

View File

@@ -27,12 +27,12 @@ PiecesList::PiecesList() {
this->pushBackEmptyVectors(); this->pushBackEmptyVectors();
} }
bool PiecesList::loadPieces(int max_size) { bool PiecesList::loadPieces(unsigned max_size) {
if (max_size < 1) return false; if (max_size < 1) return false;
if (max_size <= this->highestLoadedSize) return true; if (max_size <= this->highestLoadedSize) return true;
PiecesFiles piecesFiles; PiecesFiles piecesFiles;
for (int i = this->highestLoadedSize + 1; i <= max_size; i++) { for (unsigned i = this->highestLoadedSize + 1; i <= max_size; i++) {
if (!piecesFiles.loadPieces(i, this->loadedPieces.at(i), this->convexPieces.at(i), this->holelessPieces.at(i), this->otherPieces.at(i))) { if (!piecesFiles.loadPieces(i, this->loadedPieces.at(i), this->convexPieces.at(i), this->holelessPieces.at(i), this->otherPieces.at(i))) {
return false; return false;
} }
@@ -45,14 +45,14 @@ bool PiecesList::loadPieces(int max_size) {
return true; return true;
} }
bool PiecesList::selectPiece(int size, int number) { bool PiecesList::selectPiece(unsigned size, unsigned number) {
if (size < 1 || size > this->highestLoadedSize || number >= this->loadedPieces.at(size).size()) return false; if (size < 1 || size > this->highestLoadedSize || number >= this->loadedPieces.at(size).size()) return false;
this->selectedPieces.push_back(std::pair<int, int>(size, number)); this->selectedPieces.push_back(std::pair<int, int>(size, number));
return true; return true;
} }
bool PiecesList::selectAllPieces(int size) { bool PiecesList::selectAllPieces(unsigned size) {
if (size < 1 || size > this->highestLoadedSize) return false; if (size < 1 || size > this->highestLoadedSize) return false;
for (int i = 0; i < this->loadedPieces.at(size).size(); i++) { for (int i = 0; i < this->loadedPieces.at(size).size(); i++) {

View File

@@ -13,7 +13,7 @@
*/ */
class PiecesList { class PiecesList {
private: private:
int highestLoadedSize; // the highest size of pieces currently loaded unsigned highestLoadedSize; // the highest size of pieces currently loaded
std::vector<std::vector<Piece>> loadedPieces; // every loaded pieces by size std::vector<std::vector<Piece>> loadedPieces; // every loaded pieces by size
std::vector<std::vector<int>> convexPieces; // the list of convex loaded pieces by size std::vector<std::vector<int>> convexPieces; // the list of convex loaded pieces by size
std::vector<std::vector<int>> holelessPieces; // the list of holeless loaded pieces by size std::vector<std::vector<int>> holelessPieces; // the list of holeless loaded pieces by size
@@ -33,19 +33,19 @@ class PiecesList {
* Makes the list load all pieces up to the specified size * Makes the list load all pieces up to the specified size
* @return If all pieces up to the specified size are correctly loaded * @return If all pieces up to the specified size are correctly loaded
*/ */
[[nodiscard]] bool loadPieces(int max_size); [[nodiscard]] bool loadPieces(unsigned max_size);
/** /**
* Selects the specified piece * Selects the specified piece
* @return If the piece could be selected * @return If the piece could be selected
*/ */
bool selectPiece(int size, int number); bool selectPiece(unsigned size, unsigned number);
/** /**
* Selects all pieces of the specified size * Selects all pieces of the specified size
* @return If the pieces could be selected * @return If the pieces could be selected
*/ */
bool selectAllPieces(int size); bool selectAllPieces(unsigned size);
/** /**
* Selects all convex pieces of the specified size * Selects all convex pieces of the specified size

View File

@@ -15,7 +15,9 @@ AppMenu::AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings>
settings(settings), settings(settings),
renderWindow(renderWindow) { renderWindow(renderWindow) {
this->pressStartFont = sf::Font(AssetManager::getResourcePath("data/fonts/pressstart/prstart.ttf")); const Asset& file = getResource(AssetName::data_fonts_pressstart_prstartk_ttf);
this->pressStartFont = sf::Font(file.data, file.size);
} }
void AppMenu::updateMetaBinds() { void AppMenu::updateMetaBinds() {

View File

@@ -117,7 +117,7 @@ void GamePlayingAppMenu::drawFrame() const {
// board // board
for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) { for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) {
for (int x = 0; x < this->game.getBoard().getWidth(); x++) { for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
Block block = this->game.getBoard().getBlock(Position{x, y}); Block block = this->game.getBoard().getBlock(Position(x, y));
if (isBoardInvisible) block = NOTHING; if (isBoardInvisible) block = NOTHING;
sf::RectangleShape cell(cellSize); sf::RectangleShape cell(cellSize);
@@ -196,7 +196,7 @@ void GamePlayingAppMenu::drawFrame() const {
for (int y = 0; y < this->game.getNextPieces().at(i).getLength(); y++) { for (int y = 0; y < this->game.getNextPieces().at(i).getLength(); y++) {
for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) { for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) {
sf::RectangleShape cell(nextCellSize); sf::RectangleShape cell(nextCellSize);
if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) { if (this->game.getNextPieces().at(i).containsSquare(Position(x, y))) {
cell.setFillColor(pieceColor); cell.setFillColor(pieceColor);
lowestRank = y; lowestRank = y;
} }
@@ -223,7 +223,7 @@ void GamePlayingAppMenu::drawFrame() const {
for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) { for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) {
for (int x = 0; x < this->game.getHeldPiece()->getLength(); x++) { for (int x = 0; x < this->game.getHeldPiece()->getLength(); x++) {
sf::RectangleShape cell(holdCellSize); sf::RectangleShape cell(holdCellSize);
if (this->game.getHeldPiece()->getPositions().contains(Position{x, y})) { if (this->game.getHeldPiece()->containsSquare(Position(x, y))) {
cell.setFillColor(color); cell.setFillColor(color);
} }
else { else {

View File

@@ -23,7 +23,9 @@ SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menu
std::string textureName = ACTION_NAMES[action]; std::string textureName = ACTION_NAMES[action];
textureName = std::regex_replace(textureName, std::regex(" "), ""); textureName = std::regex_replace(textureName, std::regex(" "), "");
this->iconTextures[action] = sf::Texture(AssetManager::getResourcePath("data/images/keybinds/" + textureName + ".png"), false, {{0, 0}, {16, 16}}); const Asset& textureData = getResource("data/images/keybinds/" + textureName + ".png");
this->iconTextures[action] = sf::Texture(textureData.data, textureData.size, false, {{0, 0}, {16, 16}});
} }
} }

View File

@@ -6,7 +6,6 @@
#include <set> #include <set>
#include <fstream> #include <fstream>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include "../Utils/AssetManager.h"
Keybinds::Keybinds(int layoutNumber) : Keybinds::Keybinds(int layoutNumber) :
@@ -21,7 +20,7 @@ Keybinds::Keybinds(int layoutNumber) :
} }
void Keybinds::loadKeybindsFromFile() { void Keybinds::loadKeybindsFromFile() {
std::ifstream layoutFile(AssetManager::getResourcePath("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin"), std::ios::binary); std::ifstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::binary);
for (Action action : ACTION_LIST_IN_ORDER) { for (Action action : ACTION_LIST_IN_ORDER) {
this->keybinds.at(action).clear(); this->keybinds.at(action).clear();
@@ -48,7 +47,7 @@ void Keybinds::loadKeybindsFromFile() {
void Keybinds::saveKeybindsToFile() const { void Keybinds::saveKeybindsToFile() const {
if (!this->modifiable) return; if (!this->modifiable) return;
std::ofstream layoutFile(AssetManager::getResourcePath("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin"), std::ios::trunc | std::ios::binary); std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(this->layoutNumber) + ".bin", std::ios::trunc | std::ios::binary);
char byte; char byte;
for (Action action : ACTION_LIST_IN_ORDER) { for (Action action : ACTION_LIST_IN_ORDER) {

View File

@@ -3,7 +3,6 @@
#include "../Core/Menu.h" #include "../Core/Menu.h"
#include "Keybinds.h" #include "Keybinds.h"
#include "PiecesType.h" #include "PiecesType.h"
#include "../Utils/AssetManager.h"
#include <vector> #include <vector>
#include <optional> #include <optional>
@@ -50,7 +49,7 @@ void Settings::loadPieces(int loadablePiecesSizeRequest) {
} }
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest) { void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest) {
std::ifstream settingsFile(AssetManager::getResourcePath("data/config/settings.bin"), std::ios::binary); std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
char byte; char byte;
// file format version // file format version
@@ -159,7 +158,7 @@ void Settings::saveSettingsToFile() const {
this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile(); this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile();
std::ofstream settingsFile(AssetManager::getResourcePath("data/config/settings.bin"), std::ios::trunc | std::ios::binary); std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
char byte; char byte;
// file format version // file format version

View File

@@ -4,8 +4,6 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include "../Utils/AssetManager.h"
[[nodiscard]] bool resetSettingsFile(); [[nodiscard]] bool resetSettingsFile();
[[nodiscard]] bool resetKeybindFile(int layout); [[nodiscard]] bool resetKeybindFile(int layout);
@@ -20,7 +18,7 @@ int main() {
PiecesFiles pf; PiecesFiles pf;
bool warned = false; bool warned = false;
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) { for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
if (!std::filesystem::exists(AssetManager::getResourcePath("data/pieces/" + std::to_string(i) + "minos.bin"))) { if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
#ifndef DEBUG #ifndef DEBUG
if (!warned && i > DEBUG_PIECES_SIZE) { if (!warned && i > DEBUG_PIECES_SIZE) {
std::cout << "IMPORTANT: You are currently in release mode, if you do not wish to generate big pieces (can take several minutes), type 'xmake f -m debug'." << std::endl; std::cout << "IMPORTANT: You are currently in release mode, if you do not wish to generate big pieces (can take several minutes), type 'xmake f -m debug'." << std::endl;
@@ -38,7 +36,7 @@ int main() {
bool releasePiecesGenerated = true; bool releasePiecesGenerated = true;
for (int i = DEBUG_PIECES_SIZE + 1; i <= RELEASE_PIECES_SIZE; i++) { for (int i = DEBUG_PIECES_SIZE + 1; i <= RELEASE_PIECES_SIZE; i++) {
if (!std::filesystem::exists(AssetManager::getResourcePath("data/pieces/" + std::to_string(i) + "minos.bin"))) { if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
releasePiecesGenerated = false; releasePiecesGenerated = false;
} }
} }
@@ -49,29 +47,29 @@ int main() {
bool everythingGenerated = true; bool everythingGenerated = true;
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) { for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
auto filePath = AssetManager::getResourcePath("data/pieces/" + std::to_string(i) + "minos.bin"); std::string filePath = "data/pieces/" + std::to_string(i) + "minos.bin";
if (!std::filesystem::exists(filePath)) { if (!std::filesystem::exists(filePath)) {
std::cout << "ERROR: Could not open file " << filePath << std::endl; std::cout << "ERROR: Could not open file " + filePath << std::endl;
everythingIsOK &= false; everythingIsOK &= false;
} }
} }
// CHECK CONFIG FILES // CHECK CONFIG FILES
if (!std::filesystem::exists(AssetManager::getResourcePath("data/config/settings.bin"))) { if (!std::filesystem::exists("data/config/settings.bin")) {
std::cout << "INFO: Settings file not found, generating..." << std::endl; std::cout << "INFO: Settings file not found, generating..." << std::endl;
everythingIsOK &= resetSettingsFile(); everythingIsOK &= resetSettingsFile();
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) { for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
if (!std::filesystem::exists(AssetManager::getResourcePath("data/config/keybinds/layout" + std::to_string(i) + ".bin"))) { if (!std::filesystem::exists("data/config/keybinds/layout" + std::to_string(i) + ".bin")) {
std::cout << "INFO: Keybind file number " << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl; std::cout << "INFO: Keybind file number " << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl;
everythingIsOK &= resetKeybindFile(i); everythingIsOK &= resetKeybindFile(i);
} }
} }
} }
else { else {
std::ifstream settingsFile(AssetManager::getResourcePath("data/config/settings.bin"), std::ios::binary); std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
char byte; char byte;
settingsFile.get(byte); settingsFile.get(byte);
@@ -101,14 +99,14 @@ int main() {
bool resetSettingsFile() { bool resetSettingsFile() {
if (!std::filesystem::exists(AssetManager::getResourcePath("data/config"))) { if (!std::filesystem::exists("data/config")) {
std::filesystem::create_directories(AssetManager::getResourcePath("data/config")); std::filesystem::create_directories("data/config");
} }
auto filePath = AssetManager::getResourcePath("data/config/settings.bin"); std::string filePath ="data/config/settings.bin";
std::ofstream settingsFile(filePath, std::ios::trunc | std::ios::binary); std::ofstream settingsFile(filePath, std::ios::trunc | std::ios::binary);
if (!settingsFile.good()) { if (!settingsFile.good()) {
std::cerr << "ERROR: Could not open file " << filePath << std::endl; std::cerr << "ERROR: Could not open file " + filePath << std::endl;
return false; return false;
} }
@@ -182,14 +180,14 @@ bool resetKeybindFile(int layout) {
return false; return false;
} }
if (!std::filesystem::exists(AssetManager::getResourcePath("data/config/keybinds"))) { if (!std::filesystem::exists("data/config/keybinds")) {
std::filesystem::create_directories(AssetManager::getResourcePath("data/config/keybinds")); std::filesystem::create_directories("data/config/keybinds");
} }
auto filePath = AssetManager::getResourcePath("data/config/keybinds/layout" + std::to_string(layout) + ".bin"); std::string filePath = "data/config/keybinds/layout" + std::to_string(layout) + ".bin";
std::ofstream layoutFile(filePath, std::ios::trunc | std::ios::binary); std::ofstream layoutFile(filePath, std::ios::trunc | std::ios::binary);
if (!layoutFile.good()) { if (!layoutFile.good()) {
std::cerr << "ERROR: Could not open file " << filePath << std::endl; std::cerr << "ERROR: Could not open file " + filePath << std::endl;
return false; return false;
} }

View File

@@ -11,25 +11,25 @@
Generator::Generator() { Generator::Generator() {
} }
std::vector<Polyomino> Generator::generatePolyominoes(int polyominoSize) { std::vector<Polyomino>&& Generator::generatePolyominoes(int polyominoSize) {
this->validPolyominoes.clear(); this->validPolyominoes.clear();
this->currentTestedShape.clear(); this->currentTestedShape.clear();
// a polyomino has at least 1 square // a polyomino has at least 1 square
if (polyominoSize < 1) return this->validPolyominoes; if (polyominoSize < 1) return std::move(this->validPolyominoes);
// always place the first cell at (0, 0) // always place the first cell at (0, 0)
this->currentTestedShape.insert(Position{0, 0}); this->currentTestedShape.insert(Position(0, 0));
std::map<Position, int> candidatePositions; std::map<Position, int> candidatePositions;
this->generate(polyominoSize, 0, 1, candidatePositions); this->generate(polyominoSize, 0, 1, candidatePositions);
return this->validPolyominoes; return std::move(this->validPolyominoes);
} }
void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) { void Generator::generate(unsigned polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) {
// recursion stop // recursion stop
if (polyominoSize == this->currentTestedShape.size()) { if (polyominoSize == this->currentTestedShape.size()) {
Polyomino candidate(this->currentTestedShape); Polyomino candidate(std::move(this->currentTestedShape));
// we sort the rotations of the polyominoes // we sort the rotations of the polyominoes
std::vector<Polyomino> candidateRotations; std::vector<Polyomino> candidateRotations;
@@ -51,14 +51,14 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
// generate the list of candidate positions // generate the list of candidate positions
for (const Position position : this->currentTestedShape) { for (const Position position : this->currentTestedShape) {
this->tryToAddCandidatePosition(Position{position.x, position.y + 1}, nextAvaibleNumber, candidatePositions); 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 + 1, position.y), nextAvaibleNumber, candidatePositions);
this->tryToAddCandidatePosition(Position{position.x, position.y - 1}, nextAvaibleNumber, candidatePositions); 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 - 1, position.y), nextAvaibleNumber, candidatePositions);
} }
// try adding a square only to positions with a higher number than the last one // try adding a square only to positions with a higher number than the last one
for (const auto [key, val] : candidatePositions) { for (const auto& [key, val] : candidatePositions) {
if (val > lastAddedPositionNumber) { if (val > lastAddedPositionNumber) {
this->currentTestedShape.insert(key); this->currentTestedShape.insert(key);
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions); this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);

View File

@@ -25,13 +25,13 @@ class Generator {
* Generates the list of all one-sided polyominoes of the specified size * Generates the list of all one-sided polyominoes of the specified size
* @return The list of polyominoes * @return The list of polyominoes
*/ */
std::vector<Polyomino> generatePolyominoes(int polyominoSize); std::vector<Polyomino>&& generatePolyominoes(int polyominoSize);
private: private:
/** /**
* Generates all one-sided polyominoes of the specified size using the current tested shape * Generates all one-sided polyominoes of the specified size using the current tested shape
*/ */
void generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions); void generate(unsigned polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions);
/** /**
* Checks wheter a candidate position can be added to the current tested shape * Checks wheter a candidate position can be added to the current tested shape

View File

@@ -9,11 +9,10 @@
#include <string> #include <string>
Piece::Piece(const Polyomino& polyomino, Block blockType) : Piece::Piece(Polyomino&& polyomino, Block blockType) :
polyomino(polyomino), polyomino(polyomino),
blockType(blockType) { blockType(blockType),
rotationState(NONE) {
this->rotationState = NONE;
} }
void Piece::rotate(Rotation rotation) { void Piece::rotate(Rotation rotation) {
@@ -38,8 +37,8 @@ void Piece::defaultRotation() {
this->rotationState = NONE; this->rotationState = NONE;
} }
const std::set<Position>& Piece::getPositions() const { const Polyomino& Piece::getPositions() const {
return this->polyomino.getPositions(); return this->polyomino;
} }
int Piece::getLength() const { int Piece::getLength() const {
@@ -54,3 +53,7 @@ std::ostream& operator<<(std::ostream& os, const Piece& piece) {
os << getConsoleColorCode(piece.blockType) << piece.polyomino << getResetConsoleColorCode(); os << getConsoleColorCode(piece.blockType) << piece.polyomino << getResetConsoleColorCode();
return os; return os;
} }
bool Piece::containsSquare(const Position& position) const {
return polyomino.contains(position);
}

View File

@@ -21,7 +21,7 @@ class Piece {
/** /**
* Creates a piece with a specified shape and block type * Creates a piece with a specified shape and block type
*/ */
Piece(const Polyomino& piece, Block blockType); Piece(Polyomino&& piece, Block blockType);
/** /**
* Rotates the piece in the specified direction * Rotates the piece in the specified direction
@@ -36,7 +36,13 @@ class Piece {
/** /**
* @return The list of positions of the piece * @return The list of positions of the piece
*/ */
const std::set<Position>& getPositions() const; const Polyomino& getPositions() const;
/**
* @param the position of the square
* @return false if there is a hole
*/
bool containsSquare(const Position& position) const;
/** /**
* @return The length of the piece * @return The length of the piece

View File

@@ -11,7 +11,6 @@
#include <algorithm> #include <algorithm>
#include "../Common/Compression.h" #include "../Common/Compression.h"
#include "../Utils/AssetManager.h"
PiecesFiles::PiecesFiles() { PiecesFiles::PiecesFiles() {
@@ -51,11 +50,18 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyomin
std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength(); std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength();
buffer << infoByte; buffer << infoByte;
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 // write the positions of the piece
std::uint8_t positionByte; for (int i = 0; i < bytesNeeded; i++) {
for (const Position position : polyomino.getPositions()) { Polyomino::PolyominoData pData = polyominoData >> (i * 8);
positionByte = (position.x << 4) + position.y; unsigned long data =
buffer << positionByte; (pData & byteMask).to_ulong();
buffer << static_cast<std::uint8_t>(data);
} }
} }
@@ -92,13 +98,12 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
nextPieceBlockType(pieceBlock); nextPieceBlockType(pieceBlock);
} }
char convexMask = 0b1000'0000; constexpr char convexMask = 0b1000'0000;
char holeMask = 0b0100'0000; constexpr char holeMask = 0b0100'0000;
char lengthMask = 0b0011'1111; constexpr char lengthMask = 0b0011'1111;
char xMask = 0b1111'0000;
char yMask = 0b0000'1111;
std::uint8_t infoByte; std::uint8_t infoByte;
std::uint8_t positionByte;
int i = 0; int i = 0;
while (!buffer.IsFinished()) { while (!buffer.IsFinished()) {
// if (piecesFile.eof()) break; // if (piecesFile.eof()) break;
@@ -109,21 +114,22 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
bool hasHole = (infoByte & holeMask) >> 6; bool hasHole = (infoByte & holeMask) >> 6;
int length = (infoByte & lengthMask); int length = (infoByte & lengthMask);
const int bitsNeeded = length * length;
const int bytesNeeded = bitsNeeded / 8 + 1;
// read positions // read positions
std::set<Position> piecePositions; Polyomino::PolyominoData polyominoData{};
std::uint8_t positionByte;
for (int i = 0; i < polyominoSize; i++) { for (int j = 0; j < bytesNeeded; j++) {
buffer >> positionByte; buffer >> positionByte;
int x = ((unsigned char) positionByte & xMask) >> 4; Polyomino::PolyominoData tempByte(positionByte);
int y = positionByte & yMask; polyominoData |= (tempByte << (j * 8));
piecePositions.insert(Position{x, y});
} }
// create piece pieces.emplace_back(Polyomino(std::move(polyominoData), length), pieceBlock);
Piece readPiece(Polyomino(piecePositions, length), pieceBlock);
nextPieceBlockType(pieceBlock); nextPieceBlockType(pieceBlock);
pieces.push_back(readPiece);
if (isConvex) { if (isConvex) {
convexPieces.push_back(i); convexPieces.push_back(i);
} }
@@ -141,7 +147,7 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
} }
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const { bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
auto dataFolderPath = AssetManager::getResourcePath("data/pieces"); std::string dataFolderPath = "data/pieces/";
if (!std::filesystem::exists(dataFolderPath)) { if (!std::filesystem::exists(dataFolderPath)) {
std::filesystem::create_directories(dataFolderPath); std::filesystem::create_directories(dataFolderPath);
@@ -151,6 +157,6 @@ bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
return false; return false;
} }
filePath = dataFolderPath / (std::to_string(polyominoSize) + "minos.bin"); filePath = dataFolderPath + std::to_string(polyominoSize) + "minos.bin";
return true; return true;
} }

View File

@@ -8,9 +8,9 @@
#include <climits> #include <climits>
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <cassert>
Polyomino::Polyomino(std::set<Position>&& positions) : positions(0) {
Polyomino::Polyomino(const std::set<Position>& positions) {
int minX = INT_MAX; int minX = INT_MAX;
int maxX = INT_MIN; int maxX = INT_MIN;
int minY = INT_MAX; int minY = INT_MAX;
@@ -25,71 +25,64 @@ Polyomino::Polyomino(const 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
std::set<Position> newPositions; Polyomino newPolyomino({}, this->length);
for (Position position : positions) { for (Position position : positions) {
newPositions.insert(Position{position.x - minX, position.y - minY}); newPolyomino.insert(Position(position.x - minX, position.y - minY));
} }
this->positions = std::move(newPositions); this->positions = std::move(newPolyomino.positions);
} }
Polyomino::Polyomino(const std::set<Position>& positions, int length) : Polyomino::Polyomino(PolyominoData&& positions, std::int8_t length) :
positions(positions), positions(std::move(positions)),
length(length) { length(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->positions) { 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;
} }
std::set<Position> newPositions; Polyomino newPolyomino({}, this->length);
for (const Position position : this->positions) { for (const Position position : *this) {
newPositions.insert(Position{position.x - minX, position.y - minY}); newPolyomino.insert(Position(position.x - minX, position.y - minY));
} }
this->positions = std::move(newPositions); this->positions = std::move(newPolyomino.positions);
} }
void Polyomino::rotateCW() { void Polyomino::rotateCW() {
std::set<Position> newPositions; Polyomino temp({}, this->length);
for (const Position position : this->positions) { for (const Position position : *this) {
newPositions.insert(Position{position.y, (length - 1) - (position.x)}); temp.insert(Position(position.y, (length - 1) - (position.x)));
} }
this->positions = std::move(newPositions); this->positions = std::move(temp.positions);
} }
void Polyomino::rotate180() { void Polyomino::rotate180() {
std::set<Position> newPositions; Polyomino temp({}, this->length);
for (const Position position : this->positions) { for (const Position position : *this) {
newPositions.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(newPositions); this->positions = std::move(temp.positions);
} }
void Polyomino::rotateCCW() { void Polyomino::rotateCCW() {
std::set<Position> newPositions; Polyomino temp({}, this->length);
for (const Position position : this->positions) { for (const Position position : *this) {
newPositions.insert(Position{(length - 1) - (position.y), position.x}); temp.insert(Position((length - 1) - (position.y), position.x));
} }
this->positions = std::move(newPositions); this->positions = std::move(temp.positions);
} }
void Polyomino::goToSpawnPosition() { void Polyomino::goToSpawnPosition() {
// initialize array // initialize array
std::vector<std::vector<int>> linesCompleteness; std::vector<int> empty(this->length, 0);
linesCompleteness.reserve(4); std::vector<std::vector<int>> linesCompleteness(4, empty);
std::vector<int> empty;
for (int j = 0; j < this->length; j++) {
empty.push_back(0);
}
for (int i = 0; i < 4; i++) {
linesCompleteness.push_back(empty);
}
// calculates amount of squares per rows and columns // calculates amount of squares per rows and columns
for (const Position position : this->positions) { 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
@@ -155,20 +148,21 @@ void Polyomino::goToSpawnPosition() {
if (sideToBeOn % 2 == 1) { if (sideToBeOn % 2 == 1) {
std::swap(verticalEmptyLines, horizontalEmptyLines); std::swap(verticalEmptyLines, horizontalEmptyLines);
} }
int minX = INT_MAX; int minX = INT_MAX;
int minY = INT_MAX; int minY = INT_MAX;
for (const Position position : this->positions) { 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
std::set<Position> newPositions; Polyomino temp({}, this->length);
for (const Position position : positions) { for (const Position position : *this) {
newPositions.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(newPositions); this->positions = std::move(temp.positions);
} }
void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesCompleteness, bool currentFlattestSides[4], int& sideToBeOn, bool checkLeftSide) const { void Polyomino::checkForFlattestSide(const std::vector<std::vector<int>>& linesCompleteness, bool currentFlattestSides[4], int& sideToBeOn, bool checkLeftSide) const {
@@ -216,7 +210,7 @@ bool Polyomino::isConvex() const {
bool startedColumn = false; bool startedColumn = false;
bool completedColumn = false; bool completedColumn = false;
for (int i = 0; i < this->length; i++) { for (int i = 0; i < this->length; i++) {
if (this->positions.contains(Position{i, j})) { if (this->contains(Position(i, j))) {
if (completedLine) return false; if (completedLine) return false;
else startedLine = true; else startedLine = true;
} }
@@ -224,7 +218,7 @@ bool Polyomino::isConvex() const {
if (startedLine) completedLine = true; if (startedLine) completedLine = true;
} }
if (this->positions.contains(Position{j, i})) { if (this->contains(Position(j, i))) {
if (completedColumn) return false; if (completedColumn) return false;
else startedColumn = true; else startedColumn = true;
} }
@@ -238,31 +232,31 @@ 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->positions.contains(candidate) || emptyPositions.contains(candidate)) return; if (this->contains(candidate) || emptyPositions.contains(candidate)) return;
// if it's a new empty square, try its neighbors // if it's a new empty square, try its neighbors
emptyPositions.insert(candidate); emptyPositions.insert(candidate);
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y + 1}); tryToInsertPosition(emptyPositions, Position(candidate.x, candidate.y + 1));
tryToInsertPosition(emptyPositions, Position{candidate.x + 1, candidate.y}); tryToInsertPosition(emptyPositions, Position(candidate.x + 1, candidate.y));
tryToInsertPosition(emptyPositions, Position{candidate.x, candidate.y - 1}); tryToInsertPosition(emptyPositions, Position(candidate.x, candidate.y - 1));
tryToInsertPosition(emptyPositions, Position{candidate.x - 1, candidate.y}); tryToInsertPosition(emptyPositions, Position(candidate.x - 1, candidate.y));
} }
const std::set<Position>& Polyomino::getPositions() const { const Polyomino::PolyominoData& Polyomino::getPositionsData() const {
return this->positions; return this->positions;
} }
@@ -277,13 +271,19 @@ 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 (int y = this->length - 1; y >= 0; y--) { assert(other.positions.any() && "The other polyomino is empty !");
for (int x = 0; x < this->length; x++) {
bool hasThisPosition = this->positions.contains(Position{x, y}); static const PolyominoData longMask = 0xFFFFFFFFFFFFFFFF;
bool hasOtherPosition = other.positions.contains(Position{x, y}); const int longsNeeded = this->length * this->length / 64 + 1;
if (hasThisPosition != hasOtherPosition) return hasThisPosition;
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;
} }
} }
return false; return false;
} }
@@ -294,7 +294,7 @@ bool Polyomino::operator==(const Polyomino& other) const {
std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) { std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino) {
for (int y = polyomino.length - 1; y >= 0; y--) { for (int y = polyomino.length - 1; y >= 0; y--) {
for (int x = 0; x < polyomino.length; x++) { for (int x = 0; x < polyomino.length; x++) {
if (polyomino.positions.contains(Position{x, y})) { if (polyomino.contains(Position(x, y))) {
os << "*"; os << "*";
} }
else { else {
@@ -305,3 +305,31 @@ 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 {
return this->positions[getBitIndex(position)];
}
void Polyomino::insert(const Position& position) {
this->positions.set(getBitIndex(position), true);
}
void Polyomino::erase(const Position& position) {
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

@@ -5,26 +5,68 @@
#include <vector> #include <vector>
#include <set> #include <set>
#include <iostream> #include <iostream>
#include <array>
#include <bitset>
/** /**
* A mathematical object consisting of touching squares on a 2D grid * A mathematical object consisting of touching squares on a 2D grid
*/ */
class Polyomino { class Polyomino {
private:
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: public:
/** static const std::size_t MAX_LENGTH = 16;
* Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked using PolyominoData = std::bitset<MAX_LENGTH * MAX_LENGTH>;
*/
Polyomino(const std::set<Position>& positions);
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
std::int8_t length; // the size of the smallest square box in which the polyomino can fit on any rotation
public:
/** /**
* Creates a polyomino with the specified positions 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<Position>& positions, int length); Polyomino(PolyominoData&& positions, std::int8_t length);
/**
* Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked
*/
Polyomino(std::set<Position>&& positions);
/** /**
* Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0) * Translates the polyomino to the lowest unsigned values (lower row on y = 0, and left-most column on x = 0)
@@ -70,23 +112,33 @@ class Polyomino {
*/ */
bool hasHole() const; bool hasHole() const;
private : private:
/** /**
* 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:
/** /**
* @return The positions of the polyomino * @return The positions data of the polyomino
*/ */
const std::set<Position>& getPositions() const; const PolyominoData& getPositionsData() 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
*/ */
@@ -110,4 +162,23 @@ class Polyomino {
* @return A reference to the output stream * @return A reference to the output stream
*/ */
friend std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino); friend std::ostream& operator<<(std::ostream& os, const Polyomino& polyomino);
/**
* @return True if it contains the position
*/
bool contains(const Position& position) const;
/**
* @brief Insert a square at the position
*/
void insert(const Position& position);
/**
* @brief Removes a square at the position
*/
void erase(const Position& position);
ConstIterator begin() const;
ConstIterator end() const;
}; };

View File

@@ -1,14 +1,14 @@
#pragma once #pragma once
#include <iostream> #include <iostream>
#include <cstdint>
/** /**
* A position on a 2D grid * A position on a 2D grid
*/ */
struct Position { struct Position {
int x; // x position std::int8_t x; // x position
int y; // y position std::int8_t y; // y position
}; };
@@ -17,7 +17,7 @@ struct Position {
* @return The sums of the coordinates of both positions * @return The sums of the coordinates of both positions
*/ */
inline Position operator+(const Position& left, const Position& right) { inline Position operator+(const Position& left, const Position& right) {
return Position{left.x + right.x, left.y + right.y}; return Position(left.x + right.x, left.y + right.y);
} }
/** /**
@@ -34,7 +34,7 @@ inline Position& operator+=(Position& left, const Position& right) {
* @return The difference of the coordinate between the left and right position * @return The difference of the coordinate between the left and right position
*/ */
inline Position operator-(const Position& left, const Position& right) { inline Position operator-(const Position& left, const Position& right) {
return Position{left.x - right.x, left.y - right.y}; return Position(left.x - right.x, left.y - right.y);
} }
/** /**

View File

@@ -250,9 +250,9 @@ void TextApp::printGame(const Game& game) const {
for (int y = maxHeight; y >= 0; y--) { for (int y = maxHeight; y >= 0; y--) {
for (int x = 0; x < game.getBoard().getWidth(); x++) { for (int x = 0; x < game.getBoard().getWidth(); x++) {
/* BOARD PRINTING */ /* BOARD PRINTING */
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition())); bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getActivePiecePosition()));
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getGhostPiecePosition())); bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position(x, y) - game.getGhostPiecePosition()));
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y}); Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position(x, y));
if (isActivePieceHere || isGhostPieceHere) { if (isActivePieceHere || isGhostPieceHere) {
std::cout << getConsoleColorCode(block); std::cout << getConsoleColorCode(block);
@@ -294,7 +294,7 @@ void TextApp::printGame(const Game& game) const {
} }
else { else {
for (int i = 0; i < game.getHeldPiece()->getLength(); i++) { for (int i = 0; i < game.getHeldPiece()->getLength(); i++) {
if (game.getHeldPiece()->getPositions().contains(Position{i, printedPieceLineHeight})) { if (game.getHeldPiece()->getPositions().contains(Position(i, printedPieceLineHeight))) {
std::cout << getConsoleColorCode(game.getHeldPiece()->getBlockType()) << "*"; std::cout << getConsoleColorCode(game.getHeldPiece()->getBlockType()) << "*";
} }
else { else {
@@ -316,7 +316,7 @@ void TextApp::printGame(const Game& game) const {
} }
else { else {
for (int i = 0; i < game.getNextPieces().at(nextQueuePrintedPiece).getLength(); i++) { for (int i = 0; i < game.getNextPieces().at(nextQueuePrintedPiece).getLength(); i++) {
if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position{i, printedPieceLineHeight})) { if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position(i, printedPieceLineHeight))) {
std::cout << getConsoleColorCode(game.getNextPieces().at(nextQueuePrintedPiece).getBlockType()) << "*"; std::cout << getConsoleColorCode(game.getNextPieces().at(nextQueuePrintedPiece).getBlockType()) << "*";
} }
else { else {

View File

@@ -1,7 +1,6 @@
#include "../Pieces/Generator.h" #include "../Pieces/Generator.h"
#include "../Pieces/PiecesFiles.h" #include "../Pieces/PiecesFiles.h"
#include "TextApp.h" #include "TextApp.h"
#include "../Utils/AssetManager.h"
#include <chrono> #include <chrono>
#include <filesystem> #include <filesystem>
@@ -23,7 +22,7 @@ int main(int argc, char** argv) {
PiecesFiles pf; PiecesFiles pf;
bool warned = false; bool warned = false;
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) { for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
if (!std::filesystem::exists(AssetManager::getResourcePath("data/pieces/" + std::to_string(i) + "minos.bin"))) { if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
if (!warned) { if (!warned) {
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl; std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
warned = true; warned = true;

View File

@@ -1,12 +1,97 @@
#include "AssetManager.h" #include "./AssetManager.h"
namespace fs = std::filesystem; #include <map>
std::string AssetManager::getResourcePrefix() { static const unsigned char data_fonts_pressstart_prstart_ttf[] = {
char* env = std::getenv("JMINOS_DATA"); #include <data/fonts/pressstart/prstart.ttf.h>
return env ? env : std::string{}; };
static const unsigned char data_fonts_pressstart_prstartk_ttf[] = {
#include <data/fonts/pressstart/prstartk.ttf.h>
};
static const unsigned char data_images_keybinds_Harddrop_png[] = {
#include <data/images/keybinds/Harddrop.png.h>
};
static const unsigned char data_images_keybinds_Moveleft_png[] = {
#include <data/images/keybinds/Moveleft.png.h>
};
static const unsigned char data_images_keybinds_RotateCW_png[] = {
#include <data/images/keybinds/RotateCW.png.h>
};
static const unsigned char data_images_keybinds_RotateCCW_png[] = {
#include <data/images/keybinds/RotateCCW.png.h>
};
static const unsigned char data_images_keybinds_Softdrop_png[] = {
#include <data/images/keybinds/Softdrop.png.h>
};
static const unsigned char data_images_keybinds_Moveright_png[] = {
#include <data/images/keybinds/Moveright.png.h>
};
static const unsigned char data_images_keybinds_Rotate180_png[] = {
#include <data/images/keybinds/Rotate180.png.h>
};
static const unsigned char data_images_keybinds_Hold_png[] = {
#include <data/images/keybinds/Hold.png.h>
};
static const unsigned char data_images_keybinds_Rotate0_png[] = {
#include <data/images/keybinds/Rotate0.png.h>
};
static const unsigned char data_images_keybinds_Retry_png[] = {
#include <data/images/keybinds/Retry.png.h>
};
static const unsigned char data_images_keybinds_Pause_png[] = {
#include <data/images/keybinds/Pause.png.h>
};
static const Asset assets[] = {
{data_fonts_pressstart_prstart_ttf, sizeof(data_fonts_pressstart_prstart_ttf)},
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
{data_images_keybinds_Harddrop_png, sizeof(data_images_keybinds_Harddrop_png)},
{data_images_keybinds_Moveleft_png, sizeof(data_images_keybinds_Moveleft_png)},
{data_images_keybinds_RotateCW_png, sizeof(data_images_keybinds_RotateCW_png)},
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
{data_images_keybinds_Softdrop_png, sizeof(data_images_keybinds_Softdrop_png)},
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
{data_images_keybinds_Hold_png, sizeof(data_images_keybinds_Hold_png)},
{data_images_keybinds_Rotate0_png, sizeof(data_images_keybinds_Rotate0_png)},
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
{data_images_keybinds_Pause_png, sizeof(data_images_keybinds_Pause_png)},
};
static const std::map<std::string, AssetName> assetMap = {
{"data/fonts/pressstart/prstart.ttf", AssetName::data_fonts_pressstart_prstart_ttf},
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
{"data/images/keybinds/Harddrop.png", AssetName::data_images_keybinds_Harddrop_png},
{"data/images/keybinds/Moveleft.png", AssetName::data_images_keybinds_Moveleft_png},
{"data/images/keybinds/RotateCW.png", AssetName::data_images_keybinds_RotateCW_png},
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
{"data/images/keybinds/Softdrop.png", AssetName::data_images_keybinds_Softdrop_png},
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
{"data/images/keybinds/Hold.png", AssetName::data_images_keybinds_Hold_png},
{"data/images/keybinds/Rotate0.png", AssetName::data_images_keybinds_Rotate0_png},
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
{"data/images/keybinds/Pause.png", AssetName::data_images_keybinds_Pause_png},
};
const Asset& getResource(AssetName fileName) {
return assets[static_cast<std::size_t>(fileName)];
} }
fs::path AssetManager::getResourcePath(const std::string& resource) { const Asset& getResource(const std::string& fileName) {
return fs::path{getResourcePrefix()} / resource; return getResource(assetMap.at(fileName));
} }

View File

@@ -2,10 +2,29 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <filesystem>
class AssetManager { struct Asset {
public: const unsigned char* data;
static std::string getResourcePrefix(); std::size_t size;
static std::filesystem::path getResourcePath(const std::string& resource); };
};
enum class AssetName {
data_fonts_pressstart_prstart_ttf,
data_fonts_pressstart_prstartk_ttf,
data_images_keybinds_Harddrop_png,
data_images_keybinds_Moveleft_png,
data_images_keybinds_RotateCW_png,
data_images_keybinds_RotateCCW_png,
data_images_keybinds_Softdrop_png,
data_images_keybinds_Moveright_png,
data_images_keybinds_Rotate180_png,
data_images_keybinds_Hold_png,
data_images_keybinds_Rotate0_png,
data_images_keybinds_Retry_png,
data_images_keybinds_Pause_png,
};
const Asset& getResource(AssetName fileName);
const Asset& getResource(const std::string& fileName);

View File

@@ -1,11 +1,8 @@
add_rules("mode.debug", "mode.release") add_rules("mode.debug", "mode.release")
set_version("1.0.0") includes("xmake/bin2c.lua")
set_project("org.zulianc.jminos")
includes("@builtin/xpack") add_requires("sfml 3.0.0", "zlib")
add_requires("sfml 3.0.0", "zlib", {system = false})
set_languages("c++20") set_languages("c++20")
@@ -13,7 +10,7 @@ set_rundir(".")
target("core") target("core")
set_kind("$(kind)") set_kind("$(kind)")
add_files("src/Pieces/*.cpp", "src/Core/*.cpp", "src/Common/*.cpp", "src/Utils/*.cpp") add_files("src/Pieces/*.cpp", "src/Core/*.cpp", "src/Common/*.cpp")
add_packages("zlib") add_packages("zlib")
target("text") target("text")
@@ -30,11 +27,16 @@ target("bmark")
target("graph") target("graph")
set_default(true) set_default(true)
add_rules("bin2c", {
extensions = {".png", ".ttf"},
outputSource = {"src/Utils/AssetManager.cpp"},
outputHeader = {"src/Utils/AssetManager.h"}
})
set_kind("binary") set_kind("binary")
add_files("./src/GraphicalUI/**.cpp") add_files("./src/GraphicalUI/**.cpp")
add_files("data/fonts/**.ttf", "data/images/**.png")
add_deps("core") add_deps("core")
add_packages("sfml") add_packages("sfml")
add_installfiles("(data/**)")
if is_mode("debug") then if is_mode("debug") then
add_defines("DEBUG") add_defines("DEBUG")
@@ -44,20 +46,6 @@ if is_plat("mingw") then
add_ldflags("-static-libstdc++", "-static") add_ldflags("-static-libstdc++", "-static")
end end
xpack("jminos")
set_formats("flatpak")
set_extension("")
set_title("jminos")
set_author("zulianc")
set_description("A test installer.")
set_homepage("https://git.ale-pri.com/TetrisNerd/jminos")
set_company("org.zulianc")
on_package(function (package)
os.cd("flatpak")
os.exec("flatpak-builder --force-clean --user --install-deps-from=flathub --repo=repo --install builddir org.zulianc.jminos.yml")
os.exec("flatpak build-bundle repo jminos.flatpak org.zulianc.jminos --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo")
os.mv("jminos.flatpak", package:outputdir())
end)
-- --
-- If you want to known more usage about xmake, please see https://xmake.io -- If you want to known more usage about xmake, please see https://xmake.io