1 Commits

Author SHA1 Message Date
6da3cb66fa changed InfoAppMenu 2025-05-23 23:57:13 +02:00
25 changed files with 220 additions and 324 deletions

View File

@@ -1,36 +0,0 @@
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/diagrams/*.violet.html doc/*.violet.html
doc/mockups/* doc/mockups/*
# data files # data files

View File

@@ -1,35 +1,28 @@
# jminos # jminos
Modern stacker game with every polyominoes from size 1 to 15, made in C++ with [SFML 3](https://www.sfml-dev.org/)! Modern stacker game with every polyominos 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 for Windows 11 and WSL2 Ubuntu. This game has been tested on and built on Windows 11 and WSL2 Ubuntu only.
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 up to pentedecaminoes! - Every polyominoes from size 1 to 15, selectable as you wish, with customizable propotionnality for each size!
- 7bag with proportionnality for each polyomino size!
- AutoRS as the Rotation System!
- 0° rotations!
- All spin!
- IRS, IHS, infinite hold, and other leniency mechanics!
- Customizable board size!
- Customizable keybinds! - Customizable keybinds!
- 0° rotations!
- AutoRS as the Rotation System!
- IRS, IHS, infinite hold, and other leniency mechanics!
- Very bland interface!! (i'm not a designer) - Very bland interface!! (i'm not a designer)
### Available gamemodes ### Available gamemodes
@@ -38,22 +31,11 @@ You will find more infos about the Rotation System, the scoring system, or the d
- MARATHON : clear 200 lines with increasing gravity! - MARATHON : clear 200 lines with increasing gravity!
- ULTRA : scores as much as possible in only 2 minutes! - ULTRA : scores as much as possible in only 2 minutes!
- MASTER : clear 200 lines at levels higher than maximum gravity! - MASTER : clear 200 lines at levels higher than maximum gravity!
- INVISIBLE : get 1000 grade while not being able to see the board!
- ZEN : practice indefinitely in this mode with no gravity! - ZEN : practice indefinitely in this mode with no gravity!
### Screenshots ### Screenshots
Pentedecamino jumpscare // TODO when the game is finished //
![](./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
@@ -80,42 +62,12 @@ 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 779 KiB

View File

@@ -366,10 +366,6 @@ bool Game::areBlocksBones() const {
return this->parameters.getBoneBlocks(); return this->parameters.getBoneBlocks();
} }
bool Game::isBoardInvisible() const {
return this->parameters.getInvisibleBoard();
}
const Board& Game::getBoard() const { const Board& Game::getBoard() const {
return this->board.getBoard(); return this->board.getBoard();
} }

View File

@@ -136,11 +136,6 @@ class Game {
*/ */
bool areBlocksBones() const; bool areBlocksBones() const;
/**
* @return If the board is currently invisible
*/
bool isBoardInvisible() const;
/** /**
* @return The board * @return The board
*/ */

View File

@@ -24,8 +24,6 @@ void GameParameters::reset() {
case MARATHON : {this->level = 1; break;} case MARATHON : {this->level = 1; break;}
// goes from level 20 to 39 // goes from level 20 to 39
case MASTER : {this->level = 20; break;} case MASTER : {this->level = 20; break;}
// goes from level 1 to 19
case INVISIBLE : {this->level = 1; break;}
// no gravity // no gravity
case ZEN : {this->level = 0; break;} case ZEN : {this->level = 0; break;}
default : this->level = 1; default : this->level = 1;
@@ -36,7 +34,7 @@ void GameParameters::reset() {
void GameParameters::lockedPiece(const LineClear& lineClear) { void GameParameters::lockedPiece(const LineClear& lineClear) {
switch (this->gamemode) { switch (this->gamemode) {
// modes where level increases with lines // modes where level increases
case MARATHON : case MARATHON :
case MASTER : { case MASTER : {
int previousLines = this->clearedLines; int previousLines = this->clearedLines;
@@ -53,23 +51,9 @@ void GameParameters::lockedPiece(const LineClear& lineClear) {
default : this->clearedLines += lineClear.lines; default : this->clearedLines += lineClear.lines;
} }
int previousGrade = this->grade;
if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) { if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) {
this->grade += (1 + lineClear.lines); this->grade += (1 + lineClear.lines);
} }
switch (this->gamemode) {
// modes where level increases with grade
case INVISIBLE : {
if (previousGrade / 100 < this->grade / 100) {
this->level += 2;
this->updateStats();
}
break;
}
// other modes
default : break;
}
} }
bool GameParameters::hasWon(int framesPassed) const { bool GameParameters::hasWon(int framesPassed) const {
@@ -82,8 +66,6 @@ bool GameParameters::hasWon(int framesPassed) const {
case MARATHON : return this->clearedLines >= 200; case MARATHON : return this->clearedLines >= 200;
// win once 200 lines have been cleared // win once 200 lines have been cleared
case MASTER : return this->clearedLines >= 200; case MASTER : return this->clearedLines >= 200;
// win once 1000 grade has been passed
case INVISIBLE : return this->grade >= 1000;
// infinite mode // infinite mode
case ZEN : case ZEN :
default : return false; default : return false;
@@ -102,8 +84,7 @@ void GameParameters::updateStats() {
} }
// 3 for slow-controls gamemodes // 3 for slow-controls gamemodes
case MARATHON : case MARATHON :
case MASTER : case MASTER : {
case INVISIBLE : {
this->nextQueueLength = 3; this->nextQueueLength = 3;
break; break;
} }
@@ -113,13 +94,10 @@ void GameParameters::updateStats() {
/* BONE BLOCKS */ /* BONE BLOCKS */
switch (this->gamemode) { switch (this->gamemode) {
// blocks turns into bone blocks at level 30 // blocks turns into bone blocks at level 30
case MASTER : {this->boneBlocks = (this->level >= 30); break;} case MASTER : this->boneBlocks = (this->level >= 30);
default : this->boneBlocks = false; default : this->boneBlocks = false;
} }
/* INVISIBLE */
this->invisibleBoard = (this->gamemode == INVISIBLE);
/* GRAVITY */ /* GRAVITY */
// get gravity for an assumed 20-rows board // get gravity for an assumed 20-rows board
static const int gravityPerLevel[] = { static const int gravityPerLevel[] = {
@@ -174,8 +152,6 @@ void GameParameters::updateStats() {
case MARATHON : {this->ARE = 24 - (this->level - 1); break;} case MARATHON : {this->ARE = 24 - (this->level - 1); break;}
// starts at 400ms (24f) at lvl 20 and ends at 083ms (5f) at lvl 39 // starts at 400ms (24f) at lvl 20 and ends at 083ms (5f) at lvl 39
case MASTER : {this->ARE = 24 - (this->level - 20); break;} case MASTER : {this->ARE = 24 - (this->level - 20); break;}
// fixed at 250ms (15f)
case INVISIBLE : {this->ARE = 15; break;}
// no ARE by default // no ARE by default
default : this->ARE = 0; default : this->ARE = 0;
} }
@@ -252,10 +228,6 @@ bool GameParameters::getBoneBlocks() const {
return this->boneBlocks; return this->boneBlocks;
} }
bool GameParameters::getInvisibleBoard() const {
return this->invisibleBoard;
}
int GameParameters::getGravity() const { int GameParameters::getGravity() const {
return this->gravity; return this->gravity;
} }

View File

@@ -18,7 +18,6 @@ class GameParameters {
int grade; // the current amount of points int grade; // the current amount of points
int nextQueueLength; // the number of pieces visibles in the next queue int nextQueueLength; // the number of pieces visibles in the next queue
bool boneBlocks; // wheter all blocks are bone blocks bool boneBlocks; // wheter all blocks are bone blocks
bool invisibleBoard; // wheter the board is invisible
int gravity; // the gravity at which pieces drop int gravity; // the gravity at which pieces drop
int lockDelay; // the time before the piece lock in place int lockDelay; // the time before the piece lock in place
int forcedLockDelay; // the forced time before the piece lock in place int forcedLockDelay; // the forced time before the piece lock in place
@@ -78,14 +77,9 @@ class GameParameters {
int getNextQueueLength() const; int getNextQueueLength() const;
/** /**
* @return Wheter the blocks are currently bone blocks * Returns wheter the blocks are currently bone blocks
*/ */
bool getBoneBlocks() const; bool getBoneBlocks() const;
/**
* @return Wheter the board is currently invisible
*/
bool getInvisibleBoard() const;
/** /**
* @return The current gravity for a 20-line high board * @return The current gravity for a 20-line high board

View File

@@ -11,7 +11,6 @@ enum Gamemode {
MARATHON, MARATHON,
ULTRA, ULTRA,
MASTER, MASTER,
INVISIBLE,
ZEN ZEN
}; };
@@ -25,7 +24,6 @@ inline std::string getGamemodeName(Gamemode gamemode) {
"MARATHON", "MARATHON",
"ULTRA", "ULTRA",
"MASTER", "MASTER",
"INVISIBLE",
"ZEN" "ZEN"
}; };
@@ -41,7 +39,6 @@ inline std::string getGamemodeGoal(Gamemode gamemode) {
"200 lines", "200 lines",
"2 minutes", "2 minutes",
"200 lines", "200 lines",
"1000 grade",
"Infinite" "Infinite"
}; };

View File

@@ -57,6 +57,33 @@ class AppMenu {
} }
} }
sf::Text createText(int fontSize = 2) const {
sf::Text newText(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * fontSize);
newText.setFillColor(sf::Color::Black);
newText.setOutlineColor(sf::Color::White);
newText.setOutlineThickness(0);
return newText;
}
void setTextPosition(sf::Text& text, float xPos, float yPos) const {
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
}
void setTitlePosition(sf::Text& text, float yPos) const {
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
}
void setTextOutline(sf::Text& text, bool hasOutline) const {
text.setOutlineThickness(hasOutline * (this->settings->getWindowSizeMultiplier() / 2));
}
void placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const { void placeText(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float xPos, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
float sizeMultiplier = this->settings->getWindowSizeMultiplier(); float sizeMultiplier = this->settings->getWindowSizeMultiplier();

View File

@@ -104,26 +104,17 @@ void GamePlayingAppMenu::computeFrame() {
void GamePlayingAppMenu::drawFrame() const { void GamePlayingAppMenu::drawFrame() const {
this->renderWindow->clear(sf::Color(200, 200, 200)); this->renderWindow->clear(sf::Color(200, 200, 200));
sf::Color bonesBlockColor(0, 0, 0);
sf::Color bonesBlockGhostColor(100, 100, 100);
bool areBlockBones = this->game.areBlocksBones();
bool isBoardInvisible = this->game.isBoardInvisible() && !(this->game.hasWon() || this->game.hasLost());
sf::Vector2f cellSize(this->cellSizeZoom, this->cellSizeZoom); sf::Vector2f cellSize(this->cellSizeZoom, this->cellSizeZoom);
float cellOutlineThickness = this->cellSizeZoom / 4;
bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost()); bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost());
// 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;
sf::RectangleShape cell(cellSize); sf::RectangleShape cell(cellSize);
cell.setFillColor((areBlockBones && block != NOTHING) cell.setFillColor(this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
? bonesBlockColor
: this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
cell.setPosition(this->getBoardBlockPosition(x, y)); cell.setPosition(this->getBoardBlockPosition(x, y));
this->renderWindow->draw(cell); this->renderWindow->draw(cell);
} }
@@ -131,10 +122,7 @@ void GamePlayingAppMenu::drawFrame() const {
if (drawActivePiece) { if (drawActivePiece) {
// ghost piece // ghost piece
sf::Color ghostColor = areBlockBones sf::Color ghostColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
? bonesBlockGhostColor
: this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
for (const Position& position : this->game.getActivePiece()->getPositions()) { for (const Position& position : this->game.getActivePiece()->getPositions()) {
Position cellPosition = (this->game.getGhostPiecePosition() + position); Position cellPosition = (this->game.getGhostPiecePosition() + position);
@@ -145,13 +133,13 @@ void GamePlayingAppMenu::drawFrame() const {
} }
// active piece outline // active piece outline
float pieceOutlineSize = std::roundf(this->cellSizeZoom / 4);
sf::Color pieceOultlineColor = sf::Color(255, 255 - (255 * this->game.getForcedLockDelayProgression()), 255 - (255 * this->game.getForcedLockDelayProgression())); sf::Color pieceOultlineColor = sf::Color(255, 255 - (255 * this->game.getForcedLockDelayProgression()), 255 - (255 * this->game.getForcedLockDelayProgression()));
for (const Position& position : this->game.getActivePiece()->getPositions()) { for (const Position& position : this->game.getActivePiece()->getPositions()) {
Position cellPosition = (this->game.getActivePiecePosition() + position); Position cellPosition = (this->game.getActivePiecePosition() + position);
sf::RectangleShape cell(cellSize); sf::RectangleShape cell(cellSize);
cell.setOutlineThickness(cellOutlineThickness); cell.setOutlineThickness(pieceOutlineSize);
cell.setOutlineColor(pieceOultlineColor); cell.setOutlineColor(pieceOultlineColor);
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y)); cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
this->renderWindow->draw(cell); this->renderWindow->draw(cell);
@@ -166,9 +154,7 @@ void GamePlayingAppMenu::drawFrame() const {
if (drawActivePiece) { if (drawActivePiece) {
// active piece // active piece
sf::Color pieceColor = areBlockBones sf::Color pieceColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
? bonesBlockColor
: this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
for (const Position& position : this->game.getActivePiece()->getPositions()) { for (const Position& position : this->game.getActivePiece()->getPositions()) {
Position cellPosition = (this->game.getActivePiecePosition() + position); Position cellPosition = (this->game.getActivePiecePosition() + position);
@@ -187,9 +173,7 @@ void GamePlayingAppMenu::drawFrame() const {
nextBox.position.y -= upShift; nextBox.position.y -= upShift;
sf::Vector2f nextCellSize(this->nextCellSizeZoom, this->nextCellSizeZoom); sf::Vector2f nextCellSize(this->nextCellSizeZoom, this->nextCellSizeZoom);
sf::Color pieceColor = areBlockBones sf::Color color = this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
? bonesBlockColor
: this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
sf::Color boxColor = sf::Color(180, 180, 180); sf::Color boxColor = sf::Color(180, 180, 180);
int lowestRank = 0; int lowestRank = 0;
@@ -197,7 +181,7 @@ void GamePlayingAppMenu::drawFrame() const {
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).getPositions().contains(Position{x, y})) {
cell.setFillColor(pieceColor); cell.setFillColor(color);
lowestRank = y; lowestRank = y;
} }
else { else {
@@ -215,11 +199,9 @@ void GamePlayingAppMenu::drawFrame() const {
// hold box // hold box
if (this->game.getHeldPiece() != nullptr) { if (this->game.getHeldPiece() != nullptr) {
sf::Vector2f holdCellSize(this->holdCellSizeZoom, this->holdCellSizeZoom); sf::Vector2f holdCellSize(this->holdCellSizeZoom, this->holdCellSizeZoom);
sf::Color color = areBlockBones sf::Color color = this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
? bonesBlockColor
: this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
sf::Color boxColor = sf::Color(180, 180, 180); sf::Color boxColor = sf::Color(180, 180, 180);
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);
@@ -230,7 +212,7 @@ void GamePlayingAppMenu::drawFrame() const {
cell.setFillColor(boxColor); cell.setFillColor(boxColor);
} }
cell.setPosition(sf::Vector2f(this->holdBoxPosition.position.x + (x * this->nextCellSizeZoom), cell.setPosition(sf::Vector2f(this->holdBoxPosition.position.x + (x * this->nextCellSizeZoom),
this->holdBoxPosition.position.y + ((this->game.getHeldPiece()->getLength() - y - 1) * this->holdCellSizeZoom))); this->holdBoxPosition.position.y + ((this->game.getHeldPiece()->getLength() - y - 1) * this->holdCellSizeZoom)));
this->renderWindow->draw(cell); this->renderWindow->draw(cell);
} }
} }

View File

@@ -13,7 +13,7 @@
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : GameSettingsAppMenu::GameSettingsAppMenu(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({2, 3, 3}) { playerCursor({2, 3, 2}) {
} }
@@ -33,8 +33,7 @@ void GameSettingsAppMenu::computeFrame() {
case 2 : { case 2 : {
switch (this->playerCursor.getPosition().x) { switch (this->playerCursor.getPosition().x) {
case 0 : {this->settings->setGamemode(MASTER); break;} case 0 : {this->settings->setGamemode(MASTER); break;}
case 1 : {this->settings->setGamemode(INVISIBLE); break;} case 1 : {this->settings->setGamemode(ZEN); break;}
case 2 : {this->settings->setGamemode(ZEN); break;}
} }
break; break;
} }
@@ -77,8 +76,7 @@ void GameSettingsAppMenu::drawFrame() const {
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1}); this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1}); this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1});
this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2}); this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2});
this->placeText(text, this->playerCursor, "INVISIBLE", 25.f, 45.f, sf::Vector2u{1, 2}); this->placeText(text, this->playerCursor, "ZEN", 25.f, 45.f, sf::Vector2u{1, 2});
this->placeText(text, this->playerCursor, "ZEN", 50.f, 45.f, sf::Vector2u{2, 2});
this->renderWindow->display(); this->renderWindow->display();
} }

View File

@@ -67,14 +67,47 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
"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."
) { ),
sectionNameText(this->createText()),
sectionContentText(this->createText()),
renderTexture(this->renderWindow->getSize()),
sprite(this->renderTexture.getTexture()) {
this->setTextOutline(this->sectionNameText, true);
this->sectionContentText.setLineSpacing((float) this->settings->getWindowSizeMultiplier() / 8);
this->sectionNameText.setString(this->sectionsName[this->playerCursor.getPosition().x]);
this->setTitlePosition(this->sectionNameText, 10.f);
this->sectionContentText.setString(this->sectionsContent[this->playerCursor.getPosition().x]);
this->setTextPosition(this->sectionContentText, 5.f, 30.f);
this->renderTexture.clear(sf::Color(200, 200, 200));
this->renderTexture.draw(this->sectionNameText);
this->renderTexture.draw(this->sectionContentText);
this->renderTexture.display();
this->sprite.setTexture(this->renderTexture.getTexture());
} }
void InfoAppMenu::computeFrame() { void InfoAppMenu::computeFrame() {
this->updateMetaBinds(); this->updateMetaBinds();
this->playerCursor.updatePosition(); this->playerCursor.updatePosition();
if (this->playerCursor.movedLeft() || this->playerCursor.movedRight()) {
this->sectionNameText.setString(this->sectionsName[this->playerCursor.getPosition().x]);
this->setTitlePosition(this->sectionNameText, 10.f);
this->sectionContentText.setString(this->sectionsContent[this->playerCursor.getPosition().x]);
this->setTextPosition(this->sectionContentText, 5.f, 30.f);
this->renderTexture.clear(sf::Color(200, 200, 200));
this->renderTexture.draw(this->sectionNameText);
this->renderTexture.draw(this->sectionContentText);
this->renderTexture.display();
this->sprite.setTexture(this->renderTexture.getTexture());
}
if (this->escReleased) { if (this->escReleased) {
this->menuStack->pop(); this->menuStack->pop();
} }
@@ -83,15 +116,7 @@ void InfoAppMenu::computeFrame() {
void InfoAppMenu::drawFrame() const { void InfoAppMenu::drawFrame() const {
this->renderWindow->clear(sf::Color(200, 200, 200)); this->renderWindow->clear(sf::Color(200, 200, 200));
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2); this->renderWindow->draw(sprite);
text.setFillColor(sf::Color(0, 0, 0));
text.setOutlineColor(sf::Color(255, 255, 255));
this->placeTitle(text, this->playerCursor, this->sectionsName[this->playerCursor.getPosition().x], 10.f, this->playerCursor.getPosition());
text.setLineSpacing((float) this->settings->getWindowSizeMultiplier() / 8);
text.setOutlineThickness(0);
this->placeText(text, {}, this->sectionsContent[this->playerCursor.getPosition().x], 5.f, 30.f, {});
this->renderWindow->display(); this->renderWindow->display();
} }

View File

@@ -15,6 +15,10 @@ class InfoAppMenu : public AppMenu {
PlayerCursor playerCursor; PlayerCursor playerCursor;
sf::String sectionsName[INFO_SECTIONS_COUNT]; sf::String sectionsName[INFO_SECTIONS_COUNT];
sf::String sectionsContent[INFO_SECTIONS_COUNT]; sf::String sectionsContent[INFO_SECTIONS_COUNT];
sf::Text sectionNameText;
sf::Text sectionContentText;
sf::RenderTexture renderTexture;
sf::Sprite sprite;
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

@@ -42,6 +42,13 @@ void PlayerCursor::updatePosition() {
} }
} }
bool PlayerCursor::moved() const {
return (this->movedLeft()
|| this->movedRight()
|| this->movedUp()
|| this->movedDown());
}
bool PlayerCursor::movedLeft() const { bool PlayerCursor::movedLeft() const {
return this->shouldMove(this->leftDAS); return this->shouldMove(this->leftDAS);
} }
@@ -115,7 +122,7 @@ const sf::Vector2u& PlayerCursor::getPosition() const {
bool PlayerCursor::shouldMove(int DAS) const { bool PlayerCursor::shouldMove(int DAS) const {
return (DAS == 1 return (DAS == 1
|| (DAS > MENU_DAS && (DAS % 5) == 0) || (DAS > MENU_DAS && (DAS % 5) == 0)
|| (DAS > (FRAMES_PER_SECOND * 2))); || (DAS > (MENU_DAS * 4)));
} }
void PlayerCursor::moveLeft() { void PlayerCursor::moveLeft() {

View File

@@ -18,6 +18,8 @@ class PlayerCursor {
void updatePosition(); void updatePosition();
bool moved() const;
bool movedLeft() const; bool movedLeft() const;
bool movedRight() const; bool movedRight() const;

View File

@@ -25,35 +25,22 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
} }
Generator generator; Generator generator;
std::vector<Polyomino> polyominoes = generator.generatePolyominoes(polyominoSize); std::vector<Polyomino> nMinos = 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 : polyominoes) { for (Polyomino& nMino : nMinos) {
nMino.goToSpawnPosition(); nMino.goToSpawnPosition();
} }
std::sort(polyominoes.begin(), polyominoes.end()); std::sort(nMinos.begin(), nMinos.end());
for (const Polyomino& polyomino : polyominoes) { for (const Polyomino& nMino : nMinos) {
// write the characteristics of the piece // write the characteristics of the piece
char infoByte = (polyomino.isConvex() << 7) + (polyomino.hasHole() << 6) + polyomino.getLength(); char infoByte = (nMino.isConvex() << 7) + (nMino.hasHole() << 6) + nMino.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 (const Position position : polyomino.getPositions()) { for (Position position : nMino.getPositions()) {
positionByte = (position.x << 4) + position.y; positionByte = (position.x << 4) + position.y;
piecesFile.write(&positionByte, 1); piecesFile.write(&positionByte, 1);
} }

View File

@@ -22,18 +22,13 @@ 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,60 +3,45 @@
#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 max_size); void testGeneratorForAllSizes(int amount);
void testGeneratorForOneSize(int size); void testGeneratorForOneSize(int size);
void printPiecesByTypesForOneSize(int size); void testGeneratorByprintingAllNminos(int n);
void readStatsFromFilesForAllSizes(int max_size); void testStoringAndRetrievingPieces(int size);
void generateFilesForAllSizes(int amount);
void benchmarking(int min_size, int max_size); void generateFilesForOneSize(int 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));
#ifdef BENCHMARK // dev: generate files if it hasn't been done before, UI will NOT generate the files
benchmarking(1, BENCHMARK_PIECES_SIZE); //generateFilesForAllSizes(10);
#else
// dev: generate files if it hasn't been done before, UI will NOT generate the files
//generateFilesForAllSizes(10);
PiecesFiles pf; TextApp UI;
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) { UI.run();
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 max_size) { void testGeneratorForAllSizes(int amount) {
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 <= max_size; i++) { for (int i = 1; i <= amount; 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;
} }
} }
@@ -78,8 +63,24 @@ void testGeneratorForOneSize(int size) {
} }
} }
void printPiecesByTypesForOneSize(int size) { void testGeneratorByprintingAllNminos(int n) {
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;
@@ -103,9 +104,79 @@ void printPiecesByTypesForOneSize(int size) {
} }
} }
void readStatsFromFilesForAllSizes(int max_size) { void generateFilesForAllSizes(int amount) {
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;
@@ -118,66 +189,3 @@ void readStatsFromFilesForAllSizes(int max_size) {
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

@@ -11,27 +11,18 @@ 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)
set_kind("binary") set_kind("binary")
add_files("./src/GraphicalUI/**.cpp") add_files("./src/GraphicalUI/**.cpp")
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
-- --