9 Commits

Author SHA1 Message Date
d50714ef8c working asset manager !
All checks were successful
Linux arm64 / Build (push) Successful in 1m58s
2025-05-27 21:29:56 +02:00
3d1feb6295 start asset manager
Some checks failed
Linux arm64 / Build (push) Failing after 1m58s
2025-05-27 17:09:36 +02:00
7a96136631 added xmake benchmark target
All checks were successful
Linux arm64 / Build (push) Successful in 1m52s
2025-05-27 14:52:34 +02:00
fc6348cebc added benchmarking and screenshots
All checks were successful
Linux arm64 / Build (push) Successful in 1m58s
2025-05-27 11:38:15 +02:00
0bb25d4628 benchmarking 1/2
All checks were successful
Linux arm64 / Build (push) Successful in 1m54s
2025-05-26 19:32:31 +02:00
69c07abcec update readme
All checks were successful
Linux arm64 / Build (push) Successful in 1m51s
2025-05-24 14:25:50 +02:00
774aa1422f Merge branch 'main' of https://git.ale-pri.com/TetrisNerd/jminos
All checks were successful
Linux arm64 / Build (push) Successful in 1m56s
2025-05-24 14:05:42 +02:00
4dfeda0957 update info menu 2025-05-24 14:05:37 +02:00
05bb79d4a9 Add CI (#3)
All checks were successful
Linux arm64 / Build (push) Successful in 1m56s
Reviewed-on: #3
Co-authored-by: Persson-dev <sim16.prib@gmail.com>
Co-committed-by: Persson-dev <sim16.prib@gmail.com>
2025-05-24 10:59:12 +00:00
22 changed files with 539 additions and 150 deletions

36
.github/workflows/ubuntu.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Linux arm64
run-name: Build And Test
on: [push]
env:
XMAKE_ROOT: y
jobs:
Build:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Prepare XMake
uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: latest
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'ubuntu'
- name: Install SFML
run: |
apt update
apt install -y libsfml-dev
- name: XMake config
run: xmake f -p linux -y
- name: Build
run: xmake
- name: Test
run: xmake test

2
.gitignore vendored
View File

@@ -10,7 +10,7 @@ build/
# personnal documentation # personnal documentation
doc/*.txt doc/*.txt
doc/*.violet.html doc/diagrams/*.violet.html
doc/mockups/* doc/mockups/*
# data files # data files

View File

@@ -1,24 +1,30 @@
# jminos # jminos
Modern stacker game with every polyominos from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)! Modern stacker game with every polyominoes from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)!
## Download ## Download
// TODO when the game is finished // // TODO when the game is finished //
This game has been tested on and built on Windows 11 and WSL2 Ubuntu only. This game has been tested on and built for Windows 11 and WSL2 Ubuntu.
If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build). If your OS isn't compactible with either of theses two, you can try [manually building the project](#manual-build).
## How to play ## How to play
Choose which pieces you want to play with and stack them up until you either win or top out!
Make full lines to make them dissapear.
Use the different spins to kick the pieces in spot you couldn't imagine were attaignable!
Each gamemode has its own objective, but you can play as you wish.
You can see and change in-game keybinds in the **SETTINGS** section of the main menu! You can see and change in-game keybinds in the **SETTINGS** section of the main menu!
All of in-menu navigation is done with the **arrow keys**, the **Enter key** and the **Escape key**. Theses are unchangeable keybinds. All of in-menu navigation is done with the **arrow keys**, the **Enter key** and the **Escape key**. Theses are unchangeable keybinds.
You will find more infos about the Rotation System, the scoring system, or the different pieces type in the **INFO** section of the main menu. You will find more infos about the Rotation System, the scoring system, or the different pieces type in the **INFO** section of the main menu.
If you want to know more details about the generation of polyominoes, [check the documentation](/doc/)!
## Features ## Features
- Every polyominoes from size 1 to 15, selectable as you wish, with customizable propotionnality for each size! - Every polyominoes up to pentedecaminoes!
- 7bag with proportionnality for each polyomino size!
- Customizable board size!
- Customizable keybinds! - Customizable keybinds!
- 0° rotations! - 0° rotations!
- AutoRS as the Rotation System! - AutoRS as the Rotation System!
@@ -35,7 +41,17 @@ If you want to know more details about the generation of polyominoes, [check the
### Screenshots ### Screenshots
// TODO when the game is finished // Pentedecamino jumpscare
![](./doc/readme/big_piece.png)
Pieces select screen
![](./doc/readme/pieces_selection.png)
AutoRS demonstration
![](./doc/readme/rotations.gif)
0° spins demonstration
![](./doc/readme/rotation_0.gif)
## Manual build ## Manual build
@@ -62,12 +78,42 @@ To switch between debug and release mode:
``xmake f -m release`` ``xmake f -m release``
If for some reasons you wanna run the command line version (not updated): If for some reasons you wanna run the command line version (not updated):
``xmake build text``
``xmake run text`` ``xmake run text``
### Package the project ### Package the project
// TODO when the game is finished // // TODO when the game is finished //
## Benchmarks
### One-sided n-polyominoes
| n | Number | Generation | File storing | File retrieving | File size |
| - | - | - | - | - | - |
| 1 | 1 | 0s 0.005622ms | 0s 0.750955ms | 0s 0.014016ms | 2 bytes |
| 2 | 1 | 0s 0.005758ms | 0s 0.181323ms | 0s 0.012256ms | 3 bytes |
| 3 | 2 | 0s 0.017525ms | 0s 0.054497ms | 0s 0.006431ms | 8 bytes |
| 4 | 7 | 0s 0.050554ms | 0s 0.067617ms | 0s 0.010984ms | 35 bytes |
| 5 | 18 | 0s 0.191971ms | 0s 0.109905ms | 0s 0.021234ms | 108 bytes |
| 6 | 60 | 0s 0.651757ms | 0s 0.327465ms | 0s 0.04558ms | 420 bytes |
| 7 | 196 | 0s 3.38847ms | 0s 1.94434ms | 0s 0.258777ms | 1568 bytes |
| 8 | 704 | 0s 22.7411ms | 0s 10.0103ms | 0s 1.34813ms | 6336 bytes |
| 9 | 2500 | 0s 66.2949ms | 0s 20.6137ms | 0s 2.56374ms | 25000 bytes |
| 10 | 9189 | 0s 194.764ms | 0s 84.5884ms | 0s 9.64467ms | 101079 bytes |
| 11 | 33896 | 0s 759.182ms | 0s 378.494ms | 0s 44.1424ms | 406752 bytes |
| 12 | 126759 | 2s 709.277ms | 1s 530.34ms | 0s 155ms | 1647867 bytes |
| 13 | 476270 | 10s 668.308ms | 7s 395.512ms | 0s 765.601ms | 6667780 bytes |
| 14 | 1802312 | 45s 606.597ms | 32s 28.7977ms | 2s 919.653ms | 27034680 bytes |
| 15 | ~6M | ~5mn | ~5mn | ~10s | ~100 MB |
_File storing includes normalizing and sorting all polyominoes before writing them to the file._
If you want to know more details about the generation and storage of polyominoes, [check the documentation](/doc/)!
Run it yourself by typing:
``xmake build benchmark``
``xmake run benchmark``
## Credits ## Credits
Library used: [SFML 3](https://www.sfml-dev.org/). Library used: [SFML 3](https://www.sfml-dev.org/).

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

BIN
doc/readme/big_piece.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
doc/readme/rotation_0.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 KiB

BIN
doc/readme/rotations.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 779 KiB

View File

@@ -0,0 +1,12 @@
#include "AppMenu.h"
#include "../../Utils/AssetManager.h"
AppMenu::AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : menuStack(menuStack),
settings(settings),
renderWindow(renderWindow)
{
const Asset& file = getResource(AssetName::data_fonts_pressstart_prstartk_ttf);
this->pressStartFont = sf::Font(file.data, file.size);
}

View File

@@ -21,16 +21,10 @@ class AppMenu {
bool enterReleased = false; bool enterReleased = false;
bool escPressed = false; bool escPressed = false;
bool escReleased = false; bool escReleased = false;
sf::Font pressStartFont = sf::Font("data/fonts/pressstart/prstartk.ttf"); sf::Font pressStartFont;
public: public:
AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
menuStack(menuStack),
settings(settings),
renderWindow(renderWindow)
{
}
virtual void computeFrame() = 0; virtual void computeFrame() = 0;

View File

@@ -10,12 +10,13 @@
InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
AppMenu(menuStack, settings, renderWindow), AppMenu(menuStack, settings, renderWindow),
playerCursor({4}), playerCursor({INFO_SECTIONS_COUNT}),
sectionsName( sectionsName(
"< ABOUT >", "< ABOUT >",
"< PIECES TYPES >",
"< 0 DEGREES ROTATIONS >",
"< ROTATION SYSTEM >", "< ROTATION SYSTEM >",
"< SCORING >", "< SCORING >"
"< 0 DEGREES ROTATIONS >"
), ),
sectionsContent( sectionsContent(
"This game is written in C++,\n" "This game is written in C++,\n"
@@ -27,6 +28,25 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
"to them in any ways.\n" "to them in any ways.\n"
"Current version: beta.", "Current version: beta.",
"There is multiple pieces type in\n"
"the selection screen. Use theses\n"
"categories for size of at least 7.\n"
"Convex, Holeless and Others are\n"
"all mutually exclusive.\n"
"Others have holes inside them, and\n"
"Convex are presumably easier to\n"
"play with than Holeless.",
"This games introduces 0 degrees\n"
"rotations, which work by simpling\n"
"moving the piece down and kicking\n"
"it as is, allowing for new kinds\n"
"of kicks.\n"
"As a leniency mechanic, when a\n"
"piece spawns it will automatically\n"
"try a 0 degrees rotations if it\n"
"spawned inside a wall.",
"This game uses its own\n" "This game uses its own\n"
"Rotation Sytem, called AutoRS.\n" "Rotation Sytem, called AutoRS.\n"
"The rotation center is always the\n" "The rotation center is always the\n"
@@ -46,17 +66,7 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
"and doubles the score gained.\n" "and doubles the score gained.\n"
"A spin is detected when the piece is\n" "A spin is detected when the piece is\n"
"locked in place, a mini-spin simply\n" "locked in place, a mini-spin simply\n"
"when the last move was a kick.", "when the last move was a kick."
"This games introduces 0 degrees\n"
"rotations, which work by simpling\n"
"moving the piece down and kicking\n"
"it as is, allowing for new kinds\n"
"of kicks.\n"
"As a leniency mechanic, when a\n"
"piece spawns it will automatically\n"
"try a 0 degrees rotations if it\n"
"spawned inside a wall."
) { ) {
} }

View File

@@ -7,12 +7,14 @@
#include <memory> #include <memory>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
static const int INFO_SECTIONS_COUNT = 5;
class InfoAppMenu : public AppMenu { class InfoAppMenu : public AppMenu {
private: private:
PlayerCursor playerCursor; PlayerCursor playerCursor;
sf::String sectionsName[4]; sf::String sectionsName[INFO_SECTIONS_COUNT];
sf::String sectionsContent[4]; sf::String sectionsContent[INFO_SECTIONS_COUNT];
public: public:
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow); InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);

View File

@@ -2,6 +2,7 @@
#include "AppMenu.h" #include "AppMenu.h"
#include "../PlayerCursor.h" #include "../PlayerCursor.h"
#include "../../Utils/AssetManager.h"
#include <stack> #include <stack>
#include <memory> #include <memory>
@@ -22,8 +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(" "), "");
std::filesystem::path texturePath("data/images/keybinds/" + textureName + ".png"); const Asset& textureData = getResource("data/images/keybinds/" + textureName + ".png");
this->iconTextures[action] = sf::Texture(texturePath, false, {{0, 0}, {16, 16}});
this->iconTextures[action] = sf::Texture(textureData.data, textureData.size, false, {{0, 0}, {16, 16}});
} }
} }

View File

@@ -70,6 +70,7 @@ int main() {
void resetSettingsFile() { void resetSettingsFile() {
std::filesystem::create_directories("data/config");
std::ofstream settingsFile("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;
@@ -137,6 +138,7 @@ void resetSettingsFile() {
void resetKeybindFile(int layout) { void resetKeybindFile(int layout) {
if (layout < 0 || layout > 4) return; if (layout < 0 || layout > 4) return;
std::filesystem::create_directories("data/config/keybinds/layout");
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary); std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary);
std::map<Action, sfKey> keybinds; std::map<Action, sfKey> keybinds;

View File

@@ -10,6 +10,8 @@
#include <filesystem> #include <filesystem>
#include <algorithm> #include <algorithm>
namespace fs = std::filesystem;
PiecesFiles::PiecesFiles() { PiecesFiles::PiecesFiles() {
} }
@@ -25,22 +27,35 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
} }
Generator generator; Generator generator;
std::vector<Polyomino> nMinos = generator.generatePolyominoes(polyominoSize); std::vector<Polyomino> polyominoes = generator.generatePolyominoes(polyominoSize);
return this->savePieces(polyominoSize, polyominoes);
}
bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) const {
std::string filePath;
if (!this->getFilePath(polyominoSize, filePath)) {
return false;
}
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
if (!piecesFile.good()) {
return false;
}
// sorting the polyominoes is done after setting spawn position to ensure the order is always the same // sorting the polyominoes is done after setting spawn position to ensure the order is always the same
for (Polyomino& nMino : nMinos) { for (Polyomino& nMino : polyominoes) {
nMino.goToSpawnPosition(); nMino.goToSpawnPosition();
} }
std::sort(nMinos.begin(), nMinos.end()); std::sort(polyominoes.begin(), polyominoes.end());
for (const Polyomino& nMino : nMinos) { for (const Polyomino& polyomino : polyominoes) {
// write the characteristics of the piece // write the characteristics of the piece
char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + nMino.getLength(); char infoByte = (polyomino.isConvex() << 7) + (polyomino.hasHole() << 6) + polyomino.getLength();
piecesFile.write(&infoByte, 1); piecesFile.write(&infoByte, 1);
// write the positions of the piece // write the positions of the piece
char positionByte; char positionByte;
for (Position position : nMino.getPositions()) { for (const Position position : polyomino.getPositions()) {
positionByte = (position.x << 4) + position.y; positionByte = (position.x << 4) + position.y;
piecesFile.write(&positionByte, 1); piecesFile.write(&positionByte, 1);
} }
@@ -119,7 +134,12 @@ 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 {
std::string dataFolderPath = "data/pieces/"; std::string dataFolderPath = "data/pieces/";
if (!std::filesystem::is_directory(dataFolderPath)) {
if (!fs::exists(dataFolderPath)) {
fs::create_directories(dataFolderPath);
}
if (!fs::is_directory(dataFolderPath)) {
return false; return false;
} }

View File

@@ -22,13 +22,18 @@ class PiecesFiles {
*/ */
bool savePieces(int polyominoSize) const; bool savePieces(int polyominoSize) const;
/**
* Generate a file containing all the pieces of the specified size, assuming they have been correctly generated
* @return If the file could be created
*/
bool savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) const;
/** /**
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched * 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 * @return If the file was found
*/ */
bool loadPieces(int polyominoSize, 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 * 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 * @return If the data folder was found

View File

@@ -3,45 +3,60 @@
#include "TextApp.h" #include "TextApp.h"
#include <chrono> #include <chrono>
#include <filesystem>
#include <cmath>
static const int MAXIMUM_PIECES_SIZE = 10;
static const int BENCHMARK_PIECES_SIZE = 15;
void testGeneratorForAllSizes(int amount); void testGeneratorForAllSizes(int max_size);
void testGeneratorForOneSize(int size); void testGeneratorForOneSize(int size);
void testGeneratorByprintingAllNminos(int n); void printPiecesByTypesForOneSize(int size);
void testStoringAndRetrievingPieces(int size); void readStatsFromFilesForAllSizes(int max_size);
void generateFilesForAllSizes(int amount);
void generateFilesForOneSize(int size); void benchmarking(int min_size, int max_size);
void loadFromFilesForOneSize(int size);
void readStatsFromFilesForAllSizes(int amount);
int main(int argc, char** argv) { int main(int argc, char** argv) {
std::srand(std::time(NULL)); std::srand(std::time(NULL));
// dev: generate files if it hasn't been done before, UI will NOT generate the files #ifdef BENCHMARK
//generateFilesForAllSizes(10); benchmarking(1, BENCHMARK_PIECES_SIZE);
#else
// dev: generate files if it hasn't been done before, UI will NOT generate the files
//generateFilesForAllSizes(10);
TextApp UI; PiecesFiles pf;
UI.run(); for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
pf.savePieces(i);
}
}
TextApp UI;
UI.run();
#endif
return 0; return 0;
} }
void testGeneratorForAllSizes(int amount) { void testGeneratorForAllSizes(int max_size) {
using std::chrono::high_resolution_clock; using std::chrono::high_resolution_clock;
using std::chrono::duration_cast; using std::chrono::duration_cast;
using std::chrono::duration; using std::chrono::duration;
using std::chrono::milliseconds; using std::chrono::milliseconds;
Generator generator; Generator generator;
for (int i = 1; i <= amount; i++) { for (int i = 1; i <= max_size; i++) {
auto t1 = high_resolution_clock::now(); auto t1 = high_resolution_clock::now();
std::vector<Polyomino> n_minos = generator.generatePolyominoes(i); std::vector<Polyomino> n_minos = generator.generatePolyominoes(i);
auto t2 = high_resolution_clock::now(); auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1; duration<double, std::milli> ms_double = t2 - t1;
std::cout << "generated " << n_minos.size() << " polyominoes of size " << i << " in " << ms_double.count() << "ms" << std::endl; std::cout << "Generated " << n_minos.size() << " polyominoes of size " << i << " in " << ms_double.count() << "ms" << std::endl;
} }
} }
@@ -63,24 +78,8 @@ void testGeneratorForOneSize(int size) {
} }
} }
void testGeneratorByprintingAllNminos(int n) { void printPiecesByTypesForOneSize(int size) {
Generator generator;
std::vector<Polyomino> n_minos = generator.generatePolyominoes(n);
for (Polyomino& n_mino : n_minos) {
n_mino.goToSpawnPosition();
}
std::sort(n_minos.begin(), n_minos.end());
for (Polyomino& n_mino : n_minos) {
std::cout << n_mino << std::endl;
}
}
void testStoringAndRetrievingPieces(int size) {
PiecesFiles piecesFiles; PiecesFiles piecesFiles;
piecesFiles.savePieces(size);
std::vector<Piece> pieces; std::vector<Piece> pieces;
std::vector<int> convexPieces; std::vector<int> convexPieces;
@@ -104,79 +103,9 @@ void testStoringAndRetrievingPieces(int size) {
} }
} }
void generateFilesForAllSizes(int amount) { void readStatsFromFilesForAllSizes(int max_size) {
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
PiecesFiles piecesFiles; PiecesFiles piecesFiles;
for (int i = 1; i <= max_size; i++) {
for (int i = 1; i <= amount; i++) {
auto t1 = high_resolution_clock::now();
piecesFiles.savePieces(i);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << "Generated pieces files for size " << i << " in " << ms_double.count() << "ms" << std::endl;
}
std::vector<Piece> pieces;
std::vector<int> convexPieces;
std::vector<int> holelessPieces;
std::vector<int> otherPieces;
for (int i = 1; i <= amount; i++) {
auto t1 = high_resolution_clock::now();
piecesFiles.loadPieces(i, pieces, convexPieces, holelessPieces, otherPieces);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << "Read pieces from files for size " << i << " in " << ms_double.count() << "ms" << std::endl;
}
}
void generateFilesForOneSize(int size) {
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
PiecesFiles piecesFiles;
std::cout << "Generating " << size << "-minos files" << std::endl;
for (int i = 0; i < 10; i++) {
auto t1 = high_resolution_clock::now();
piecesFiles.savePieces(size);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << ms_double.count() << "ms" << std::endl;
}
}
void loadFromFilesForOneSize(int size) {
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
PiecesFiles piecesFiles;
std::vector<Piece> pieces;
std::vector<int> convexPieces;
std::vector<int> holelessPieces;
std::vector<int> otherPieces;
std::cout << "Loading " << size << "-minos from files" << std::endl;
for (int i = 0; i < 10; i++) {
auto t1 = high_resolution_clock::now();
piecesFiles.loadPieces(size, pieces, convexPieces, holelessPieces, otherPieces);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << ms_double.count() << "ms" << std::endl;
}
}
void readStatsFromFilesForAllSizes(int amount) {
PiecesFiles piecesFiles;
for (int i = 1; i <= amount; i++) {
std::vector<Piece> pieces; std::vector<Piece> pieces;
std::vector<int> convexPieces; std::vector<int> convexPieces;
std::vector<int> holelessPieces; std::vector<int> holelessPieces;
@@ -189,3 +118,66 @@ void readStatsFromFilesForAllSizes(int amount) {
std::cout << "Others " << i << "-minos : " << otherPieces.size() << std::endl; std::cout << "Others " << i << "-minos : " << otherPieces.size() << std::endl;
} }
} }
void benchmarking(int min_size, int max_size) {
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::duration;
using std::chrono::milliseconds;
std::cout << "| n | Number | Generation | File storing | File retrieving | File size |" << std::endl;
std::cout << "| - | - | - | - | - | - |" << std::endl;
Generator gen;
PiecesFiles pf;
for (int i = min_size; i <= max_size; i++) {
std::cout << "| " << i;
auto t1 = high_resolution_clock::now();
std::vector<Polyomino> polyominoes = gen.generatePolyominoes(i);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << " | " << polyominoes.size();
std::flush(std::cout);
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
std::flush(std::cout);
t1 = high_resolution_clock::now();
pf.savePieces(i, polyominoes);
t2 = high_resolution_clock::now();
ms_double = t2 - t1;
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
std::flush(std::cout);
polyominoes.clear();
polyominoes.shrink_to_fit();
std::vector<Piece> pieces;
std::vector<int> convexPieces;
std::vector<int> holelessPieces;
std::vector<int> otherPieces;
t1 = high_resolution_clock::now();
pf.loadPieces(i, pieces, convexPieces, holelessPieces, otherPieces);
t2 = high_resolution_clock::now();
ms_double = t2 - t1;
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
std::flush(std::cout);
pieces.clear();
pieces.shrink_to_fit();
convexPieces.clear();
convexPieces.shrink_to_fit();
holelessPieces.clear();
holelessPieces.shrink_to_fit();
otherPieces.clear();
otherPieces.shrink_to_fit();
std::string filePath;
pf.getFilePath(i, filePath);
int fileSize = std::filesystem::file_size(filePath);
std::cout << " | " << fileSize << " bytes |" << std::endl;
std::flush(std::cout);
}
}

View File

@@ -0,0 +1,97 @@
#include "./AssetManager.h"
#include <map>
static const unsigned char data_fonts_pressstart_prstartk_ttf[] = {
#include <data/fonts/pressstart/prstartk.ttf.h>
};
static const unsigned char data_fonts_pressstart_prstart_ttf[] = {
#include <data/fonts/pressstart/prstart.ttf.h>
};
static const unsigned char data_images_keybinds_Rotate0_png[] = {
#include <data/images/keybinds/Rotate0.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_RotateCW_png[] = {
#include <data/images/keybinds/RotateCW.png.h>
};
static const unsigned char data_images_keybinds_Pause_png[] = {
#include <data/images/keybinds/Pause.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_Softdrop_png[] = {
#include <data/images/keybinds/Softdrop.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_Moveleft_png[] = {
#include <data/images/keybinds/Moveleft.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_Retry_png[] = {
#include <data/images/keybinds/Retry.png.h>
};
static const unsigned char data_images_keybinds_Harddrop_png[] = {
#include <data/images/keybinds/Harddrop.png.h>
};
static const Asset assets[] = {
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
{data_fonts_pressstart_prstart_ttf, sizeof(data_fonts_pressstart_prstart_ttf)},
{data_images_keybinds_Rotate0_png, sizeof(data_images_keybinds_Rotate0_png)},
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
{data_images_keybinds_RotateCW_png, sizeof(data_images_keybinds_RotateCW_png)},
{data_images_keybinds_Pause_png, sizeof(data_images_keybinds_Pause_png)},
{data_images_keybinds_Hold_png, sizeof(data_images_keybinds_Hold_png)},
{data_images_keybinds_Softdrop_png, sizeof(data_images_keybinds_Softdrop_png)},
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
{data_images_keybinds_Moveleft_png, sizeof(data_images_keybinds_Moveleft_png)},
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
{data_images_keybinds_Harddrop_png, sizeof(data_images_keybinds_Harddrop_png)},
};
static const std::map<std::string, AssetName> assetMap = {
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
{"data/fonts/pressstart/prstart.ttf", AssetName::data_fonts_pressstart_prstart_ttf},
{"data/images/keybinds/Rotate0.png", AssetName::data_images_keybinds_Rotate0_png},
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
{"data/images/keybinds/RotateCW.png", AssetName::data_images_keybinds_RotateCW_png},
{"data/images/keybinds/Pause.png", AssetName::data_images_keybinds_Pause_png},
{"data/images/keybinds/Hold.png", AssetName::data_images_keybinds_Hold_png},
{"data/images/keybinds/Softdrop.png", AssetName::data_images_keybinds_Softdrop_png},
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
{"data/images/keybinds/Moveleft.png", AssetName::data_images_keybinds_Moveleft_png},
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
{"data/images/keybinds/Harddrop.png", AssetName::data_images_keybinds_Harddrop_png},
};
const Asset& getResource(AssetName fileName) {
return assets[static_cast<std::size_t>(fileName)];
}
const Asset& getResource(const std::string& fileName) {
return getResource(assetMap.at(fileName));
}

30
src/Utils/AssetManager.h Normal file
View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <string>
struct Asset {
const unsigned char* data;
std::size_t size;
};
enum class AssetName {
data_fonts_pressstart_prstartk_ttf,
data_fonts_pressstart_prstart_ttf,
data_images_keybinds_Rotate0_png,
data_images_keybinds_Moveright_png,
data_images_keybinds_RotateCW_png,
data_images_keybinds_Pause_png,
data_images_keybinds_Hold_png,
data_images_keybinds_Softdrop_png,
data_images_keybinds_RotateCCW_png,
data_images_keybinds_Moveleft_png,
data_images_keybinds_Rotate180_png,
data_images_keybinds_Retry_png,
data_images_keybinds_Harddrop_png,
};
const Asset& getResource(AssetName fileName);
const Asset& getResource(const std::string& fileName);

View File

@@ -1,5 +1,7 @@
add_rules("mode.debug", "mode.release") add_rules("mode.debug", "mode.release")
includes("xmake/bin2c.lua")
add_requires("sfml 3.0.0") add_requires("sfml 3.0.0")
set_languages("c++20") set_languages("c++20")
@@ -11,18 +13,33 @@ target("core")
add_files("src/Pieces/*.cpp") add_files("src/Pieces/*.cpp")
add_files("src/Core/*.cpp") add_files("src/Core/*.cpp")
target("text")
set_kind("binary")
set_default(false)
add_files("./src/TextUI/*.cpp")
add_deps("core")
target("graph") target("graph")
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")
target("text")
set_default(false)
set_kind("binary")
add_files("./src/TextUI/*.cpp")
add_deps("core")
target("benchmark")
set_default(false)
set_kind("binary")
add_files("./src/TextUI/*.cpp")
add_deps("core")
add_defines("BENCHMARK")
-- --
-- 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
-- --

124
xmake/bin2c.lua Normal file
View File

@@ -0,0 +1,124 @@
rule("bin2c")
set_extensions(".bin")
on_load(function (target)
local headerdir = path.join(target:autogendir(), "rules", "bin2c")
local outputSource = table.unpack(target:extraconf("rules", "bin2c", "outputSource"))
if not os.isdir(headerdir) then
os.mkdir(headerdir)
end
target:add("includedirs", headerdir)
target:add("files", outputSource)
end)
before_buildcmd_files(function (target, batchcmds, sourcebatch, opt)
local outputHeader = table.unpack(target:extraconf("rules", "bin2c", "outputHeader"))
local outputHeaderEnumContent = ""
for _, filePath in ipairs(sourcebatch.sourcefiles) do
local escapedName = string.gsub(filePath, "[/|.]", "_")
outputHeaderEnumContent = outputHeaderEnumContent .. "\t" .. escapedName .. ",\n"
end
local outputHeaderContent = string.format([[
#pragma once
#include <cstdint>
#include <string>
struct Asset {
const unsigned char* data;
std::size_t size;
};
enum class AssetName {
%s
};
const Asset& getResource(AssetName fileName);
const Asset& getResource(const std::string& fileName);
]], outputHeaderEnumContent)
local outputSource = table.unpack(target:extraconf("rules", "bin2c", "outputSource"))
local relativePath = path.join(path.relative(path.directory(outputHeader), path.directory(outputSource)), path.filename(outputHeader))
local outputSourceContent = string.format([[
#include "%s"
#include <map>
]], relativePath)
local outputSourceArrayVars = ""
local outputSourceMapVars = ""
for _, filePath in ipairs(sourcebatch.sourcefiles) do
local escapedName = string.gsub(filePath, "[/|.]", "_")
local varDecl = string.format("static const unsigned char %s[] = {\n\t#include <%s>\n};\n\n", escapedName, filePath .. ".h")
outputSourceContent = outputSourceContent .. varDecl
outputSourceArrayVars = outputSourceArrayVars .. string.format("\t{%s, sizeof(%s)},\n", escapedName, escapedName)
outputSourceMapVars = outputSourceMapVars .. string.format("\t{\"%s\", AssetName::%s},\n", filePath, escapedName)
end
outputSourceContent = outputSourceContent .. string.format([[
static const Asset assets[] = {
%s
};
static const std::map<std::string, AssetName> assetMap = {
%s
};
]], outputSourceArrayVars, outputSourceMapVars)
outputSourceContent = outputSourceContent .. [[
const Asset& getResource(AssetName fileName) {
return assets[static_cast<std::size_t>(fileName)];
}
const Asset& getResource(const std::string& fileName) {
return getResource(assetMap.at(fileName));
}
]]
for _, sourcefile_bin in ipairs(sourcebatch.sourcefiles) do
-- get header file
local headerdir = path.join(target:autogendir(), "rules", "bin2c")
local headerfile = path.join(headerdir, sourcefile_bin .. ".h")
target:add("includedirs", headerdir)
-- add commands
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", sourcefile_bin)
batchcmds:mkdir(headerdir)
local argv = {"lua", "private.utils.bin2c", "-i", path(sourcefile_bin), "-o", path(headerfile)}
local linewidth = target:extraconf("rules", "bin2c", "linewidth")
if linewidth then
table.insert(argv, "-w")
table.insert(argv, tostring(linewidth))
end
local nozeroend = target:extraconf("rules", "bin2c", "nozeroend")
if nozeroend then
table.insert(argv, "--nozeroend")
end
batchcmds:vrunv(os.programfile(), argv, {envs = {XMAKE_SKIP_HISTORY = "y"}})
-- add deps
batchcmds:add_depfiles(sourcefile_bin)
batchcmds:set_depmtime(os.mtime(headerfile))
batchcmds:set_depcache(target:dependfile(headerfile))
end
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", outputHeader)
io.writefile(outputHeader, outputHeaderContent)
batchcmds:show_progress(opt.progress, "${color.build.object}generating.bin2c %s", outputSource)
io.writefile(outputSource, outputSourceContent)
end)