Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
de63cd22b6
|
|||
| 07ba9619ed | |||
| 4095103843 | |||
| ecc035c972 | |||
| dd6da58642 | |||
| a3ef52c7a1 | |||
| 72e9f420ab | |||
| 46b9b8dd65 | |||
| d5b51213c8 | |||
| 161c9425ae | |||
| 561b6d76b8 | |||
| b276213794 | |||
| 7b5801e630 | |||
| e676cd19f6 | |||
| 8342bf3969 | |||
| 34d781fcfe | |||
| b17a40fda5 | |||
| 17459c4026 | |||
| 3e40ff7252 | |||
| dd8314f76c | |||
| d7f52239f0 | |||
| 72f4ea75ff | |||
| b1b7277666 | |||
| fd6bdc2b09 | |||
| 69b91d6497 | |||
| d50714ef8c | |||
| 6ed85869ae | |||
| 3d1feb6295 | |||
| 7a96136631 | |||
| fc6348cebc | |||
| 0bb25d4628 | |||
| 69c07abcec | |||
| 774aa1422f | |||
| 4dfeda0957 | |||
| 05bb79d4a9 | |||
| e0ab6a4828 | |||
| a62b3c018d | |||
| 46ebb88ef2 | |||
| de14978b01 | |||
| d5ac79559e | |||
| 5ea47ddd25 | |||
| 1a95765877 | |||
| 6bff555cbc | |||
| f4b58fb67e | |||
| 0271b56542 | |||
| 5d73e751d7 | |||
| 314b7a8488 | |||
| 7151be0b1a | |||
| d124205a71 | |||
| 87920548e5 | |||
| 57620c70a2 | |||
| 3dac18c821 | |||
| 3538403f40 | |||
| ec40495328 |
36
.github/workflows/ubuntu.yml
vendored
Normal 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
@@ -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
|
||||||
|
|||||||
115
README.md
@@ -1,22 +1,42 @@
|
|||||||
# 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)
|
||||||
|
- [How to play](#how-to-play)
|
||||||
|
- [Features](#features)
|
||||||
|
- [Manual build](#manual-build)
|
||||||
|
- [Benchmarks](#benchmarks)
|
||||||
|
- [Credits](#credits)
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
// to bo included when release version is done //
|
You can download the latest release [here](https://git.ale-pri.com/TetrisNerd/jminos/releases)!
|
||||||
|
This game has been tested on Windows 11 and Linux, and releases are provided for theses two systems.
|
||||||
This game has been tested on and provides executables for Windows 11 and Linux under Ubuntu only.
|
If your OS isn't compatible, 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-and-run).
|
|
||||||
|
|
||||||
## How to play
|
## How to play
|
||||||
|
|
||||||
### General
|
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 unmutable 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 some more infos about the Rotation System and the scoring 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 and classification of polyominoes, [check the documentation](/doc/)!
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Every polyominoes up to pentedecaminoes!
|
||||||
|
- 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!
|
||||||
|
- Very bland interface!! (i'm not a designer)
|
||||||
|
|
||||||
### Available gamemodes
|
### Available gamemodes
|
||||||
|
|
||||||
@@ -24,14 +44,34 @@ If you want to know more details about the generation and classification of poly
|
|||||||
- 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!
|
||||||
- ??? : still to do
|
|
||||||
|
### Screenshots
|
||||||
|
|
||||||
|
Pentedecamino jumpscare
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Pieces select screen
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
AutoRS demonstration
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
0° spins demonstration
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Manual build
|
## Manual build
|
||||||
|
|
||||||
This project uses xmake for compiling, xmake is cross-platform and works in most OS, xmake also automatically install supported librairies.
|
This project uses xmake for compiling, xmake is cross-platform and works in most OS, xmake also automatically install supported librairies.
|
||||||
To be able to build this project, you need to [have xmake installed](https://xmake.io) and have a compiler with C++20 compatibility.
|
To be able to build this project, you need to [have xmake installed](https://xmake.io) and have a compiler with C++20 compatibility.
|
||||||
|
|
||||||
|
If you want to contribute or are simply curious, you can check the [wiki](https://git.ale-pri.com/TetrisNerd/jminos/wiki)!
|
||||||
|
|
||||||
### Build the project
|
### Build the project
|
||||||
|
|
||||||
``cd jminos``
|
``cd jminos``
|
||||||
@@ -41,15 +81,63 @@ To be able to build this project, you need to [have xmake installed](https://xma
|
|||||||
If you need to change the toolchain (for example using gcc):
|
If you need to change the toolchain (for example using gcc):
|
||||||
``xmake f --toolchain=gcc``
|
``xmake f --toolchain=gcc``
|
||||||
|
|
||||||
|
If you want to build for another platform (for example with mingw):
|
||||||
|
``xmake f -p mingw``
|
||||||
|
|
||||||
### Run the project
|
### Run the project
|
||||||
|
|
||||||
``xmake run``
|
``xmake run``
|
||||||
Note that the program will generate the polyomino files for you the first time. This lasts several minutes so it only does it up to size 10. If you want to use the full range up to size 15, you will need to uncomment the ``#define`` at line 13 of file ``src/GraphicalUI/Settings.h``.
|
|
||||||
|
The program will generate the polyomino files for you if you don't have them.
|
||||||
|
As this is a lengthy process, debug mode limits pieces size to 10.
|
||||||
|
To switch between debug and release mode:
|
||||||
|
``xmake f -m debug``
|
||||||
|
``xmake f -m release``
|
||||||
|
|
||||||
If for some reasons you wanna run the command line version:
|
If for some reasons you wanna run the command line version:
|
||||||
|
``xmake build text``
|
||||||
``xmake run text``
|
``xmake run text``
|
||||||
|
The command line version is **not** updated.
|
||||||
|
|
||||||
### Create a release version (xmake packaging ???)
|
### Package the project
|
||||||
|
|
||||||
|
To package the executable manually properly, follow theses steps:
|
||||||
|
|
||||||
|
1. Clone the project into a new folder.
|
||||||
|
2. If you already have all 15 pieces files, copy them over to the ``data/pieces`` folder, else you will be generating them at the next step.
|
||||||
|
3. Go into release mode (``xmake f -m release``) and run the project once (``xmake`` and ``xmake run``). This is to generate the default config files, close the game immediately after it started.
|
||||||
|
4. Make a new folder named ``jminos`` and copy the executable (should be located somewhere like ``build/linux/x86_64/release/graph``) as well as the ``data/pieces`` and ``data/config`` folders.
|
||||||
|
5. Zip the newly created ``jminos`` folder into a file named ``jminos_{platform}``.
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
### One-sided n-polyominoes
|
||||||
|
|
||||||
|
| n | Number | Generation | File storing | File retrieving | File size |
|
||||||
|
| -: | -: | :-: | :-: | :-: | -: |
|
||||||
|
| 1 | 1 | 0s 0.005471ms | 0s 0.14436ms | 0s 0.022223ms | 3 bytes |
|
||||||
|
| 2 | 1 | 0s 0.006979ms | 0s 0.036624ms | 0s 0.011424ms | 4 bytes |
|
||||||
|
| 3 | 2 | 0s 0.018718ms | 0s 0.035885ms | 0s 0.013246ms | 9 bytes |
|
||||||
|
| 4 | 7 | 0s 0.060544ms | 0s 0.056277ms | 0s 0.019395ms | 36 bytes |
|
||||||
|
| 5 | 18 | 0s 0.220348ms | 0s 0.166593ms | 0s 0.036526ms | 76 bytes |
|
||||||
|
| 6 | 60 | 0s 0.773924ms | 0s 0.283423ms | 0s 0.063492ms | 186 bytes |
|
||||||
|
| 7 | 196 | 0s 3.00331ms | 0s 0.827344ms | 0s 0.163653ms | 546 bytes |
|
||||||
|
| 8 | 704 | 0s 13.142ms | 0s 3.68255ms | 0s 0.630044ms | 1 898 bytes |
|
||||||
|
| 9 | 2500 | 0s 50.9272ms | 0s 16.1929ms | 0s 2.35157ms | 6889 bytes |
|
||||||
|
| 10 | 9189 | 0s 204.031ms | 0s 87.1819ms | 0s 10.5841ms | 25302 bytes |
|
||||||
|
| 11 | 33896 | 0s 832.82ms | 0s 412.466ms | 0s 57.6399ms | 93711 bytes |
|
||||||
|
| 12 | 126759 | 3s 425.907ms | 1s 982.715ms | 0s 226.816ms | 350325 bytes |
|
||||||
|
| 13 | 476270 | 14s 570.595ms | 9s 945.511ms | 0s 972.036ms | 1327156 bytes |
|
||||||
|
| 14 | 1802312 | 56s 394.426ms | 41s 675.672ms | 4s 79.0436ms | 5035148 bytes |
|
||||||
|
| 15 | 6849777 | 258s 219.666ms | 223s 386.329ms | 16s 483.426ms | 19392417 bytes |
|
||||||
|
|
||||||
|
_File storing includes type checking and sorting all polyominoes before writing them to the file._
|
||||||
|
The files are compressed, they used to be about 5x as large.
|
||||||
|
|
||||||
|
Run it yourself by typing:
|
||||||
|
``xmake f -m release``
|
||||||
|
``xmake build bmark``
|
||||||
|
``xmake run bmark``
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@@ -58,3 +146,6 @@ Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
|||||||
|
|
||||||
Inspired by other modern stacker games such as Techmino, jstris, tetr.io, etc.
|
Inspired by other modern stacker games such as Techmino, jstris, tetr.io, etc.
|
||||||
This game isn't affiliated with any of them.
|
This game isn't affiliated with any of them.
|
||||||
|
|
||||||
|
Special thanks to my friend [Simon](https://git.ale-pri.com/Persson-dev) who did most of the outside stuff (github actions, files compression, asset manager, xmake).
|
||||||
|
All the code in src/Common/, src/Utils/ and xmake/ comes from him.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
BIN
doc/diagrams/class_diagramm_graphicalUI.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
BIN
doc/diagrams/package_diagramm.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
## Pieces
|
## Pieces
|
||||||
|
|
||||||
If you don't know what a polyomino is, check [this other file](Pieces_representation.md#what-are-polyominoes).
|
_Note: the current algorithm has been adapted to use file compression. There is currently no documentation on how the compression work, but you can check the code in the [src/Common](https://git.ale-pri.com/TetrisNerd/jminos/src/branch/main/src/Common) folder._
|
||||||
|
|
||||||
Generating polyominoes of size n is exponential in regard to n. Because of this, we will store the pieces beforehand and load them upon launching the game.
|
Generating polyominoes of size n is exponential in regard to n. Because of this, we will store the pieces beforehand and load them upon launching the game.
|
||||||
|
|
||||||
We want the pieces to be always sorted in the same order, always attributed the same block type, and always set at the same spawn position, no matter how they were generated. We also want them to be separated in 3 categories : convex, not convex but without a hole, and with a hole. Theses problematics are already resolved internally, but will be calculated before storage as to not need extra calculcations upon load (except for the block type which is trivially computed).
|
We want the pieces to be always sorted in the same order, always attributed the same block type, and always set at the same spawn position, no matter how they were generated. We also want them to be separated in 3 categories : convex, not convex but without a hole, and with a hole. Theses problematics are already solved internally, but will be calculated before storage as to not need extra calculcations upon load (except for the block type which is trivially computed).
|
||||||
|
|
||||||
Pieces are stored in binary files. Each file simply contains every polyomino of one size, one after the other. Since each file contains all polyominoes of the same size, we know how much stuff to read and don't need delimiters. We know we've read all pieces simply when we reach the end of file character.
|
Pieces are stored in binary files. Each file simply contains every polyomino of one size, one after the other. Since each file contains all polyominoes of the same size, we know how much stuff to read and don't need delimiters. We know we've read all pieces simply when we reach the end of file character.
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ _Repeat for every avaible actions._
|
|||||||
|
|
||||||
The settings file has the following format:
|
The settings file has the following format:
|
||||||
|
|
||||||
|
- The versions of the file format, stored with 1 byte
|
||||||
|
- The previous maximum pieces size selected, stored with 1 byte
|
||||||
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
- The number of the chosen keybinds (from 0 to 4), stored with 1 byte
|
||||||
- The DAS of the player, stored with 1 byte
|
- The DAS of the player, stored with 1 byte
|
||||||
- The ARR of the player, stored with 1 byte
|
- The ARR of the player, stored with 1 byte
|
||||||
@@ -47,5 +49,10 @@ The settings file has the following format:
|
|||||||
- The last selected width of the board, stored with 1 byte
|
- The last selected width of the board, stored with 1 byte
|
||||||
- The last selected height of the board, stored with 1 byte
|
- The last selected height of the board, stored with 1 byte
|
||||||
- The uniformity mode (0 for default distribution, 1 for uniformous distribution, 2 for custom distribution), stored with 1 byte
|
- The uniformity mode (0 for default distribution, 1 for uniformous distribution, 2 for custom distribution), stored with 1 byte
|
||||||
- If custom distribution is set, store the proportion of each size (15x1 byte total)
|
- For each size, store the custom proportion (from 0 to 20) (15x1 byte total)
|
||||||
- Every selected pieces, using 1 byte for the type of selection (once again converted from an Enum) and 1 byte for the actual value
|
- Every selected pieces
|
||||||
|
- For every groupe of piece (ALL, CONVEX, HOLELESS, OTHER), use 1 byte for the type (once again converted from an Enum) and 1 byte for the size
|
||||||
|
- For every single piece, use 1 byte for (the size + encoding that it is a single piece),
|
||||||
|
and 3 bytes to store the number of the piece (allows number up to 16M, size 15 has 6M pieces).
|
||||||
|
|
||||||
|
When starting the game, it will verify if the current settings file is in the latest format, if it is not it will be regenerated with default values.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Game logic
|
# Game logic
|
||||||
|
|
||||||
This section will detail how the player's action are interpreted into the game.
|
This section will detail how the player's action are interpreted into the game.
|
||||||
We will only talk about pieces and not polyominoes. In this project, pieces are an abstraction of a polyomino. Though if you want to know more the polyominoes in this project, check [this other file](Pieces_representation.md).
|
We will only talk about pieces and not polyominoes. In this project, pieces are an abstraction of a polyomino.
|
||||||
|
|
||||||
## Order of operations
|
## Order of operations
|
||||||
|
|
||||||
@@ -106,3 +106,17 @@ Grade is an alternate system to line clears.
|
|||||||
|
|
||||||
The only exception occurs when the total ends in '99', a line must be cleared to progress.
|
The only exception occurs when the total ends in '99', a line must be cleared to progress.
|
||||||
When this line is cleared, the point of the piece is also counted towards the total.
|
When this line is cleared, the point of the piece is also counted towards the total.
|
||||||
|
|
||||||
|
## Pieces distribution
|
||||||
|
|
||||||
|
A bag is an object which contains a set of pieces.
|
||||||
|
The principle of a bag generator is to put every available pieces into a bag and take them out one by one until the bag is empty, to then start with a new full bag.
|
||||||
|
It's a way to have equal distribution of pieces while still allowing for a random order of pieces.
|
||||||
|
|
||||||
|
The game has 3 modes of pieces distribution:
|
||||||
|
|
||||||
|
1. Default. The simplest of them, all selected pieces are put in a single bag.
|
||||||
|
2. Uniformous. The pieces are now separated by size and put in different bags. Each size will now appear as often as any other.
|
||||||
|
3. Custom. The pieces are once again separated by size, but now the player specifies how likely each size is to appear.
|
||||||
|
|
||||||
|
Both system 2 and 3 uses a system analogous to a bag to determine which size of piece to take next.
|
||||||
|
|||||||
BIN
doc/readme/big_piece.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
doc/readme/pieces_selection.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
doc/readme/rotation_0.gif
Normal file
|
After Width: | Height: | Size: 614 KiB |
BIN
doc/readme/rotations.gif
Normal file
|
After Width: | Height: | Size: 779 KiB |
102
src/Benchmark/main.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include "../Pieces/Generator.h"
|
||||||
|
#include "../Pieces/PiecesFiles.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
static const int BENCHMARK_PIECES_SIZE = 15;
|
||||||
|
|
||||||
|
void benchmarking(int min_size, int max_size);
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
std::cout << "IMPORTANT: You are currently in debug mode, debug mode has lowest optimization settings and thus yields worse benchmarking results, to switch to release mode, type 'xmake f -m debug'." << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
benchmarking(1, BENCHMARK_PIECES_SIZE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
auto t1 = high_resolution_clock::now();
|
||||||
|
auto t2 = high_resolution_clock::now();
|
||||||
|
duration<double, std::milli> ms_double;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (int i = min_size; i <= max_size; i++) {
|
||||||
|
std::cout << "| " << i;
|
||||||
|
|
||||||
|
t1 = high_resolution_clock::now();
|
||||||
|
std::vector<Polyomino> polyominoes = gen.generatePolyominoes(i);
|
||||||
|
t2 = high_resolution_clock::now();
|
||||||
|
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();
|
||||||
|
ok = 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);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
std::cerr << "\nERROR: Could not save pieces to file." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
ok = 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);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
std::cerr << "\nERROR: Could not load pieces from file." << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/Common/Compression.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include "Compression.h"
|
||||||
|
|
||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#define COMPRESSION_THRESHOLD 64
|
||||||
|
|
||||||
|
static DataBuffer Inflate(const std::uint8_t* source, std::size_t size, std::size_t uncompressedSize) {
|
||||||
|
DataBuffer result;
|
||||||
|
result.Resize(uncompressedSize);
|
||||||
|
|
||||||
|
uncompress(reinterpret_cast<Bytef*>(result.data()), reinterpret_cast<uLongf*>(&uncompressedSize),
|
||||||
|
reinterpret_cast<const Bytef*>(source), static_cast<uLong>(size));
|
||||||
|
|
||||||
|
assert(result.GetSize() == uncompressedSize);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DataBuffer Deflate(const std::uint8_t* source, std::size_t size) {
|
||||||
|
DataBuffer result;
|
||||||
|
uLongf compressedSize = size;
|
||||||
|
|
||||||
|
result.Resize(size); // Resize for the compressed data to fit into
|
||||||
|
compress(
|
||||||
|
reinterpret_cast<Bytef*>(result.data()), &compressedSize, reinterpret_cast<const Bytef*>(source), static_cast<uLong>(size));
|
||||||
|
result.Resize(compressedSize); // Resize to cut useless data
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer Compress(const DataBuffer& buffer) {
|
||||||
|
DataBuffer packet;
|
||||||
|
|
||||||
|
if (buffer.GetSize() < COMPRESSION_THRESHOLD) {
|
||||||
|
// Don't compress since it's a small packet
|
||||||
|
VarInt compressedDataLength = 0;
|
||||||
|
packet << compressedDataLength;
|
||||||
|
packet << buffer;
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer compressedData = Deflate(buffer.data(), buffer.GetSize());
|
||||||
|
|
||||||
|
VarInt uncompressedDataLength = buffer.GetSize();
|
||||||
|
packet << uncompressedDataLength;
|
||||||
|
packet.WriteSome(compressedData.data(), compressedData.GetSize());
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength) {
|
||||||
|
VarInt uncompressedLength;
|
||||||
|
buffer >> uncompressedLength;
|
||||||
|
|
||||||
|
std::uint64_t compressedLength = packetLength - uncompressedLength.GetSerializedLength();
|
||||||
|
|
||||||
|
if (uncompressedLength.GetValue() == 0) {
|
||||||
|
// Data already uncompressed. Nothing to do
|
||||||
|
DataBuffer ret;
|
||||||
|
buffer.ReadSome(ret, compressedLength);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(buffer.GetReadOffset() + compressedLength <= buffer.GetSize());
|
||||||
|
|
||||||
|
return Inflate(buffer.data() + buffer.GetReadOffset(), compressedLength, uncompressedLength.GetValue());
|
||||||
|
}
|
||||||
23
src/Common/Compression.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file Compression.h
|
||||||
|
* \brief File containing compress utilities
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DataBuffer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Compress some data
|
||||||
|
* \param buffer the data to compress
|
||||||
|
* \return the compressed data
|
||||||
|
*/
|
||||||
|
DataBuffer Compress(const DataBuffer& buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Uncompress some data
|
||||||
|
* \param buffer the data to uncompress
|
||||||
|
* \param packetLength lenght of data
|
||||||
|
* \return the uncompressed data
|
||||||
|
*/
|
||||||
|
DataBuffer Decompress(DataBuffer& buffer, std::uint64_t packetLength);
|
||||||
98
src/Common/DataBuffer.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "DataBuffer.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(std::size_t initalCapacity) : m_ReadOffset(0) {
|
||||||
|
m_Buffer.reserve(initalCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer() : m_ReadOffset(0) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const DataBuffer& other) : m_Buffer(other.m_Buffer), m_ReadOffset(other.m_ReadOffset) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(DataBuffer&& other) : m_Buffer(std::move(other.m_Buffer)), m_ReadOffset(std::move(other.m_ReadOffset)) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const std::string& str) : m_Buffer(str.begin(), str.end()), m_ReadOffset(0) {}
|
||||||
|
|
||||||
|
DataBuffer::DataBuffer(const DataBuffer& other, Data::difference_type offset) : m_ReadOffset(0) {
|
||||||
|
m_Buffer.reserve(other.GetSize() - static_cast<std::size_t>(offset));
|
||||||
|
std::copy(other.m_Buffer.begin() + offset, other.m_Buffer.end(), std::back_inserter(m_Buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& DataBuffer::operator=(const DataBuffer& other) {
|
||||||
|
m_Buffer = other.m_Buffer;
|
||||||
|
m_ReadOffset = other.m_ReadOffset;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& DataBuffer::operator=(DataBuffer&& other) {
|
||||||
|
m_Buffer = std::move(other.m_Buffer);
|
||||||
|
m_ReadOffset = std::move(other.m_ReadOffset);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataBuffer::SetReadOffset(std::size_t pos) {
|
||||||
|
assert(pos <= GetSize());
|
||||||
|
m_ReadOffset = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t DataBuffer::GetSize() const {
|
||||||
|
return m_Buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t DataBuffer::GetRemaining() const {
|
||||||
|
return m_Buffer.size() - m_ReadOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::iterator DataBuffer::begin() {
|
||||||
|
return m_Buffer.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::iterator DataBuffer::end() {
|
||||||
|
return m_Buffer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::const_iterator DataBuffer::begin() const {
|
||||||
|
return m_Buffer.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer::const_iterator DataBuffer::end() const {
|
||||||
|
return m_Buffer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer) {
|
||||||
|
for (unsigned char u : buffer)
|
||||||
|
os << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(u) << " ";
|
||||||
|
os << std::dec;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataBuffer::ReadFile(const std::string& fileName) {
|
||||||
|
try {
|
||||||
|
std::ifstream file(fileName, std::istream::binary);
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << file.rdbuf();
|
||||||
|
const std::string& s = ss.str();
|
||||||
|
m_Buffer = DataBuffer::Data(s.begin(), s.end());
|
||||||
|
m_ReadOffset = 0;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "Failed to read file : " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return m_Buffer.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataBuffer::WriteFile(const std::string& fileName) const {
|
||||||
|
try {
|
||||||
|
std::ofstream file(fileName, std::ostream::binary);
|
||||||
|
file.write(reinterpret_cast<const char*>(m_Buffer.data()), static_cast<std::streamsize>(m_Buffer.size()));
|
||||||
|
file.flush();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "Failed to write file : " << e.what() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
284
src/Common/DataBuffer.h
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file DataBuffer.h
|
||||||
|
* \brief File containing the blitz::DataBuffer class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class DataBuffer
|
||||||
|
* \brief Class used to manipulate memory
|
||||||
|
*/
|
||||||
|
class DataBuffer {
|
||||||
|
private:
|
||||||
|
typedef std::vector<std::uint8_t> Data;
|
||||||
|
Data m_Buffer;
|
||||||
|
std::size_t m_ReadOffset;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Data::iterator iterator;
|
||||||
|
typedef Data::const_iterator const_iterator;
|
||||||
|
typedef Data::reference reference;
|
||||||
|
typedef Data::const_reference const_reference;
|
||||||
|
typedef Data::difference_type difference_type;
|
||||||
|
|
||||||
|
DataBuffer();
|
||||||
|
DataBuffer(std::size_t initalCapacity);
|
||||||
|
DataBuffer(const DataBuffer& other);
|
||||||
|
DataBuffer(const DataBuffer& other, difference_type offset);
|
||||||
|
DataBuffer(DataBuffer&& other);
|
||||||
|
DataBuffer(const std::string& str);
|
||||||
|
|
||||||
|
DataBuffer& operator=(const DataBuffer& other);
|
||||||
|
DataBuffer& operator=(DataBuffer&& other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Append(const T& data) {
|
||||||
|
std::size_t size = sizeof(data);
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + size);
|
||||||
|
std::memcpy(&m_Buffer[end_pos], &data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
DataBuffer& operator<<(const T& data) {
|
||||||
|
Append(data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append a string to the buffer
|
||||||
|
* \warning Don't use it for binary data !
|
||||||
|
* \param str The string to append
|
||||||
|
*/
|
||||||
|
DataBuffer& operator<<(const std::string& str) {
|
||||||
|
std::size_t strlen = str.length() + 1; // including null character
|
||||||
|
Resize(GetSize() + strlen);
|
||||||
|
std::memcpy(m_Buffer.data() + GetSize() - strlen, str.data(), strlen);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Append data to the buffer from another const buffer
|
||||||
|
* \param data The buffer to append
|
||||||
|
*/
|
||||||
|
DataBuffer& operator<<(const DataBuffer& data) {
|
||||||
|
m_Buffer.insert(m_Buffer.end(), data.begin(), data.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer and assign to desired variable
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
DataBuffer& operator>>(T& data) {
|
||||||
|
assert(m_ReadOffset + sizeof(T) <= GetSize());
|
||||||
|
data = *(reinterpret_cast<T*>(&m_Buffer[m_ReadOffset]));
|
||||||
|
m_ReadOffset += sizeof(T);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer and assign to the new buffer
|
||||||
|
* \param data The buffer to assign
|
||||||
|
*/
|
||||||
|
DataBuffer& operator>>(DataBuffer& data) {
|
||||||
|
data.Resize(GetSize() - m_ReadOffset);
|
||||||
|
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), m_Buffer.end(), data.begin());
|
||||||
|
m_ReadOffset = m_Buffer.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read a string from the buffer
|
||||||
|
* \param str The string to assign in the new buffer
|
||||||
|
* \warning Don't use it for binary data !
|
||||||
|
*/
|
||||||
|
DataBuffer& operator>>(std::string& str) {
|
||||||
|
std::size_t stringSize =
|
||||||
|
strlen(reinterpret_cast<const char*>(m_Buffer.data()) + m_ReadOffset) + 1; // including null character
|
||||||
|
str.resize(stringSize);
|
||||||
|
std::copy(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset),
|
||||||
|
m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset + stringSize), str.begin());
|
||||||
|
m_ReadOffset += stringSize;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write some data to the buffer
|
||||||
|
* \param buffer The buffer to write
|
||||||
|
* \param amount The amount of data to write
|
||||||
|
*/
|
||||||
|
void WriteSome(const char* buffer, std::size_t amount) {
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + amount);
|
||||||
|
std::memcpy(m_Buffer.data() + end_pos, buffer, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write some data to the buffer
|
||||||
|
* \param buffer The buffer to write
|
||||||
|
* \param amount The amount of data to write
|
||||||
|
*/
|
||||||
|
void WriteSome(const std::uint8_t* buffer, std::size_t amount) {
|
||||||
|
std::size_t end_pos = m_Buffer.size();
|
||||||
|
m_Buffer.resize(m_Buffer.size() + amount);
|
||||||
|
std::memcpy(m_Buffer.data() + end_pos, buffer, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(char* buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer);
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(std::uint8_t* buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer);
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read some data from the buffer
|
||||||
|
* \param buffer The buffer to Read
|
||||||
|
* \param amount The amount of data from the buffer
|
||||||
|
*/
|
||||||
|
void ReadSome(DataBuffer& buffer, std::size_t amount) {
|
||||||
|
assert(m_ReadOffset + amount <= GetSize());
|
||||||
|
buffer.Resize(amount);
|
||||||
|
std::copy_n(m_Buffer.begin() + static_cast<difference_type>(m_ReadOffset), amount, buffer.begin());
|
||||||
|
m_ReadOffset += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resize the buffer
|
||||||
|
* \param size The new size of the buffer
|
||||||
|
*/
|
||||||
|
void Resize(std::size_t size) {
|
||||||
|
m_Buffer.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Reserve some space in the buffer
|
||||||
|
* \param amount The amount of space to reserve
|
||||||
|
*/
|
||||||
|
void Reserve(std::size_t amount) {
|
||||||
|
m_Buffer.reserve(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Clear the buffer
|
||||||
|
*/
|
||||||
|
void Clear() {
|
||||||
|
m_Buffer.clear();
|
||||||
|
m_ReadOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief When the buffer has been read entirely
|
||||||
|
*/
|
||||||
|
bool IsFinished() const {
|
||||||
|
return m_ReadOffset >= m_Buffer.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the buffer data
|
||||||
|
*/
|
||||||
|
std::uint8_t* data() {
|
||||||
|
return m_Buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the buffer data
|
||||||
|
*/
|
||||||
|
const std::uint8_t* data() const {
|
||||||
|
return m_Buffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the read offset
|
||||||
|
*/
|
||||||
|
std::size_t GetReadOffset() const {
|
||||||
|
return m_ReadOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Set the read offset
|
||||||
|
* \param pos The new read offset
|
||||||
|
*/
|
||||||
|
void SetReadOffset(std::size_t pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the size of the buffer
|
||||||
|
*/
|
||||||
|
std::size_t GetSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the remaining size of the buffer
|
||||||
|
*/
|
||||||
|
std::size_t GetRemaining() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Read a file into the buffer
|
||||||
|
* \param fileName The name of the file to read
|
||||||
|
*/
|
||||||
|
bool ReadFile(const std::string& fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Write a file into the buffer
|
||||||
|
* \param fileName The name of the file to write to
|
||||||
|
*/
|
||||||
|
bool WriteFile(const std::string& fileName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Allocate the buffer on the heap
|
||||||
|
* \warning Don't forget to free the data !
|
||||||
|
*/
|
||||||
|
std::uint8_t* HeapAllocatedData() const {
|
||||||
|
std::uint8_t* newBuffer = new std::uint8_t[GetSize()];
|
||||||
|
std::memcpy(newBuffer, data(), GetSize());
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operator == to compare two DataBuffer
|
||||||
|
*/
|
||||||
|
bool operator==(const DataBuffer& other) const {
|
||||||
|
return m_Buffer == other.m_Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Operator << to write a DataBuffer to an ostream
|
||||||
|
*/
|
||||||
|
std::ostream& operator<<(std::ostream& os, const DataBuffer& buffer);
|
||||||
|
|
||||||
48
src/Common/VarInt.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "VarInt.h"
|
||||||
|
|
||||||
|
#include "DataBuffer.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
static constexpr int SEGMENT_BITS = 0x7F;
|
||||||
|
static constexpr int CONTINUE_BIT = 0x80;
|
||||||
|
|
||||||
|
std::size_t VarInt::GetSerializedLength() const {
|
||||||
|
DataBuffer buffer;
|
||||||
|
buffer << *this;
|
||||||
|
return buffer.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& operator<<(DataBuffer& out, const VarInt& var) {
|
||||||
|
std::uint64_t value = var.m_Value;
|
||||||
|
while (true) {
|
||||||
|
if ((value & ~static_cast<std::uint64_t>(SEGMENT_BITS)) == 0) {
|
||||||
|
out << static_cast<std::uint8_t>(value);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << static_cast<std::uint8_t>((value & SEGMENT_BITS) | CONTINUE_BIT);
|
||||||
|
|
||||||
|
value >>= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
|
||||||
|
var.m_Value = 0;
|
||||||
|
unsigned int position = 0;
|
||||||
|
std::uint8_t currentByte;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
in.ReadSome(¤tByte, 1);
|
||||||
|
var.m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;
|
||||||
|
|
||||||
|
if ((currentByte & CONTINUE_BIT) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
position += 7;
|
||||||
|
|
||||||
|
if (position >= 8 * sizeof(var.m_Value))
|
||||||
|
throw std::runtime_error("VarInt is too big");
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
54
src/Common/VarInt.h
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file VarInt.h
|
||||||
|
* \brief File containing the blitz::VarInt class
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class DataBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class VarInt
|
||||||
|
* \brief Variable-length format such that smaller numbers use fewer bytes.
|
||||||
|
*/
|
||||||
|
class VarInt {
|
||||||
|
private:
|
||||||
|
std::uint64_t m_Value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VarInt() : m_Value(0) {}
|
||||||
|
/**
|
||||||
|
* \brief Construct a variable integer from a value
|
||||||
|
* \param value The value of the variable integer
|
||||||
|
*/
|
||||||
|
VarInt(std::uint64_t value) : m_Value(value) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the value of the variable integer
|
||||||
|
*/
|
||||||
|
std::uint64_t GetValue() const {
|
||||||
|
return m_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Get the length of the serialized variable integer
|
||||||
|
*/
|
||||||
|
std::size_t GetSerializedLength() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Serialize the variable integer
|
||||||
|
* \param out The buffer to write the serialized variable integer to
|
||||||
|
* \param var The variable integer to serialize
|
||||||
|
*/
|
||||||
|
friend DataBuffer& operator<<(DataBuffer& out, const VarInt& var);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Deserialize the variable integer
|
||||||
|
* \param in The buffer to read the serialized variable integer from
|
||||||
|
* \param var The variable integer to deserialize
|
||||||
|
*/
|
||||||
|
friend DataBuffer& operator>>(DataBuffer& in, VarInt& var);
|
||||||
|
};
|
||||||
@@ -7,25 +7,53 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
static const double SMALLEST_CONSIDERED_PROPORTION = 0.01; // the smallest a proportion can get before it is considered equal to 0
|
||||||
|
|
||||||
|
|
||||||
Bag::Bag(const std::shared_ptr<PiecesList>& piecesList) :
|
Bag::Bag(const std::shared_ptr<PiecesList>& piecesList) :
|
||||||
piecesList(piecesList) {
|
piecesList(piecesList) {
|
||||||
|
|
||||||
this->currentBag = this->piecesList->getSelectedPieces();
|
this->highestSize = this->piecesList->getHighestLoadedSize();
|
||||||
this->nextBag.clear();
|
this->selectedPieces = this->piecesList->getSelectedPieces();
|
||||||
|
this->distributionMode = this->piecesList->getDistributionMode();
|
||||||
|
this->propotionsPerSize = this->piecesList->getProportionsPerSize();
|
||||||
|
|
||||||
|
this->currentBags.clear();
|
||||||
|
this->nextBags.clear();
|
||||||
|
this->sizesBag.clear();
|
||||||
|
this->sizesProgression.clear();
|
||||||
|
|
||||||
|
if (this->distributionMode == DEFAULT) {
|
||||||
|
this->currentBags.push_back(this->selectedPieces);
|
||||||
|
this->nextBags.push_back({});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 0; i <= this->highestSize; i++) {
|
||||||
|
this->currentBags.push_back(PieceBag());
|
||||||
|
this->nextBags.push_back(PieceBag());
|
||||||
|
this->sizesProgression.push_back(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& piece : this->selectedPieces) {
|
||||||
|
int pieceSize = this->piecesList->lookAtPiece(piece).getPositions().getSize();
|
||||||
|
this->currentBags.at(pieceSize).push_back(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->prepareNext();
|
this->prepareNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bag::jumpToNextBag() {
|
void Bag::jumpToNextBag() {
|
||||||
if (this->currentBag.size() < this->nextBag.size()) {
|
for (int i = 0; i < this->currentBags.size(); i++) {
|
||||||
std::swap(this->currentBag, this->nextBag);
|
if (this->currentBags.at(i).size() < this->nextBags.at(i).size()) {
|
||||||
|
std::swap(this->currentBags.at(i), this->nextBags.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::pair<int, int>& pieceIndex : this->nextBag) {
|
for (const auto& piece : this->nextBags.at(i)) {
|
||||||
this->currentBag.push_back(pieceIndex);
|
this->currentBags.at(i).push_back(piece);
|
||||||
|
}
|
||||||
|
this->nextBags.at(i).clear();
|
||||||
}
|
}
|
||||||
this->nextBag.clear();
|
|
||||||
|
|
||||||
this->prepareNext();
|
this->prepareNext();
|
||||||
}
|
}
|
||||||
@@ -43,13 +71,40 @@ Piece Bag::getNext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bag::prepareNext() {
|
void Bag::prepareNext() {
|
||||||
if (this->currentBag.empty()) {
|
if (this->distributionMode == DEFAULT) {
|
||||||
std::swap(this->currentBag, this->nextBag);
|
this->getNextPieceFromBag(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this->sizesBag.empty()) {
|
||||||
|
for (int i = 0; i <= this->highestSize; i++) {
|
||||||
|
if (this->propotionsPerSize.at(i) >= SMALLEST_CONSIDERED_PROPORTION
|
||||||
|
&& !(this->currentBags.at(i).empty() && this->nextBags.at(i).empty())) {
|
||||||
|
while (this->sizesProgression.at(i) < 1) {
|
||||||
|
this->sizesBag.push_back(i);
|
||||||
|
this->sizesProgression.at(i) += this->propotionsPerSize.at(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
int indexIndex = std::rand() % this->currentBag.size();
|
this->sizesProgression.at(i) -= 1;
|
||||||
this->next = this->currentBag.at(indexIndex);
|
}
|
||||||
|
}
|
||||||
this->nextBag.push_back(this->next);
|
}
|
||||||
this->currentBag.erase(this->currentBag.begin() + indexIndex);
|
|
||||||
|
int nextSizeIndex = std::rand() % this->sizesBag.size();
|
||||||
|
int nextSize = this->sizesBag.at(nextSizeIndex);
|
||||||
|
this->sizesBag.erase(this->sizesBag.begin() + nextSizeIndex);
|
||||||
|
|
||||||
|
this->getNextPieceFromBag(nextSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bag::getNextPieceFromBag(int bagIndex) {
|
||||||
|
if (this->currentBags.at(bagIndex).empty()) {
|
||||||
|
std::swap(this->currentBags.at(bagIndex), this->nextBags.at(bagIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
int indexIndex = std::rand() % this->currentBags.at(bagIndex).size();
|
||||||
|
this->next = this->currentBags.at(bagIndex).at(indexIndex);
|
||||||
|
|
||||||
|
this->nextBags.at(bagIndex).push_back(this->next);
|
||||||
|
this->currentBags.at(bagIndex).erase(this->currentBags.at(bagIndex).begin() + indexIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
#include "../Pieces/Piece.h"
|
#include "../Pieces/Piece.h"
|
||||||
#include "PiecesList.h"
|
#include "PiecesList.h"
|
||||||
|
#include "DistributionMode.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
using PieceBag = std::vector<std::pair<int, int>>;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A litteral bag of pieces, in which you take each of its piece randomly one by one then start again with a new bag
|
* A litteral bag of pieces, in which you take each of its piece randomly one by one then start again with a new bag
|
||||||
@@ -14,10 +17,15 @@
|
|||||||
class Bag {
|
class Bag {
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<PiecesList> piecesList; // the list of loaded pieces
|
std::shared_ptr<PiecesList> piecesList; // the list of loaded pieces
|
||||||
|
int highestSize; // the highest size of piece in the bag
|
||||||
std::vector<std::pair<int, int>> selectedPieces; // the list of pieces that can be given to the player
|
std::vector<std::pair<int, int>> selectedPieces; // the list of pieces that can be given to the player
|
||||||
|
DistributionMode distributionMode; // the distribution mode
|
||||||
|
std::vector<double> propotionsPerSize; // the proportion of pieces for each size
|
||||||
std::pair<int, int> next; // the next piece to give
|
std::pair<int, int> next; // the next piece to give
|
||||||
std::vector<std::pair<int, int>> currentBag; // the list of pieces that are still to be taken out before starting a new bag
|
std::vector<PieceBag> currentBags; // for each size, the list of pieces that are still to be taken out before starting a new bag
|
||||||
std::vector<std::pair<int, int>> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
|
std::vector<PieceBag> nextBags; // for each size, the list of pieces that have been taken out of the current bag and have been placed in the next
|
||||||
|
std::vector<int> sizesBag; // the list each of bags that are still to have a piece taken out of them
|
||||||
|
std::vector<double> sizesProgression; // how close each size is to meet its quota of pieces
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -26,7 +34,7 @@ class Bag {
|
|||||||
Bag(const std::shared_ptr<PiecesList>& piecesList);
|
Bag(const std::shared_ptr<PiecesList>& piecesList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ignores the remaining pieces in the current bag and startd fresh from a new bag
|
* Ignores the remaining pieces in the current bag and start fresh from a new bag
|
||||||
*/
|
*/
|
||||||
void jumpToNextBag();
|
void jumpToNextBag();
|
||||||
|
|
||||||
@@ -44,7 +52,12 @@ class Bag {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Prepare the next picked piece in advance
|
* Prepares the next picked piece in advance
|
||||||
*/
|
*/
|
||||||
void prepareNext();
|
void prepareNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next picked piece from the specified bag
|
||||||
|
*/
|
||||||
|
void getNextPieceFromBag(int bagIndex);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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:
|
||||||
/**
|
/**
|
||||||
|
|||||||
27
src/Core/DistributionMode.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modes of pieces distribution managed by the app
|
||||||
|
*/
|
||||||
|
enum DistributionMode {
|
||||||
|
DEFAULT,
|
||||||
|
UNIFORM,
|
||||||
|
CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A string containing the name of the given distribution mode
|
||||||
|
*/
|
||||||
|
inline std::string getPiecesDistributionName(DistributionMode distributionMode) {
|
||||||
|
static const std::string DISTRIBUTION_NAMES[] = {
|
||||||
|
"DEFAULT",
|
||||||
|
"UNIFORM",
|
||||||
|
"CUSTOM"
|
||||||
|
};
|
||||||
|
|
||||||
|
return DISTRIBUTION_NAMES[distributionMode];
|
||||||
|
}
|
||||||
@@ -67,27 +67,29 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
if (AREJustEnded) {
|
if (AREJustEnded) {
|
||||||
this->lost = this->board.spawnNextPiece();
|
this->lost = this->board.spawnNextPiece();
|
||||||
this->resetPiece(true);
|
this->resetPiece(true);
|
||||||
}
|
|
||||||
|
|
||||||
/* IRS and IHS */
|
/* IRS and IHS */
|
||||||
|
bool initialRotated = (this->initialActions.contains(ROTATE_0) || this->initialActions.contains(ROTATE_CW)
|
||||||
|
|| this->initialActions.contains(ROTATE_180) || this->initialActions.contains(ROTATE_CCW));
|
||||||
Rotation initialRotation = NONE
|
Rotation initialRotation = NONE
|
||||||
+ ((this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE)
|
+ ((this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE)
|
||||||
+ ((this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE)
|
+ ((this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE)
|
||||||
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
|
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
|
||||||
|
|
||||||
if (this->initialActions.contains(HOLD)) {
|
if (this->initialActions.contains(HOLD)) {
|
||||||
this->lost = (!this->board.hold(initialRotation));
|
this->board.hold(initialRotation);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ((initialRotation != NONE) || this->initialActions.contains(ROTATE_0)) {
|
if (initialRotated) {
|
||||||
this->lost = (!this->board.rotate(initialRotation));
|
this->board.rotate(initialRotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->lost = this->board.activePieceInWall();
|
||||||
if (this->lost) {
|
if (this->lost) {
|
||||||
if (initialRotation == NONE) {
|
|
||||||
this->board.rotate(NONE);
|
this->board.rotate(NONE);
|
||||||
this->lost = this->board.activePieceInWall();
|
this->lost = this->board.activePieceInWall();
|
||||||
|
|
||||||
if (this->lost) {
|
if (this->lost) {
|
||||||
this->framesPassed++;
|
this->framesPassed++;
|
||||||
return;
|
return;
|
||||||
@@ -97,7 +99,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
|||||||
|
|
||||||
/* HOLD */
|
/* HOLD */
|
||||||
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
||||||
if (this->board.hold()) {
|
if (this->board.hold({})) {
|
||||||
this->resetPiece(false);
|
this->resetPiece(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,8 +297,8 @@ void Game::lockPiece() {
|
|||||||
|
|
||||||
/* clearing one more line is worth 2x more
|
/* clearing one more line is worth 2x more
|
||||||
clearing with a spin is worth as much as clearing 2x more lines */
|
clearing with a spin is worth as much as clearing 2x more lines */
|
||||||
long int clearScore = LINE_CLEAR_BASE_SCORE;
|
long int clearScore = LINE_CLEAR_BASE_SCORE / 2;
|
||||||
clearScore = clearScore << (clear.lines << (clear.isSpin));
|
clearScore = clearScore << (clear.lines << clear.isSpin);
|
||||||
|
|
||||||
if (this->B2BChain && B2BConditionsAreMet) {
|
if (this->B2BChain && B2BConditionsAreMet) {
|
||||||
clearScore *= B2B_SCORE_MULTIPLIER;
|
clearScore *= B2B_SCORE_MULTIPLIER;
|
||||||
@@ -364,6 +366,10 @@ 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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,11 @@ class Game {
|
|||||||
*/
|
*/
|
||||||
bool areBlocksBones() const;
|
bool areBlocksBones() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If the board is currently invisible
|
||||||
|
*/
|
||||||
|
bool isBoardInvisible() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The board
|
* @return The board
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::shared_ptr<PiecesList>& piecesList, int nextQueueLength) :
|
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::shared_ptr<PiecesList>& piecesList, int nextQueueLength) :
|
||||||
@@ -42,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 {
|
||||||
@@ -55,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 {
|
||||||
@@ -66,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 {
|
||||||
@@ -98,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
|
||||||
@@ -145,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,12 +195,12 @@ bool GameBoard::activePieceOverlaps(const std::set<Position>& safePositions, con
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameBoard::hold(Rotation initialRotation) {
|
bool GameBoard::hold(std::optional<Rotation> initialRotation) {
|
||||||
|
Position storedPosition = this->activePiecePosition;
|
||||||
std::swap(this->activePiece, this->heldPiece);
|
std::swap(this->activePiece, this->heldPiece);
|
||||||
|
|
||||||
bool isFirstTimeHolding = (this->activePiece == nullptr);
|
bool isFirstTimeHolding = (this->activePiece == nullptr);
|
||||||
if (isFirstTimeHolding) {
|
if (isFirstTimeHolding) {
|
||||||
// try with the next piece in queue since there is no piece in the hold box yet
|
|
||||||
if (this->nextQueueLength == 0) {
|
if (this->nextQueueLength == 0) {
|
||||||
this->activePiece = std::make_shared<Piece>(this->generator.lookNext());
|
this->activePiece = std::make_shared<Piece>(this->generator.lookNext());
|
||||||
}
|
}
|
||||||
@@ -207,21 +209,25 @@ bool GameBoard::hold(Rotation initialRotation) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Piece stored = *this->activePiece;
|
// try initial rotation
|
||||||
Position storedPosition = this->activePiecePosition;
|
|
||||||
this->goToSpawnPosition();
|
this->goToSpawnPosition();
|
||||||
this->rotate(initialRotation);
|
if (initialRotation.has_value()) {
|
||||||
|
std::shared_ptr<Piece> storedPiece(this->activePiece);
|
||||||
|
this->rotate(initialRotation.value());
|
||||||
|
|
||||||
// if the piece can't spawn, abort initial rotation
|
|
||||||
if (this->activePieceInWall()) {
|
if (this->activePieceInWall()) {
|
||||||
this->activePiece = std::make_shared<Piece>(stored);
|
this->activePiece = storedPiece;
|
||||||
this->goToSpawnPosition();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the piece can't spawn, try 0° rotation
|
||||||
|
if (this->activePieceInWall()) {
|
||||||
|
std::shared_ptr<Piece> storedPiece(this->activePiece);
|
||||||
|
this->rotate(NONE);
|
||||||
|
|
||||||
// if the piece still can't spawn, abort holding
|
// if the piece still can't spawn, abort holding
|
||||||
if (this->activePieceInWall()) {
|
if (this->activePieceInWall()) {
|
||||||
if (isFirstTimeHolding) {
|
this->activePiece = (isFirstTimeHolding) ? nullptr : storedPiece;
|
||||||
this->activePiece = nullptr;
|
|
||||||
}
|
|
||||||
std::swap(this->activePiece, this->heldPiece);
|
std::swap(this->activePiece, this->heldPiece);
|
||||||
this->activePiecePosition = storedPosition;
|
this->activePiecePosition = storedPosition;
|
||||||
return false;
|
return false;
|
||||||
@@ -229,7 +235,6 @@ bool GameBoard::hold(Rotation initialRotation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isFirstTimeHolding) {
|
if (isFirstTimeHolding) {
|
||||||
// confirm we keep the piece we tried with
|
|
||||||
this->nextQueue.push_back(this->generator.getNext());
|
this->nextQueue.push_back(this->generator.getNext());
|
||||||
this->nextQueue.erase(this->nextQueue.begin());
|
this->nextQueue.erase(this->nextQueue.begin());
|
||||||
}
|
}
|
||||||
@@ -260,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;
|
||||||
}
|
}
|
||||||
@@ -273,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());
|
||||||
@@ -340,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 << "*";
|
||||||
}
|
}
|
||||||
@@ -356,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) {
|
||||||
@@ -364,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 << "*";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,14 +84,14 @@ 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:
|
||||||
/**
|
/**
|
||||||
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
|
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
|
||||||
* @return If it suceeded
|
* @return If it suceeded
|
||||||
*/
|
*/
|
||||||
bool hold(Rotation initialRotation = NONE);
|
bool hold(std::optional<Rotation> initialRotation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spawns the next piece from the queue
|
* Spawns the next piece from the queue
|
||||||
@@ -102,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
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ 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;
|
||||||
@@ -34,7 +36,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
|
// modes where level increases with lines
|
||||||
case MARATHON :
|
case MARATHON :
|
||||||
case MASTER : {
|
case MASTER : {
|
||||||
int previousLines = this->clearedLines;
|
int previousLines = this->clearedLines;
|
||||||
@@ -51,9 +53,23 @@ 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 {
|
||||||
@@ -66,6 +82,8 @@ 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;
|
||||||
@@ -84,7 +102,8 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -94,10 +113,13 @@ 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);
|
case MASTER : {this->boneBlocks = (this->level >= 30); break;}
|
||||||
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[] = {
|
||||||
@@ -152,6 +174,8 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -228,6 +252,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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
|
||||||
@@ -77,10 +78,15 @@ class GameParameters {
|
|||||||
int getNextQueueLength() const;
|
int getNextQueueLength() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns wheter the blocks are currently bone blocks
|
* @return 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
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ enum Gamemode {
|
|||||||
MARATHON,
|
MARATHON,
|
||||||
ULTRA,
|
ULTRA,
|
||||||
MASTER,
|
MASTER,
|
||||||
|
INVISIBLE,
|
||||||
ZEN
|
ZEN
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ inline std::string getGamemodeName(Gamemode gamemode) {
|
|||||||
"MARATHON",
|
"MARATHON",
|
||||||
"ULTRA",
|
"ULTRA",
|
||||||
"MASTER",
|
"MASTER",
|
||||||
|
"INVISIBLE",
|
||||||
"ZEN"
|
"ZEN"
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,7 +41,8 @@ inline std::string getGamemodeGoal(Gamemode gamemode) {
|
|||||||
"200 lines",
|
"200 lines",
|
||||||
"2 minutes",
|
"2 minutes",
|
||||||
"200 lines",
|
"200 lines",
|
||||||
"Chill"
|
"1000 grade",
|
||||||
|
"Infinite"
|
||||||
};
|
};
|
||||||
|
|
||||||
return GAMEMODE_DESCRIPTIONS[gamemode];
|
return GAMEMODE_DESCRIPTIONS[gamemode];
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "../Pieces/Piece.h"
|
#include "../Pieces/Piece.h"
|
||||||
#include "../Pieces/PiecesFiles.h"
|
#include "../Pieces/PiecesFiles.h"
|
||||||
|
#include "DistributionMode.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -11,6 +12,10 @@ PiecesList::PiecesList() {
|
|||||||
this->highestLoadedSize = 0;
|
this->highestLoadedSize = 0;
|
||||||
this->selectedPieces.clear();
|
this->selectedPieces.clear();
|
||||||
|
|
||||||
|
this->distributionMode = DEFAULT;
|
||||||
|
this->proportionsPerSize.clear();
|
||||||
|
this->customProportionsPerSize.clear();
|
||||||
|
|
||||||
// we need to have something at index 0 even if there is no pieces of size 0
|
// we need to have something at index 0 even if there is no pieces of size 0
|
||||||
this->loadedPieces.clear();
|
this->loadedPieces.clear();
|
||||||
this->convexPieces.clear();
|
this->convexPieces.clear();
|
||||||
@@ -22,12 +27,12 @@ PiecesList::PiecesList() {
|
|||||||
this->pushBackEmptyVectors();
|
this->pushBackEmptyVectors();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PiecesList::loadPieces(int size) {
|
bool PiecesList::loadPieces(unsigned max_size) {
|
||||||
if (size < 1) return false;
|
if (max_size < 1) return false;
|
||||||
|
if (max_size <= this->highestLoadedSize) return true;
|
||||||
|
|
||||||
PiecesFiles piecesFiles;
|
PiecesFiles piecesFiles;
|
||||||
for (int i = this->highestLoadedSize + 1; i <= 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;
|
||||||
}
|
}
|
||||||
@@ -40,14 +45,14 @@ bool PiecesList::loadPieces(int 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++) {
|
||||||
@@ -87,6 +92,14 @@ void PiecesList::unselectAll() {
|
|||||||
this->selectedPieces.clear();
|
this->selectedPieces.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PiecesList::setDistributionMode(DistributionMode distributionMode) {
|
||||||
|
if (distributionMode == DEFAULT || distributionMode == UNIFORM || distributionMode == CUSTOM) {
|
||||||
|
this->distributionMode = distributionMode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int PiecesList::getHighestLoadedSize() const {
|
int PiecesList::getHighestLoadedSize() const {
|
||||||
return this->highestLoadedSize;
|
return this->highestLoadedSize;
|
||||||
}
|
}
|
||||||
@@ -101,13 +114,42 @@ std::vector<std::pair<int, int>> PiecesList::getSelectedPieces() const {
|
|||||||
return this->selectedPieces;
|
return this->selectedPieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DistributionMode PiecesList::getDistributionMode() const {
|
||||||
|
return this->distributionMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PiecesList::changeCustomDistribution(int size, double distribution) {
|
||||||
|
if (size < 1 || size > this->highestLoadedSize || distribution > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->customProportionsPerSize.at(size) = distribution;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> PiecesList::getProportionsPerSize() const {
|
||||||
|
if (this->distributionMode == CUSTOM) {
|
||||||
|
return this->customProportionsPerSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this->proportionsPerSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Piece PiecesList::getPiece(const std::pair<int, int>& pieceIndex) const {
|
Piece PiecesList::getPiece(const std::pair<int, int>& pieceIndex) const {
|
||||||
return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
|
return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Piece& PiecesList::lookAtPiece(const std::pair<int, int>& pieceIndex) const {
|
||||||
|
return this->loadedPieces.at(pieceIndex.first).at(pieceIndex.second);
|
||||||
|
}
|
||||||
|
|
||||||
void PiecesList::pushBackEmptyVectors() {
|
void PiecesList::pushBackEmptyVectors() {
|
||||||
this->loadedPieces.push_back(std::vector<Piece>());
|
this->loadedPieces.push_back(std::vector<Piece>());
|
||||||
this->convexPieces.push_back(std::vector<int>());
|
this->convexPieces.push_back(std::vector<int>());
|
||||||
this->holelessPieces.push_back(std::vector<int>());
|
this->holelessPieces.push_back(std::vector<int>());
|
||||||
this->otherPieces.push_back(std::vector<int>());
|
this->otherPieces.push_back(std::vector<int>());
|
||||||
|
|
||||||
|
this->proportionsPerSize.push_back(1);
|
||||||
|
this->customProportionsPerSize.push_back(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../Pieces/Piece.h"
|
#include "../Pieces/Piece.h"
|
||||||
|
#include "DistributionMode.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -12,12 +13,15 @@
|
|||||||
*/
|
*/
|
||||||
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
|
||||||
std::vector<std::vector<int>> otherPieces; // the list of other loaded pieces by size
|
std::vector<std::vector<int>> otherPieces; // the list of other loaded pieces by size
|
||||||
std::vector<std::pair<int, int>> selectedPieces; // the list of all currently selected pieces
|
std::vector<std::pair<int, int>> selectedPieces; // the list of all currently selected pieces
|
||||||
|
DistributionMode distributionMode; // the current pieces distribution mode
|
||||||
|
std::vector<double> proportionsPerSize; // the proportion of piece for each sizes
|
||||||
|
std::vector<double> customProportionsPerSize; // the proportion of piece for each sizes when the distribution mode is set to custom
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
@@ -26,22 +30,22 @@ class PiecesList {
|
|||||||
PiecesList();
|
PiecesList();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the list load all pieces of the specified size
|
* Makes the list load all pieces up to the specified size
|
||||||
* @return If it sucessfully loaded the pieces
|
* @return If all pieces up to the specified size are correctly loaded
|
||||||
*/
|
*/
|
||||||
bool loadPieces(int 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
|
||||||
@@ -66,6 +70,19 @@ class PiecesList {
|
|||||||
*/
|
*/
|
||||||
void unselectAll();
|
void unselectAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the current pieces distribution mode
|
||||||
|
* @return If the mode is supported
|
||||||
|
*/
|
||||||
|
bool setDistributionMode(DistributionMode distributionMode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the distribution of the specified size for when the distribution mode is set to custom,
|
||||||
|
* the specified distribution must lower or equal to 1
|
||||||
|
* @return If the new distribution was applied
|
||||||
|
*/
|
||||||
|
bool changeCustomDistribution(int size, double distribution);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The highest loaded size of pieces
|
* @return The highest loaded size of pieces
|
||||||
*/
|
*/
|
||||||
@@ -81,11 +98,26 @@ class PiecesList {
|
|||||||
*/
|
*/
|
||||||
std::vector<std::pair<int, int>> getSelectedPieces() const;
|
std::vector<std::pair<int, int>> getSelectedPieces() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The current distribution mode
|
||||||
|
*/
|
||||||
|
DistributionMode getDistributionMode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The proportion of pieces for each loaded size
|
||||||
|
*/
|
||||||
|
std::vector<double> getProportionsPerSize() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A copy of the piece corresponding to the specified index
|
* @return A copy of the piece corresponding to the specified index
|
||||||
*/
|
*/
|
||||||
Piece getPiece(const std::pair<int, int>& pieceIndex) const;
|
Piece getPiece(const std::pair<int, int>& pieceIndex) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The piece corresponding to the specified index
|
||||||
|
*/
|
||||||
|
const Piece& lookAtPiece(const std::pair<int, int>& pieceIndex) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Adds empty vectors at the end of every pieces list
|
* Adds empty vectors at the end of every pieces list
|
||||||
|
|||||||
72
src/GraphicalUI/AppMenus/AppMenu.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "AppMenu.h"
|
||||||
|
|
||||||
|
#include "../Settings.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
#include "../../Utils/AssetManager.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::updateMetaBinds() {
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||||
|
this->enterPressed = true;
|
||||||
|
this->enterReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->enterReleased = this->enterPressed;
|
||||||
|
this->enterPressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
||||||
|
this->escPressed = true;
|
||||||
|
this->escReleased = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->escReleased = this->escPressed;
|
||||||
|
this->escPressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::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();
|
||||||
|
|
||||||
|
text.setString(string);
|
||||||
|
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||||
|
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
||||||
|
}
|
||||||
|
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppMenu::placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
||||||
|
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
||||||
|
|
||||||
|
text.setString(string);
|
||||||
|
if (playerCursor.has_value() && cursorPos.has_value()) {
|
||||||
|
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
||||||
|
}
|
||||||
|
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
||||||
|
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
||||||
|
this->renderWindow->draw(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Color AppMenu::getColorOfBlock(Block block, int luminosityShift) const {
|
||||||
|
Color rgbColor = BLOCKS_COLOR[block];
|
||||||
|
return sf::Color(std::clamp(rgbColor.red + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.green + luminosityShift, 0, 255),
|
||||||
|
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
||||||
|
}
|
||||||
@@ -21,63 +21,21 @@ 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;
|
||||||
|
|
||||||
virtual void drawFrame() const = 0;
|
virtual void drawFrame() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateMetaBinds() {
|
void updateMetaBinds();
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
|
||||||
enterPressed = true;
|
|
||||||
enterReleased = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
enterReleased = enterPressed;
|
|
||||||
enterPressed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
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;
|
||||||
escPressed = true;
|
|
||||||
escReleased = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
escReleased = escPressed;
|
|
||||||
escPressed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const;
|
||||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
|
||||||
|
|
||||||
text.setString(string);
|
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
|
||||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
|
||||||
}
|
|
||||||
text.setOrigin(sf::Vector2f({0, text.getLocalBounds().size.y / 2}));
|
|
||||||
text.setPosition(sf::Vector2f({sizeMultiplier * xPos, sizeMultiplier * yPos}));
|
|
||||||
this->renderWindow->draw(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const {
|
|
||||||
float sizeMultiplier = this->settings->getWindowSizeMultiplier();
|
|
||||||
|
|
||||||
text.setString(string);
|
|
||||||
if (playerCursor.has_value() && cursorPos.has_value()) {
|
|
||||||
text.setOutlineThickness((playerCursor.value().getPosition() == cursorPos.value()) ? (sizeMultiplier / 2) : 0);
|
|
||||||
}
|
|
||||||
text.setOrigin({text.getLocalBounds().getCenter().x, text.getLocalBounds().size.y / 2});
|
|
||||||
text.setPosition(sf::Vector2f({sizeMultiplier * 40.f, sizeMultiplier * yPos}));
|
|
||||||
this->renderWindow->draw(text);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
@@ -58,8 +57,6 @@ void GameBoardAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
this->placeTitle(text, {}, "BOARD SETTINGS", 5.f, {});
|
this->placeTitle(text, {}, "BOARD SETTINGS", 5.f, {});
|
||||||
|
|
||||||
sf::Vector2u windowSize = this->renderWindow->getSize();
|
|
||||||
|
|
||||||
this->placeText(text, this->playerCursor, "< BOARD WIDTH: " + std::to_string(menu.getBoardWidth()) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
this->placeText(text, this->playerCursor, "< BOARD WIDTH: " + std::to_string(menu.getBoardWidth()) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||||
this->placeText(text, this->playerCursor, "< BOARD HEIGHT: " + std::to_string(menu.getBoardHeight()) + " >", 5.f, 25.f, sf::Vector2u{0, 1});
|
this->placeText(text, this->playerCursor, "< BOARD HEIGHT: " + std::to_string(menu.getBoardHeight()) + " >", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||||
|
|
||||||
|
|||||||
85
src/GraphicalUI/AppMenus/GameDistributionAppMenu.cpp
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#include "GameDistributionAppMenu.h"
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
GameDistributionAppMenu::GameDistributionAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
playerCursor({1}) {
|
||||||
|
|
||||||
|
for (int i = 1; i <= this->settings->getLoadablePiecesSize(); i++) {
|
||||||
|
this->playerCursor.addRow(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameDistributionAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
this->playerCursor.updatePosition();
|
||||||
|
|
||||||
|
PiecesList& piecesList = this->settings->getMenu().getPiecesList();
|
||||||
|
|
||||||
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
|
if (this->playerCursor.movedLeft()) {
|
||||||
|
piecesList.setDistributionMode(DistributionMode((int) piecesList.getDistributionMode() - 1));
|
||||||
|
}
|
||||||
|
if (this->playerCursor.movedRight()) {
|
||||||
|
piecesList.setDistributionMode(DistributionMode((int) piecesList.getDistributionMode() + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (piecesList.getDistributionMode() != CUSTOM) {
|
||||||
|
this->playerCursor.goToPosition({0, 0});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (this->playerCursor.movedLeft()) {
|
||||||
|
this->settings->decreaseDistribution(this->playerCursor.getPosition().y);
|
||||||
|
}
|
||||||
|
if (this->playerCursor.movedRight()) {
|
||||||
|
this->settings->increaseDistribution(this->playerCursor.getPosition().y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->escReleased) {
|
||||||
|
this->settings->confirmDistribution();
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameDistributionAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
const Menu& menu = this->settings->getMenu();
|
||||||
|
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
|
|
||||||
|
this->placeTitle(text, {}, "DISTRIBUTION SETTINGS", 5.f, {});
|
||||||
|
|
||||||
|
const DistributionMode distributionMode = this->settings->getMenu().readPiecesList().getDistributionMode();
|
||||||
|
const std::vector<int>& distributions = this->settings->getDistributions();
|
||||||
|
|
||||||
|
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 1, 0, this->settings->getLoadablePiecesSize() - 3);
|
||||||
|
if (firstElem == 0) {
|
||||||
|
this->placeText(text, this->playerCursor, "< DISTRIBUTION MODE: " + getPiecesDistributionName(distributionMode) + " >", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem) + " PROBABILITY: " + std::to_string(distributions.at(firstElem)) + " >", 5.f, 15.f, sf::Vector2u{(unsigned int) firstElem, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distributionMode != CUSTOM) {
|
||||||
|
text.setFillColor(sf::Color(100, 100, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 1) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 1)) + " >", 5.f, 25.f, sf::Vector2u{0, (unsigned int) firstElem + 1});
|
||||||
|
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 2) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 2)) + " >", 5.f, 35.f, sf::Vector2u{0, (unsigned int) firstElem + 2});
|
||||||
|
this->placeText(text, this->playerCursor, "< SIZE " + std::to_string(firstElem + 3) + " PROBABILITY: " + std::to_string(distributions.at(firstElem + 3)) + " >", 5.f, 45.f, sf::Vector2u{0, (unsigned int) firstElem + 3});
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
|
}
|
||||||
21
src/GraphicalUI/AppMenus/GameDistributionAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class GameDistributionAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
PlayerCursor playerCursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameDistributionAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
|
void computeFrame() override;
|
||||||
|
|
||||||
|
void drawFrame() const override;
|
||||||
|
};
|
||||||
234
src/GraphicalUI/AppMenus/GamePiecesAppMenu.cpp
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
#include "GamePiecesAppMenu.h"
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "GameDistributionAppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
GamePiecesAppMenu::GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
playerCursor({1, (unsigned int) this->settings->getSelectedPieces().size() + 1u}) {
|
||||||
|
|
||||||
|
for (int i = 1; i <= this->settings->getLoadablePiecesSize(); i++) {
|
||||||
|
this->playerCursor.addRow(i + 1, this->settings->getMenu().readPiecesList().getNumberOfPieces(i) + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->settings->getLoadablePiecesSize() < MAXIMUM_PIECES_SIZE) {
|
||||||
|
this->playerCursor.addRow(this->settings->getLoadablePiecesSize() + 2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePiecesAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
this->playerCursor.updatePosition();
|
||||||
|
|
||||||
|
if (this->playerCursor.movedDown() && this->playerCursor.getPosition().y == 2) {
|
||||||
|
this->playerCursor.goToPosition({0, 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->enterReleased) {
|
||||||
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
|
this->menuStack->push(std::make_shared<GameDistributionAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->playerCursor.getPosition().y == (this->settings->getLoadablePiecesSize() + 2)) {
|
||||||
|
int newMaxSize = this->settings->getLoadablePiecesSize() + 1;
|
||||||
|
this->settings->loadPieces(newMaxSize);
|
||||||
|
|
||||||
|
this->playerCursor.removeRow(newMaxSize + 1);
|
||||||
|
this->playerCursor.addRow(newMaxSize + 1, this->settings->getMenu().readPiecesList().getNumberOfPieces(newMaxSize) + 4);
|
||||||
|
this->playerCursor.goToPosition({0u, newMaxSize + 1u});
|
||||||
|
|
||||||
|
if (newMaxSize < MAXIMUM_PIECES_SIZE) {
|
||||||
|
this->playerCursor.addRow(newMaxSize + 2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this->playerCursor.getPosition().y > 1) {
|
||||||
|
if (this->playerCursor.getPosition().x >= 4) {
|
||||||
|
this->settings->selectPieces(createSinglePieceType(this->playerCursor.getPosition().y - 1), this->playerCursor.getPosition().x - 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (this->playerCursor.getPosition().x) {
|
||||||
|
case 0 : {this->settings->selectPieces(ALL_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||||
|
case 1 : {this->settings->selectPieces(CONVEX_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||||
|
case 2 : {this->settings->selectPieces(HOLELESS_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||||
|
case 3 : {this->settings->selectPieces(OTHER_PIECES, this->playerCursor.getPosition().y - 1); break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->playerCursor.addPosition(0, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->escReleased) {
|
||||||
|
if (this->playerCursor.getPosition().y == 1) {
|
||||||
|
if (this->playerCursor.getPosition().x > 0) {
|
||||||
|
this->settings->unselectPieces(this->playerCursor.getPosition().x - 1);
|
||||||
|
this->playerCursor.removePosition(this->playerCursor.getPosition().x - 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->settings->confirmSelectedPieces();
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePiecesAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
|
|
||||||
|
this->placeTitle(text, {}, "PIECES SELECT", 5.f, {});
|
||||||
|
|
||||||
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
|
this->placeText(text, this->playerCursor, "PIECES DISTRIBUTION", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||||
|
this->drawSelectedPiecesRow(25.f);
|
||||||
|
this->drawRow(1, 35.f, true);
|
||||||
|
this->drawRow(2, 45.f, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->drawSelectedPiecesRow(15.f);
|
||||||
|
|
||||||
|
bool drawFromFirstElem = (this->playerCursor.getPosition().y == 1);
|
||||||
|
bool addExtraLine = (this->settings->getLoadablePiecesSize() < MAXIMUM_PIECES_SIZE);
|
||||||
|
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 2, 1, this->settings->getLoadablePiecesSize() - 2 + (addExtraLine ? 1 : 0));
|
||||||
|
this->drawRow(firstElem, 25.f, drawFromFirstElem);
|
||||||
|
this->drawRow(firstElem + 1, 35.f, drawFromFirstElem);
|
||||||
|
this->drawRow(firstElem + 2, 45.f, drawFromFirstElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||||
|
sf::RectangleShape rect({(float) this->renderWindow->getSize().x, 8.f * this->settings->getWindowSizeMultiplier()});
|
||||||
|
rect.setPosition({0.f, (yPos - 4.f) * this->settings->getWindowSizeMultiplier()});
|
||||||
|
rect.setFillColor({240, 240, 240});
|
||||||
|
this->renderWindow->draw(rect);
|
||||||
|
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier());
|
||||||
|
text.setFillColor({0, 0, 0});
|
||||||
|
|
||||||
|
int elem = (this->playerCursor.getPosition().y == 1) ? std::max(((int) this->playerCursor.getPosition().x) - 4, 0) : 0;
|
||||||
|
float xProgress = 1.f;
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
while (true) {
|
||||||
|
if ((this->playerCursor.getPosition().y == 1) && (elem == this->playerCursor.getPosition().x)) {
|
||||||
|
this->placeText(text, {}, "|", xProgress, yPos, {});
|
||||||
|
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elem >= this->settings->getSelectedPieces().size()) return;
|
||||||
|
|
||||||
|
const auto& [pieceType, value] = this->settings->getSelectedPieces().at(elem);
|
||||||
|
|
||||||
|
int pieceSize = getSizeOfPieces(pieceType);
|
||||||
|
if (pieceSize > 0) {
|
||||||
|
if (!(pieceSize > this->settings->getLoadablePiecesSize())) {
|
||||||
|
const Piece& piece = this->settings->getMenu().readPiecesList().lookAtPiece({pieceSize, value});
|
||||||
|
int cellSize = (8 * this->settings->getWindowSizeMultiplier()) / (piece.getLength());
|
||||||
|
sf::FloatRect piecePosition(sf::Vector2f(xProgress, yPos - 4.f) * (float) this->settings->getWindowSizeMultiplier(), sf::Vector2f(8 , 8) * (float) this->settings->getWindowSizeMultiplier());
|
||||||
|
this->drawPiece(piece, cellSize, piecePosition, false);
|
||||||
|
xProgress += (1.f + 8.f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->placeText(text, {}, "UNLOADED_" + std::to_string(pieceSize), xProgress, yPos, {});
|
||||||
|
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!(value > this->settings->getLoadablePiecesSize())) {
|
||||||
|
this->placeText(text, {}, ((first) ? "" : " ") + getPiecesTypeName(pieceType) + "_" + std::to_string(value), xProgress, yPos, {});
|
||||||
|
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->placeText(text, {}, "UNLOADED_" + std::to_string(pieceSize), xProgress, yPos, {});
|
||||||
|
xProgress += (1.f + (text.getGlobalBounds().size.x / this->settings->getWindowSizeMultiplier()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
elem++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePiecesAppMenu::drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const {
|
||||||
|
if (piecesSize > this->settings->getLoadablePiecesSize()) {
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setOutlineThickness(this->settings->getWindowSizeMultiplier() / 2);
|
||||||
|
if (this->playerCursor.getPosition().y == (piecesSize + 1)) {
|
||||||
|
text.setOutlineColor({255, 255, 255});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text.setOutlineColor({0, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sizeString = "LOAD SIZE " + std::to_string(piecesSize) + "? ";
|
||||||
|
if (piecesSize <= 10) {
|
||||||
|
text.setFillColor({0, 255, 0});
|
||||||
|
this->placeText(text, {}, sizeString + "(LOW LOAD TIME)", 1.f, yPos, {});
|
||||||
|
}
|
||||||
|
else if (piecesSize <= 13) {
|
||||||
|
text.setFillColor({255, 255, 0});
|
||||||
|
this->placeText(text, {}, sizeString + "(MEDIUM LOAD TIME)", 1.f, yPos, {});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text.setFillColor({255, 0, 0});
|
||||||
|
this->placeText(text, {}, sizeString + "(LONG LOAD TIME)", 1.f, yPos, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int numberOfPieces = this->settings->getMenu().readPiecesList().getNumberOfPieces(piecesSize);
|
||||||
|
int firstElem = (drawFromFirstElem) ? -4 : std::max(((int) this->playerCursor.getPosition().x) - 7, -4);
|
||||||
|
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier());
|
||||||
|
text.setFillColor({0, 0, 0});
|
||||||
|
text.setOutlineColor({255, 255, 255});
|
||||||
|
|
||||||
|
this->placeText(text, {}, "SIZE " + std::to_string(piecesSize), 1.f, yPos, {});
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
if (i + firstElem >= numberOfPieces) return;
|
||||||
|
|
||||||
|
if ((i + firstElem) < 0) {
|
||||||
|
switch (i + firstElem) {
|
||||||
|
case -4 : {this->placeText(text, this->playerCursor, "ALL", 10.f + (i * 10.f), yPos, sf::Vector2u{0, piecesSize + 1u}); break;}
|
||||||
|
case -3 : {this->placeText(text, this->playerCursor, "CONVEX", 10.f + (i * 10.f), yPos, sf::Vector2u{1, piecesSize + 1u}); break;}
|
||||||
|
case -2 : {this->placeText(text, this->playerCursor, "HOLELESS", 10.f + (i * 10.f), yPos, sf::Vector2u{2, piecesSize + 1u}); break;}
|
||||||
|
case -1 : {this->placeText(text, this->playerCursor, "OTHER", 10.f + (i * 10.f), yPos, sf::Vector2u{3, piecesSize + 1u}); break;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const Piece& piece = this->settings->getMenu().readPiecesList().lookAtPiece({piecesSize, firstElem + i});
|
||||||
|
int cellSize = (8 * this->settings->getWindowSizeMultiplier()) / (piece.getLength());
|
||||||
|
sf::FloatRect piecePosition(sf::Vector2f(10.f + (i * 10.f), yPos - 4.f) * (float) this->settings->getWindowSizeMultiplier(), sf::Vector2f(8 , 8) * (float) this->settings->getWindowSizeMultiplier());
|
||||||
|
this->drawPiece(piece, cellSize, piecePosition, this->playerCursor.getPosition() == sf::Vector2u{i + firstElem + 4u, piecesSize + 1u});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamePiecesAppMenu::drawPiece(const Piece& piece, int cellSize, const sf::FloatRect& piecePosition, bool selected) const {
|
||||||
|
sf::RectangleShape rect(piecePosition.size);
|
||||||
|
rect.setPosition(piecePosition.position);
|
||||||
|
rect.setFillColor({180, 180, 180});
|
||||||
|
if (selected) {
|
||||||
|
rect.setOutlineColor({0, 0, 0});
|
||||||
|
rect.setOutlineThickness(this->settings->getWindowSizeMultiplier() / 2);
|
||||||
|
}
|
||||||
|
this->renderWindow->draw(rect);
|
||||||
|
|
||||||
|
sf::RectangleShape cell(sf::Vector2f(cellSize, cellSize));
|
||||||
|
cell.setFillColor(this->getColorOfBlock(piece.getBlockType(), 0));
|
||||||
|
|
||||||
|
for (const Position& cellPosition : piece.getPositions()) {
|
||||||
|
cell.setPosition(sf::Vector2f(piecePosition.position.x + (cellPosition.x * cellSize),
|
||||||
|
piecePosition.position.y + ((piece.getLength() - cellPosition.y - 1) * cellSize) ));
|
||||||
|
this->renderWindow->draw(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/GraphicalUI/AppMenus/GamePiecesAppMenu.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class GamePiecesAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
PlayerCursor playerCursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
|
void computeFrame() override;
|
||||||
|
|
||||||
|
void drawFrame() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void drawSelectedPiecesRow(float yPos) const;
|
||||||
|
|
||||||
|
void drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const;
|
||||||
|
|
||||||
|
void drawPiece(const Piece& piece, int cellSize, const sf::FloatRect& pos, bool selected) const;
|
||||||
|
};
|
||||||
@@ -9,14 +9,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
static const int TIME_BEFORE_STARTING = 60;
|
|
||||||
|
|
||||||
|
|
||||||
GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
AppMenu(menuStack, settings, renderWindow),
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
game(this->settings->getMenu().startGame(this->settings->getGamemode())) {
|
game(this->settings->getMenu().startGame(this->settings->getGamemode())) {
|
||||||
|
|
||||||
this->startTimer = TIME_BEFORE_STARTING;
|
this->startTimer = this->settings->getStartTimerLength() * FRAMES_PER_SECOND;
|
||||||
|
if (this->startTimer == 0) {
|
||||||
|
this->game.start();
|
||||||
|
}
|
||||||
this->paused = false;
|
this->paused = false;
|
||||||
this->pausePressed = false;
|
this->pausePressed = false;
|
||||||
this->retryPressed = false;
|
this->retryPressed = false;
|
||||||
@@ -39,7 +40,7 @@ GamePlayingAppMenu::GamePlayingAppMenu(std::shared_ptr<MenuStack> menuStack, std
|
|||||||
|
|
||||||
this->nextCellSizeZoom = this->nextQueuePosition[0].size.y;
|
this->nextCellSizeZoom = this->nextQueuePosition[0].size.y;
|
||||||
for (const auto& piece : this->settings->getMenu().getPiecesList().getSelectedPieces()) {
|
for (const auto& piece : this->settings->getMenu().getPiecesList().getSelectedPieces()) {
|
||||||
float nextPieceCellSizeZoom = ((int) this->nextQueuePosition[0].size.y) / this->settings->getMenu().getPiecesList().getPiece(piece).getLength();
|
float nextPieceCellSizeZoom = ((int) this->nextQueuePosition[0].size.y) / this->settings->getMenu().getPiecesList().lookAtPiece(piece).getLength();
|
||||||
this->nextCellSizeZoom = std::min(this->nextCellSizeZoom, nextPieceCellSizeZoom);
|
this->nextCellSizeZoom = std::min(this->nextCellSizeZoom, nextPieceCellSizeZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +78,10 @@ void GamePlayingAppMenu::computeFrame() {
|
|||||||
else {
|
else {
|
||||||
if (this->retryPressed) {
|
if (this->retryPressed) {
|
||||||
this->game.reset();
|
this->game.reset();
|
||||||
this->startTimer = TIME_BEFORE_STARTING;
|
this->startTimer = this->settings->getStartTimerLength() * FRAMES_PER_SECOND;
|
||||||
|
if (this->startTimer == 0) {
|
||||||
|
this->game.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this->retryPressed = false;
|
this->retryPressed = false;
|
||||||
}
|
}
|
||||||
@@ -101,16 +105,25 @@ 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(this->getColorOfBlock(block, (block == NOTHING) ? 0 : -30));
|
cell.setFillColor((areBlockBones && block != NOTHING)
|
||||||
|
? 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);
|
||||||
}
|
}
|
||||||
@@ -118,7 +131,10 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
if (drawActivePiece) {
|
if (drawActivePiece) {
|
||||||
// ghost piece
|
// ghost piece
|
||||||
sf::Color ghostColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), 100);
|
sf::Color ghostColor = areBlockBones
|
||||||
|
? 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);
|
||||||
|
|
||||||
@@ -129,13 +145,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(pieceOutlineSize);
|
cell.setOutlineThickness(cellOutlineThickness);
|
||||||
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);
|
||||||
@@ -150,7 +166,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
if (drawActivePiece) {
|
if (drawActivePiece) {
|
||||||
// active piece
|
// active piece
|
||||||
sf::Color pieceColor = this->getColorOfBlock(this->game.getActivePiece()->getBlockType(), -200 * (this->game.getLockDelayProgression()));
|
sf::Color pieceColor = areBlockBones
|
||||||
|
? 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);
|
||||||
@@ -169,15 +187,17 @@ 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 color = this->getColorOfBlock(this->game.getNextPieces().at(i).getBlockType(), 0);
|
sf::Color pieceColor = areBlockBones
|
||||||
|
? 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;
|
||||||
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(color);
|
cell.setFillColor(pieceColor);
|
||||||
lowestRank = y;
|
lowestRank = y;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -195,13 +215,15 @@ 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 = this->getColorOfBlock(this->game.getHeldPiece()->getBlockType(), 0);
|
sf::Color color = areBlockBones
|
||||||
|
? 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);
|
||||||
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 {
|
||||||
@@ -247,6 +269,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
// game state
|
// game state
|
||||||
text.setOutlineColor(sf::Color(255, 255, 255));
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
text.setOutlineThickness(windowSizeMultiplier / 2.f);
|
text.setOutlineThickness(windowSizeMultiplier / 2.f);
|
||||||
|
text.setCharacterSize(windowSizeMultiplier * 4);
|
||||||
|
|
||||||
if (this->game.hasWon()) {
|
if (this->game.hasWon()) {
|
||||||
this->placeTitle(text, {}, "WIN", 25.f, {});
|
this->placeTitle(text, {}, "WIN", 25.f, {});
|
||||||
@@ -258,20 +281,12 @@ void GamePlayingAppMenu::drawFrame() const {
|
|||||||
this->placeTitle(text, {}, "PAUSE", 25.f, {});
|
this->placeTitle(text, {}, "PAUSE", 25.f, {});
|
||||||
}
|
}
|
||||||
else if (this->startTimer > 0) {
|
else if (this->startTimer > 0) {
|
||||||
text.setCharacterSize(windowSizeMultiplier * 4);
|
this->placeTitle(text, {}, std::to_string(((this->startTimer - 1) / ((this->settings->getStartTimerLength() * FRAMES_PER_SECOND) / 4))), 25.f, {});
|
||||||
this->placeTitle(text, {}, std::to_string(((this->startTimer - 1) / (TIME_BEFORE_STARTING / 4))), 25.f, {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->renderWindow->display();
|
this->renderWindow->display();
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Color GamePlayingAppMenu::getColorOfBlock(Block block, int luminosityShift) const {
|
|
||||||
Color rgbColor = BLOCKS_COLOR[block];
|
|
||||||
return sf::Color(std::clamp(rgbColor.red + luminosityShift, 0, 255),
|
|
||||||
std::clamp(rgbColor.green + luminosityShift, 0, 255),
|
|
||||||
std::clamp(rgbColor.blue + luminosityShift, 0, 255));
|
|
||||||
}
|
|
||||||
|
|
||||||
sf::Vector2f GamePlayingAppMenu::getBoardBlockPosition(int x, int y) const {
|
sf::Vector2f GamePlayingAppMenu::getBoardBlockPosition(int x, int y) const {
|
||||||
return sf::Vector2f(this->boardPosition.position.x + (x * this->cellSizeZoom),
|
return sf::Vector2f(this->boardPosition.position.x + (x * this->cellSizeZoom),
|
||||||
this->boardPosition.position.y + ((this->game.getBoard().getBaseHeight() + 9 - y) * this->cellSizeZoom));
|
this->boardPosition.position.y + ((this->game.getBoard().getBaseHeight() + 9 - y) * this->cellSizeZoom));
|
||||||
|
|||||||
@@ -28,7 +28,5 @@ class GamePlayingAppMenu : public AppMenu {
|
|||||||
|
|
||||||
void drawFrame() const override;
|
void drawFrame() const override;
|
||||||
|
|
||||||
sf::Color getColorOfBlock(Block block, int luminosityShift) const;
|
|
||||||
|
|
||||||
sf::Vector2f getBoardBlockPosition(int x, int y) const;
|
sf::Vector2f getBoardBlockPosition(int x, int y) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "GameSettingsAppMenu.h"
|
#include "GameSettingsAppMenu.h"
|
||||||
|
|
||||||
#include "AppMenu.h"
|
#include "AppMenu.h"
|
||||||
|
#include "GamePiecesAppMenu.h"
|
||||||
#include "GameBoardAppMenu.h"
|
#include "GameBoardAppMenu.h"
|
||||||
#include "GamePlayingAppMenu.h"
|
#include "GamePlayingAppMenu.h"
|
||||||
#include "../PlayerCursor.h"
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ 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(ZEN); break;}
|
case 1 : {this->settings->setGamemode(INVISIBLE); break;}
|
||||||
case 2 : break; //TODO
|
case 2 : {this->settings->setGamemode(ZEN); break;}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ void GameSettingsAppMenu::computeFrame() {
|
|||||||
if (this->enterReleased) {
|
if (this->enterReleased) {
|
||||||
if (this->playerCursor.getPosition().y == 0) {
|
if (this->playerCursor.getPosition().y == 0) {
|
||||||
if (this->playerCursor.getPosition().x == 0) {
|
if (this->playerCursor.getPosition().x == 0) {
|
||||||
//TODO
|
this->menuStack->push(std::make_shared<GamePiecesAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
}
|
}
|
||||||
if (this->playerCursor.getPosition().x == 1) {
|
if (this->playerCursor.getPosition().x == 1) {
|
||||||
this->menuStack->push(std::make_shared<GameBoardAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
this->menuStack->push(std::make_shared<GameBoardAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
@@ -67,15 +67,18 @@ void GameSettingsAppMenu::drawFrame() const {
|
|||||||
|
|
||||||
this->placeTitle(text, {}, "GAME SETTINGS", 5.f, {});
|
this->placeTitle(text, {}, "GAME SETTINGS", 5.f, {});
|
||||||
|
|
||||||
this->placeText(text, this->playerCursor, "PIECES SELECT (TODO)", 5.f, 15.f, sf::Vector2u{0, 0});
|
this->placeText(text, this->playerCursor, "PIECES SELECT", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||||
this->placeText(text, this->playerCursor, "BOARD SELECT", 40.f, 15.f, sf::Vector2u{1, 0});
|
this->placeText(text, this->playerCursor, "BOARD SELECT", 40.f, 15.f, sf::Vector2u{1, 0});
|
||||||
|
|
||||||
this->placeText(text, this->playerCursor, "SPRINT", 5.f, 25.f, sf::Vector2u{0, 1});
|
text.setOutlineThickness(0);
|
||||||
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 25.f, sf::Vector2u{1, 1});
|
this->placeTitle(text, {}, "GAMEMODE SELECT", 25.f, {});
|
||||||
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 25.f, sf::Vector2u{2, 1});
|
|
||||||
this->placeText(text, this->playerCursor, "MASTER", 5.f, 35.f, sf::Vector2u{0, 2});
|
this->placeText(text, this->playerCursor, "SPRINT", 5.f, 35.f, sf::Vector2u{0, 1});
|
||||||
this->placeText(text, this->playerCursor, "ZEN", 25.f, 35.f, sf::Vector2u{1, 2});
|
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
|
||||||
this->placeText(text, this->playerCursor, "??? (TODO)", 50.f, 35.f, sf::Vector2u{2, 2});
|
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, "INVISIBLE", 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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,20 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
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"
|
||||||
"using SFML 3 for the GUI.\n"
|
"using SFML 3 for the GUI.\n"
|
||||||
"It has been inspired by other\n"
|
"It has been inspired by other\n"
|
||||||
@@ -28,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"
|
||||||
@@ -47,18 +66,8 @@ 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."
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,13 @@
|
|||||||
#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];
|
std::array<std::string, INFO_SECTIONS_COUNT> sectionsName;
|
||||||
sf::String sectionsContent[4];
|
std::array<std::string, INFO_SECTIONS_COUNT> sectionsContent;
|
||||||
|
|
||||||
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);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
#include "AppMenu.h"
|
#include "AppMenu.h"
|
||||||
#include "../PlayerCursor.h"
|
#include "../PlayerCursor.h"
|
||||||
|
#include "../../Utils/AssetManager.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
SettingsKeybindsAppMenu::SettingsKeybindsAppMenu(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({12, 1}) {
|
playerCursor({1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) {
|
||||||
|
|
||||||
this->selectedAnAction = false;
|
this->selectedAnAction = false;
|
||||||
|
|
||||||
@@ -23,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}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
@@ -37,10 +36,10 @@ void SettingsMainAppMenu::computeFrame() {
|
|||||||
}
|
}
|
||||||
case 3 : {
|
case 3 : {
|
||||||
if (this->playerCursor.movedLeft()) {
|
if (this->playerCursor.movedLeft()) {
|
||||||
this->settings->lowerMasterVolume();
|
this->settings->shortenStartTimer();
|
||||||
}
|
}
|
||||||
if (this->playerCursor.movedRight()) {
|
if (this->playerCursor.movedRight()) {
|
||||||
this->settings->raiseMasterVolume();
|
this->settings->lengthenStartTimer();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -73,7 +72,7 @@ void SettingsMainAppMenu::drawFrame() const {
|
|||||||
this->placeText(text, this->playerCursor, "CHANGE KEYBINDS", 5.f, 15.f, sf::Vector2u{0, 0});
|
this->placeText(text, this->playerCursor, "CHANGE KEYBINDS", 5.f, 15.f, sf::Vector2u{0, 0});
|
||||||
this->placeText(text, this->playerCursor, "CHANGE CONTROLS", 5.f, 25.f, sf::Vector2u{0, 1});
|
this->placeText(text, this->playerCursor, "CHANGE CONTROLS", 5.f, 25.f, sf::Vector2u{0, 1});
|
||||||
this->placeText(text, this->playerCursor, "< WINDOW SIZE: " + std::to_string(windowSize.x) + "x" + std::to_string(windowSize.y) + " >", 5.f, 35.f, sf::Vector2u{0, 2});
|
this->placeText(text, this->playerCursor, "< WINDOW SIZE: " + std::to_string(windowSize.x) + "x" + std::to_string(windowSize.y) + " >", 5.f, 35.f, sf::Vector2u{0, 2});
|
||||||
this->placeText(text, this->playerCursor, "< VOLUME: " + std::to_string(this->settings->getMasterVolume()) + "% >", 5.f, 45.f, sf::Vector2u{0, 3});
|
this->placeText(text, this->playerCursor, "< START TIMER: " + std::to_string(this->settings->getStartTimerLength()) + "s >", 5.f, 45.f, sf::Vector2u{0, 3});
|
||||||
|
|
||||||
this->renderWindow->display();
|
this->renderWindow->display();
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/GraphicalUI/AppMenus/StartUpAppMenu.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#include "StartUpAppMenu.h"
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "MainAppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
StartUpAppMenu::StartUpAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||||
|
AppMenu(menuStack, settings, renderWindow),
|
||||||
|
playerCursor({MAXIMUM_PIECES_SIZE + 1}) {
|
||||||
|
|
||||||
|
this->playerCursor.goToPosition({(unsigned int) std::clamp(this->settings->getLoadablePiecesSize(), MINIMUM_PIECES_SIZE, MAXIMUM_PIECES_SIZE), 0u});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartUpAppMenu::computeFrame() {
|
||||||
|
this->updateMetaBinds();
|
||||||
|
this->playerCursor.updatePosition();
|
||||||
|
|
||||||
|
if (this->playerCursor.getPosition().x < MINIMUM_PIECES_SIZE) {
|
||||||
|
if (this->playerCursor.movedLeft()) {
|
||||||
|
this->playerCursor.goToPosition({MAXIMUM_PIECES_SIZE, 0});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->playerCursor.goToPosition({MINIMUM_PIECES_SIZE, 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->enterReleased) {
|
||||||
|
this->settings->loadSettingsFromFile(true, {this->playerCursor.getPosition().x});
|
||||||
|
this->menuStack->pop();
|
||||||
|
|
||||||
|
if (this->settings->hasLoadedPieces()) {
|
||||||
|
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "ERROR: COULD NOT LOAD PIECES" << std::endl;
|
||||||
|
std::cout << "ARGUMENT WAS: " << this->playerCursor.getPosition().x << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this->escReleased) {
|
||||||
|
this->menuStack->pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartUpAppMenu::drawFrame() const {
|
||||||
|
this->renderWindow->clear(sf::Color(200, 200, 200));
|
||||||
|
|
||||||
|
sf::Text text(this->pressStartFont, "", this->settings->getWindowSizeMultiplier() * 2);
|
||||||
|
text.setFillColor(sf::Color(0, 0, 0));
|
||||||
|
text.setOutlineColor(sf::Color(255, 255, 255));
|
||||||
|
|
||||||
|
this->placeTitle(text, {}, "SELECT THE LOADED PIECES MAXIMUM SIZE", 10.f, {});
|
||||||
|
this->placeTitle(text, this->playerCursor, "< " + std::to_string(this->playerCursor.getPosition().x) + " >", 25.f, this->playerCursor.getPosition());
|
||||||
|
|
||||||
|
text.setOutlineColor({0, 0, 0});
|
||||||
|
if (this->playerCursor.getPosition().x <= 10) {
|
||||||
|
text.setFillColor({0, 255, 0});
|
||||||
|
this->placeTitle(text, {}, "LOW LOAD TIME", 40.f, {});
|
||||||
|
}
|
||||||
|
else if (this->playerCursor.getPosition().x <= 13) {
|
||||||
|
text.setFillColor({255, 255, 0});
|
||||||
|
this->placeTitle(text, {}, "MEDIUM LOAD TIME", 40.f, {});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text.setFillColor({255, 0, 0});
|
||||||
|
this->placeTitle(text, {}, "LONG LOAD TIME", 40.f, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
this->renderWindow->display();
|
||||||
|
}
|
||||||
21
src/GraphicalUI/AppMenus/StartUpAppMenu.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AppMenu.h"
|
||||||
|
#include "../PlayerCursor.h"
|
||||||
|
|
||||||
|
#include <stack>
|
||||||
|
#include <memory>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class StartUpAppMenu : public AppMenu {
|
||||||
|
private:
|
||||||
|
PlayerCursor playerCursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StartUpAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||||
|
|
||||||
|
void computeFrame() override;
|
||||||
|
|
||||||
|
void drawFrame() const override;
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "GraphApp.h"
|
#include "GraphApp.h"
|
||||||
|
|
||||||
#include "AppMenus/AppMenu.h"
|
#include "AppMenus/AppMenu.h"
|
||||||
#include "AppMenus/MainAppMenu.h"
|
#include "AppMenus/StartUpAppMenu.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
@@ -12,14 +12,14 @@ static const double TIME_BETWEEN_FRAMES = (1000.f / FRAMES_PER_SECOND);
|
|||||||
|
|
||||||
|
|
||||||
GraphApp::GraphApp() {
|
GraphApp::GraphApp() {
|
||||||
this->settings = std::make_shared<Settings>();
|
this->settings = std::make_shared<Settings>(false);
|
||||||
this->menuStack = std::make_shared<MenuStack>();
|
this->menuStack = std::make_shared<MenuStack>();
|
||||||
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
this->renderWindow = std::make_shared<sf::RenderWindow>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphApp::run() {
|
void GraphApp::run() {
|
||||||
this->settings->changeVideoMode(*this->renderWindow);
|
this->settings->changeVideoMode(*this->renderWindow);
|
||||||
this->menuStack->push(std::make_shared<MainAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
this->menuStack->push(std::make_shared<StartUpAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||||
|
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
double timeAtNextFrame = 0;
|
double timeAtNextFrame = 0;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <optional>
|
#include <algorithm>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
using sfKey = sf::Keyboard::Key;
|
using sfKey = sf::Keyboard::Key;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
enum PiecesType {
|
enum PiecesType {
|
||||||
CONVEX_PIECES,
|
CONVEX_PIECES,
|
||||||
@@ -19,3 +21,15 @@ inline int getSizeOfPieces(PiecesType type) {
|
|||||||
inline PiecesType createSinglePieceType(int size) {
|
inline PiecesType createSinglePieceType(int size) {
|
||||||
return PiecesType(SINGLE_PIECE + size - 1);
|
return PiecesType(SINGLE_PIECE + size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string getPiecesTypeName(PiecesType piecesType) {
|
||||||
|
static const std::string PIECES_TYPE_NAME[] = {
|
||||||
|
"CONVEX",
|
||||||
|
"HOLELESS",
|
||||||
|
"OTHER",
|
||||||
|
"ALL",
|
||||||
|
"SINGLE"
|
||||||
|
};
|
||||||
|
|
||||||
|
return PIECES_TYPE_NAME[piecesType];
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,6 +66,48 @@ void PlayerCursor::goToPosition(const sf::Vector2u& newPosition) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PlayerCursor::addPosition(unsigned int x, unsigned int y) {
|
||||||
|
if (y >= this->rows.size()) return false;
|
||||||
|
if (x > this->rows.at(y)) return false;
|
||||||
|
|
||||||
|
this->rows.at(y)++;
|
||||||
|
if ((y == this->position.y) && (x <= this->position.x)) {
|
||||||
|
this->position.x++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerCursor::removePosition(unsigned int x, unsigned int y) {
|
||||||
|
if (y >= this->rows.size()) return false;
|
||||||
|
if (x >= this->rows.at(y)) return false;
|
||||||
|
|
||||||
|
this->rows.at(y)--;
|
||||||
|
if ((y == this->position.y) && (x < this->position.x)) {
|
||||||
|
this->position.x--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerCursor::addRow(unsigned int position, unsigned int width) {
|
||||||
|
if (position > this->rows.size()) return false;
|
||||||
|
|
||||||
|
this->rows.insert(this->rows.begin() + position, width);
|
||||||
|
if (position <= this->position.y) {
|
||||||
|
this->position.y++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerCursor::removeRow(unsigned int position) {
|
||||||
|
if (position >= this->rows.size()) return false;
|
||||||
|
|
||||||
|
this->rows.erase(this->rows.begin() + position);
|
||||||
|
if (position < this->position.y) {
|
||||||
|
this->position.y--;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const sf::Vector2u& PlayerCursor::getPosition() const {
|
const sf::Vector2u& PlayerCursor::getPosition() const {
|
||||||
return this->position;
|
return this->position;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ class PlayerCursor {
|
|||||||
|
|
||||||
void goToPosition(const sf::Vector2u& newPosition);
|
void goToPosition(const sf::Vector2u& newPosition);
|
||||||
|
|
||||||
|
bool addPosition(unsigned int x, unsigned int y);
|
||||||
|
|
||||||
|
bool removePosition(unsigned int x, unsigned int y);
|
||||||
|
|
||||||
|
bool addRow(unsigned int position, unsigned int width);
|
||||||
|
|
||||||
|
bool removeRow(unsigned int position);
|
||||||
|
|
||||||
const sf::Vector2u& getPosition() const;
|
const sf::Vector2u& getPosition() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
#include "../Core/Menu.h"
|
#include "../Core/Menu.h"
|
||||||
#include "Keybinds.h"
|
#include "Keybinds.h"
|
||||||
|
#include "PiecesType.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
@@ -10,25 +13,64 @@
|
|||||||
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
static const sf::Vector2u BASE_WINDOW_SIZE = {80, 50};
|
||||||
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 10, 14, 20, 30, 40};
|
static const int WINDOW_SIZE_MULTIPLIERS[] = {4, 6, 10, 14, 20, 30, 40};
|
||||||
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
static const int WINDOW_SIZE_LAST_MODE = (sizeof(WINDOW_SIZE_MULTIPLIERS) / sizeof(int)) - 1;
|
||||||
|
static const int START_TIMER_MAX = 4;
|
||||||
|
static const int DISTRIBUTION_MAX = 20;
|
||||||
|
|
||||||
|
|
||||||
Settings::Settings() {
|
Settings::Settings(bool loadPieces) {
|
||||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
this->keybinds.clear();
|
||||||
this->menu.getPiecesList().loadPieces(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->keybinds.reserve(NUMBER_OF_KEYBINDS);
|
this->keybinds.reserve(NUMBER_OF_KEYBINDS);
|
||||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||||
this->keybinds.emplace_back(i);
|
this->keybinds.emplace_back(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->loadSettingsFromFile();
|
this->loadSettingsFromFile(loadPieces, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::loadSettingsFromFile() {
|
void Settings::loadPieces(int loadablePiecesSizeRequest) {
|
||||||
|
if (loadablePiecesSizeRequest < MINIMUM_PIECES_SIZE) {
|
||||||
|
loadablePiecesSizeRequest = MINIMUM_PIECES_SIZE;
|
||||||
|
}
|
||||||
|
else if (loadablePiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||||
|
loadablePiecesSizeRequest = MAXIMUM_PIECES_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool succeeded = true;
|
||||||
|
int i = 1;
|
||||||
|
while (succeeded && (i <= loadablePiecesSizeRequest)) {
|
||||||
|
succeeded = this->menu.getPiecesList().loadPieces(i);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succeeded) {
|
||||||
|
this->loadablePiecesSize = loadablePiecesSizeRequest;
|
||||||
|
}
|
||||||
|
this->loadedPieces = succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest) {
|
||||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||||
char byte;
|
char byte;
|
||||||
|
|
||||||
|
// file format version
|
||||||
|
settingsFile.get(byte);
|
||||||
|
|
||||||
|
// loadable pieces size
|
||||||
|
settingsFile.get(byte);
|
||||||
|
this->loadablePiecesSize = byte;
|
||||||
|
|
||||||
|
if (loadPieces) {
|
||||||
|
if (loadablePiecesSizeRequest.has_value()) {
|
||||||
|
this->loadPieces(loadablePiecesSizeRequest.value());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->loadPieces(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->loadedPieces = false;
|
||||||
|
}
|
||||||
|
|
||||||
// keybind layout
|
// keybind layout
|
||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
this->chosenKeybinds = byte;
|
this->chosenKeybinds = byte;
|
||||||
@@ -49,9 +91,9 @@ void Settings::loadSettingsFromFile() {
|
|||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
this->windowSizeMode = byte;
|
this->windowSizeMode = byte;
|
||||||
|
|
||||||
// master volume
|
// start timer length
|
||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
this->masterVolume = byte;
|
this->startTimerLength = byte;
|
||||||
|
|
||||||
// gamemode
|
// gamemode
|
||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
@@ -65,35 +107,68 @@ void Settings::loadSettingsFromFile() {
|
|||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
this->menu.setBoardHeight(byte);
|
this->menu.setBoardHeight(byte);
|
||||||
|
|
||||||
|
if (this->loadedPieces) {
|
||||||
// piece distribution
|
// piece distribution
|
||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
//TODO
|
this->menu.getPiecesList().setDistributionMode(DistributionMode(byte));
|
||||||
if (byte == 2) {
|
|
||||||
|
this->distributions.clear();
|
||||||
|
this->distributions.push_back(0);
|
||||||
for (int i = 1; i <= 15; i++) {
|
for (int i = 1; i <= 15; i++) {
|
||||||
settingsFile.get(byte);
|
settingsFile.get(byte);
|
||||||
//TODO
|
this->distributions.push_back(byte);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
this->confirmDistribution();
|
||||||
|
|
||||||
// selected pieces
|
// selected pieces
|
||||||
char pieceType;
|
char pieceType;
|
||||||
char pieceValue;
|
char pieceSize;
|
||||||
|
char lowByte;
|
||||||
|
char midByte;
|
||||||
|
char highByte;
|
||||||
|
|
||||||
this->selectedPieces.clear();
|
this->selectedPieces.clear();
|
||||||
while (settingsFile.get(pieceType)) {
|
while (settingsFile.get(pieceType)) {
|
||||||
if (settingsFile.eof()) break;
|
if (settingsFile.eof()) break;
|
||||||
|
|
||||||
settingsFile.get(pieceValue);
|
if (getSizeOfPieces(PiecesType(pieceType)) == 0) {
|
||||||
this->selectedPieces.push_back({PiecesType(pieceType), pieceValue});
|
settingsFile.get(pieceSize);
|
||||||
|
|
||||||
|
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceSize);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
settingsFile.get(lowByte);
|
||||||
|
settingsFile.get(midByte);
|
||||||
|
settingsFile.get(highByte);
|
||||||
|
|
||||||
|
int pieceNumber = ((unsigned char) lowByte) + ((unsigned char) midByte << 8) + ((unsigned char) highByte << 16);
|
||||||
|
this->selectedPieces.emplace_back(PiecesType(pieceType), pieceNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this->confirmSelectedPieces();
|
this->confirmSelectedPieces();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this->distributions.clear();
|
||||||
|
this->selectedPieces.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Settings::saveSettingsToFile() const {
|
void Settings::saveSettingsToFile() const {
|
||||||
|
if (!this->loadedPieces) return;
|
||||||
|
|
||||||
this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile();
|
this->keybinds.at(CUSTOMIZABLE_KEYBINDS).saveKeybindsToFile();
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
// file format version
|
||||||
|
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// loadable pieces size
|
||||||
|
byte = this->loadablePiecesSize;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// keybind layout
|
// keybind layout
|
||||||
byte = this->chosenKeybinds;
|
byte = this->chosenKeybinds;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
@@ -114,8 +189,8 @@ void Settings::saveSettingsToFile() const {
|
|||||||
byte = this->windowSizeMode;
|
byte = this->windowSizeMode;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// master volume
|
// start timer length
|
||||||
byte = this->masterVolume;
|
byte = this->startTimerLength;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// gamemode
|
// gamemode
|
||||||
@@ -131,16 +206,31 @@ void Settings::saveSettingsToFile() const {
|
|||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// piece distribution
|
// piece distribution
|
||||||
//TODO
|
byte = this->menu.readPiecesList().getDistributionMode();
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; i++) {
|
||||||
|
byte = this->distributions.at(i);
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
// selected pieces
|
// selected pieces
|
||||||
for (const auto& [type, value] : this->selectedPieces) {
|
for (const auto& [type, value] : this->selectedPieces) {
|
||||||
byte = type;
|
byte = type;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
if (getSizeOfPieces(type) == 0) {
|
||||||
byte = value;
|
byte = value;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
int number = value;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
byte = (number % 256);
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
number = (number >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::selectNextKeybinds() {
|
bool Settings::selectNextKeybinds() {
|
||||||
@@ -188,17 +278,17 @@ void Settings::changeVideoMode(sf::RenderWindow& window) const {
|
|||||||
window.setPosition(sf::Vector2i((desktopSize.x / 2) - (windowSize.x / 2), (desktopSize.y / 2) - (windowSize.y / 2)));
|
window.setPosition(sf::Vector2i((desktopSize.x / 2) - (windowSize.x / 2), (desktopSize.y / 2) - (windowSize.y / 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::raiseMasterVolume() {
|
bool Settings::lengthenStartTimer() {
|
||||||
if (this->masterVolume < 100) {
|
if (this->startTimerLength < START_TIMER_MAX) {
|
||||||
this->masterVolume = std::min(this->masterVolume + 5, 100);
|
this->startTimerLength++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Settings::lowerMasterVolume() {
|
bool Settings::shortenStartTimer() {
|
||||||
if (this->masterVolume > 0) {
|
if (this->startTimerLength > 0) {
|
||||||
this->masterVolume = std::max(this->masterVolume - 5, 0);
|
this->startTimerLength--;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -209,37 +299,82 @@ void Settings::setGamemode(Gamemode gamemode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Settings::selectPieces(PiecesType type, int value) {
|
void Settings::selectPieces(PiecesType type, int value) {
|
||||||
|
if (!this->loadedPieces) return;
|
||||||
|
|
||||||
this->selectedPieces.emplace_back(type, value);
|
this->selectedPieces.emplace_back(type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::unselectPieces(int index) {
|
void Settings::unselectPieces(int index) {
|
||||||
|
if (!this->loadedPieces) return;
|
||||||
if (index >= this->selectedPieces.size()) return;
|
if (index >= this->selectedPieces.size()) return;
|
||||||
|
|
||||||
this->selectedPieces.erase(this->selectedPieces.begin() + index);
|
this->selectedPieces.erase(this->selectedPieces.begin() + index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::confirmSelectedPieces() {
|
void Settings::confirmSelectedPieces() {
|
||||||
|
if (!this->loadedPieces) return;
|
||||||
|
|
||||||
this->menu.getPiecesList().unselectAll();
|
this->menu.getPiecesList().unselectAll();
|
||||||
|
|
||||||
|
bool selectedNone = true;
|
||||||
for (const auto& [type, value] : this->selectedPieces) {
|
for (const auto& [type, value] : this->selectedPieces) {
|
||||||
int size = getSizeOfPieces(type);
|
int size = getSizeOfPieces(type);
|
||||||
|
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
if (!(value > this->loadablePiecesSize)) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CONVEX_PIECES : {this->menu.getPiecesList().selectConvexPieces(value); break;}
|
case CONVEX_PIECES : {this->menu.getPiecesList().selectConvexPieces(value); break;}
|
||||||
case HOLELESS_PIECES : {this->menu.getPiecesList().selectHolelessPieces(value); break;}
|
case HOLELESS_PIECES : {this->menu.getPiecesList().selectHolelessPieces(value); break;}
|
||||||
case OTHER_PIECES : {this->menu.getPiecesList().selectOtherPieces(value); break;}
|
case OTHER_PIECES : {this->menu.getPiecesList().selectOtherPieces(value); break;}
|
||||||
case ALL_PIECES : {this->menu.getPiecesList().selectAllPieces(value); break;}
|
case ALL_PIECES : {this->menu.getPiecesList().selectAllPieces(value); break;}
|
||||||
}
|
}
|
||||||
|
selectedNone = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (size > MAXIMUM_PIECES_SIZE) return;
|
if (!(getSizeOfPieces(type) > this->loadablePiecesSize)) {
|
||||||
|
|
||||||
this->menu.getPiecesList().selectPiece(size, value);
|
this->menu.getPiecesList().selectPiece(size, value);
|
||||||
|
selectedNone = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (selectedNone) {
|
||||||
|
this->selectedPieces.push_back(DEFAULT_SELECTION);
|
||||||
|
this->confirmSelectedPieces();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::increaseDistribution(int size) {
|
||||||
|
if (!this->loadedPieces) return false;
|
||||||
|
if (size < 1 || size > this->loadablePiecesSize) return false;
|
||||||
|
|
||||||
|
if (this->distributions.at(size) < DISTRIBUTION_MAX) {
|
||||||
|
this->distributions.at(size)++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::decreaseDistribution(int size) {
|
||||||
|
if (!this->loadedPieces) return false;
|
||||||
|
if (size < 1 || size > this->loadablePiecesSize) return false;
|
||||||
|
|
||||||
|
if (this->distributions.at(size) > 0) {
|
||||||
|
this->distributions.at(size)--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::confirmDistribution() {
|
||||||
|
if (!this->loadedPieces) return;
|
||||||
|
|
||||||
|
for (int i = 1; i <= 15; i++) {
|
||||||
|
this->menu.getPiecesList().changeCustomDistribution(i, (double) 1 / (this->distributions.at(i) + 0.001));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Menu& Settings::getMenu() {
|
Menu& Settings::getMenu() {
|
||||||
return this->menu;
|
return this->menu;
|
||||||
}
|
}
|
||||||
@@ -248,6 +383,14 @@ Keybinds& Settings::getKeybinds() {
|
|||||||
return this->keybinds.at(this->chosenKeybinds);
|
return this->keybinds.at(this->chosenKeybinds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Settings::getLoadablePiecesSize() const {
|
||||||
|
return this->loadablePiecesSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::hasLoadedPieces() const {
|
||||||
|
return this->loadedPieces;
|
||||||
|
}
|
||||||
|
|
||||||
int Settings::getKeybindsLayout() const {
|
int Settings::getKeybindsLayout() const {
|
||||||
return this->chosenKeybinds;
|
return this->chosenKeybinds;
|
||||||
}
|
}
|
||||||
@@ -260,10 +403,14 @@ int Settings::getWindowSizeMultiplier() const {
|
|||||||
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
return WINDOW_SIZE_MULTIPLIERS[this->windowSizeMode];
|
||||||
}
|
}
|
||||||
|
|
||||||
int Settings::getMasterVolume() const {
|
int Settings::getStartTimerLength() const {
|
||||||
return this->masterVolume;
|
return this->startTimerLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::pair<PiecesType, int>>& Settings::getSelectedPieces() const {
|
const std::vector<std::pair<PiecesType, int>>& Settings::getSelectedPieces() const {
|
||||||
return this->selectedPieces;
|
return this->selectedPieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<int>& Settings::getDistributions() const {
|
||||||
|
return this->distributions;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,34 +4,47 @@
|
|||||||
#include "Keybinds.h"
|
#include "Keybinds.h"
|
||||||
#include "PiecesType.h"
|
#include "PiecesType.h"
|
||||||
|
|
||||||
#include <SFML/Graphics.hpp>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
|
||||||
|
static const int CURRENT_FILE_FORMAT_VERSION = 11;
|
||||||
|
|
||||||
static const int MAXIMUM_BOARD_WIDTH = 40;
|
static const int MAXIMUM_BOARD_WIDTH = 40;
|
||||||
static const int MAXIMUM_BOARD_HEIGHT = 40;
|
static const int MAXIMUM_BOARD_HEIGHT = 40;
|
||||||
|
|
||||||
//#define __JMINOS_RELEASE__
|
static const int RELEASE_PIECES_SIZE = 15;
|
||||||
#ifdef __JMINOS_RELEASE__
|
static const int DEBUG_PIECES_SIZE = 10;
|
||||||
static const int MAXIMUM_PIECES_SIZE = 15;
|
|
||||||
|
static const int MINIMUM_PIECES_SIZE = 4;
|
||||||
|
#ifdef NDEBUG
|
||||||
|
static const int MAXIMUM_PIECES_SIZE = RELEASE_PIECES_SIZE;
|
||||||
#else
|
#else
|
||||||
static const int MAXIMUM_PIECES_SIZE = 10;
|
static const int MAXIMUM_PIECES_SIZE = DEBUG_PIECES_SIZE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const std::pair<PiecesType, int> DEFAULT_SELECTION = {ALL_PIECES, MINIMUM_PIECES_SIZE};
|
||||||
|
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
private:
|
private:
|
||||||
Menu menu;
|
Menu menu;
|
||||||
|
int loadablePiecesSize;
|
||||||
|
bool loadedPieces;
|
||||||
std::vector<Keybinds> keybinds;
|
std::vector<Keybinds> keybinds;
|
||||||
int chosenKeybinds;
|
int chosenKeybinds;
|
||||||
int windowSizeMode;
|
int windowSizeMode;
|
||||||
int masterVolume;
|
int startTimerLength;
|
||||||
Gamemode gamemode;
|
Gamemode gamemode;
|
||||||
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
std::vector<std::pair<PiecesType, int>> selectedPieces;
|
||||||
|
std::vector<int> distributions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Settings();
|
Settings(bool loadPieces);
|
||||||
|
|
||||||
void loadSettingsFromFile();
|
void loadPieces(int loadablePiecesSizeRequest);
|
||||||
|
|
||||||
|
void loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest);
|
||||||
|
|
||||||
void saveSettingsToFile() const;
|
void saveSettingsToFile() const;
|
||||||
|
|
||||||
@@ -47,9 +60,9 @@ class Settings {
|
|||||||
|
|
||||||
void changeVideoMode(sf::RenderWindow& window) const;
|
void changeVideoMode(sf::RenderWindow& window) const;
|
||||||
|
|
||||||
bool raiseMasterVolume();
|
bool lengthenStartTimer();
|
||||||
|
|
||||||
bool lowerMasterVolume();
|
bool shortenStartTimer();
|
||||||
|
|
||||||
void setGamemode(Gamemode gamemode);
|
void setGamemode(Gamemode gamemode);
|
||||||
|
|
||||||
@@ -59,17 +72,29 @@ class Settings {
|
|||||||
|
|
||||||
void confirmSelectedPieces();
|
void confirmSelectedPieces();
|
||||||
|
|
||||||
|
bool increaseDistribution(int size);
|
||||||
|
|
||||||
|
bool decreaseDistribution(int size);
|
||||||
|
|
||||||
|
void confirmDistribution();
|
||||||
|
|
||||||
Menu& getMenu();
|
Menu& getMenu();
|
||||||
|
|
||||||
Keybinds& getKeybinds();
|
Keybinds& getKeybinds();
|
||||||
|
|
||||||
|
int getLoadablePiecesSize() const;
|
||||||
|
|
||||||
|
bool hasLoadedPieces() const;
|
||||||
|
|
||||||
int getKeybindsLayout() const;
|
int getKeybindsLayout() const;
|
||||||
|
|
||||||
Gamemode getGamemode() const;
|
Gamemode getGamemode() const;
|
||||||
|
|
||||||
int getWindowSizeMultiplier() const;
|
int getWindowSizeMultiplier() const;
|
||||||
|
|
||||||
int getMasterVolume() const;
|
int getStartTimerLength() const;
|
||||||
|
|
||||||
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
const std::vector<std::pair<PiecesType, int>>& getSelectedPieces() const;
|
||||||
|
|
||||||
|
const std::vector<int>& getDistributions() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,45 +4,123 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
[[nodiscard]] bool resetSettingsFile();
|
||||||
void resetSettingsFile();
|
[[nodiscard]] bool resetKeybindFile(int layout);
|
||||||
void resetKeybindFile(int layout);
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::srand(std::time(NULL));
|
std::srand(std::time(NULL));
|
||||||
|
|
||||||
|
bool everythingIsOK = true;
|
||||||
|
|
||||||
|
// CHECK PIECES FILES
|
||||||
|
|
||||||
PiecesFiles pf;
|
PiecesFiles pf;
|
||||||
|
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("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||||
std::cout << "pieces files for size " << i << " not found, generating..." << std::endl;
|
#ifndef DEBUG
|
||||||
pf.savePieces(i);
|
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;
|
||||||
|
warned = true;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
if (!std::filesystem::exists("data/config/settings.bin")) {
|
|
||||||
std::cout << "settings file not found, generating..." << std::endl;
|
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
|
||||||
resetSettingsFile();
|
everythingIsOK &= pf.savePieces(i);
|
||||||
}
|
|
||||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
|
||||||
if (!std::filesystem::exists("data/config/keybinds/layout" + std::to_string(i) + ".bin")) {
|
|
||||||
std::cout << "keybind file n°" << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl;
|
|
||||||
resetKeybindFile(i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
std::cout << "IMPORTANT: You are currently in debug mode, if you wish to use bigger pieces, type 'xmake f -m release'." << std::endl;
|
||||||
|
|
||||||
|
bool releasePiecesGenerated = true;
|
||||||
|
for (int i = DEBUG_PIECES_SIZE + 1; i <= RELEASE_PIECES_SIZE; i++) {
|
||||||
|
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||||
|
releasePiecesGenerated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!releasePiecesGenerated) {
|
||||||
|
std::cout << "NOTE: You do not have all pieces generated, generating can take several minutes." << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool everythingGenerated = true;
|
||||||
|
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||||
|
std::string filePath = "data/pieces/" + std::to_string(i) + "minos.bin";
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(filePath)) {
|
||||||
|
std::cout << "ERROR: Could not open file " + filePath << std::endl;
|
||||||
|
everythingIsOK &= false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK CONFIG FILES
|
||||||
|
|
||||||
|
if (!std::filesystem::exists("data/config/settings.bin")) {
|
||||||
|
std::cout << "INFO: Settings file not found, generating..." << std::endl;
|
||||||
|
everythingIsOK &= resetSettingsFile();
|
||||||
|
|
||||||
|
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||||
|
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;
|
||||||
|
everythingIsOK &= resetKeybindFile(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||||
|
char byte;
|
||||||
|
|
||||||
|
settingsFile.get(byte);
|
||||||
|
if ((unsigned char) byte < CURRENT_FILE_FORMAT_VERSION) {
|
||||||
|
std::cout << "INFO: Files format changed, regenerating..." << std::endl;
|
||||||
|
everythingIsOK &= resetSettingsFile();
|
||||||
|
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||||
|
everythingIsOK &= resetKeybindFile(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LAUNCH APP
|
||||||
|
|
||||||
|
if (everythingIsOK) {
|
||||||
GraphApp UI;
|
GraphApp UI;
|
||||||
UI.run();
|
UI.run();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "ERROR: The game could not launch properly." << std::endl;
|
||||||
|
std::cout << "Press enter to quit. ";
|
||||||
|
std::cin.get();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void resetSettingsFile() {
|
bool resetSettingsFile() {
|
||||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
if (!std::filesystem::exists("data/config")) {
|
||||||
char byte;
|
std::filesystem::create_directories("data/config");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string filePath ="data/config/settings.bin";
|
||||||
|
std::ofstream settingsFile(filePath, std::ios::trunc | std::ios::binary);
|
||||||
|
if (!settingsFile.good()) {
|
||||||
|
std::cerr << "ERROR: Could not open file " + filePath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char byte;
|
||||||
Menu menu;
|
Menu menu;
|
||||||
|
|
||||||
|
// file format version
|
||||||
|
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
// loadable pieces size
|
||||||
|
byte = MINIMUM_PIECES_SIZE;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// keybind layout
|
// keybind layout
|
||||||
byte = 0;
|
byte = 0;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
@@ -63,8 +141,8 @@ void resetSettingsFile() {
|
|||||||
byte = 2;
|
byte = 2;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// master volume
|
// start timer length
|
||||||
byte = 50;
|
byte = 2;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// gamemode
|
// gamemode
|
||||||
@@ -80,20 +158,43 @@ void resetSettingsFile() {
|
|||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
// piece distribution
|
// piece distribution
|
||||||
byte = 0;
|
byte = DEFAULT;
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
|
for (int i = 1; i <= 15; i++) {
|
||||||
// selected pieces
|
byte = 1;
|
||||||
byte = ALL_PIECES;
|
|
||||||
settingsFile.write(&byte, 1);
|
|
||||||
byte = 4;
|
|
||||||
settingsFile.write(&byte, 1);
|
settingsFile.write(&byte, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetKeybindFile(int layout) {
|
// selected pieces
|
||||||
if (layout < 0 || layout > 4) return;
|
byte = DEFAULT_SELECTION.first;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
byte = DEFAULT_SELECTION.second;
|
||||||
|
settingsFile.write(&byte, 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool resetKeybindFile(int layout) {
|
||||||
|
if (layout < 0 || layout > NUMBER_OF_KEYBINDS) {
|
||||||
|
std::cerr << "ERROR: Trying to create keybind layout number " << layout << " which is outside of range (" << NUMBER_OF_KEYBINDS << ")." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::filesystem::exists("data/config/keybinds")) {
|
||||||
|
std::filesystem::create_directories("data/config/keybinds");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string filePath = "data/config/keybinds/layout" + std::to_string(layout) + ".bin";
|
||||||
|
std::ofstream layoutFile(filePath, std::ios::trunc | std::ios::binary);
|
||||||
|
if (!layoutFile.good()) {
|
||||||
|
std::cerr << "ERROR: Could not open file " + filePath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout == NUMBER_OF_KEYBINDS) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if (layout != 4) {
|
if (layout != 4) {
|
||||||
@@ -159,4 +260,6 @@ void resetKeybindFile(int layout) {
|
|||||||
byte = 0xFF;
|
byte = 0xFF;
|
||||||
layoutFile.write(&byte, 1);
|
layoutFile.write(&byte, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -50,15 +50,15 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate the list of candidate positions
|
// generate the list of candidate positions
|
||||||
for (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 (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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "../Common/Compression.h"
|
||||||
|
|
||||||
|
|
||||||
PiecesFiles::PiecesFiles() {
|
PiecesFiles::PiecesFiles() {
|
||||||
}
|
}
|
||||||
@@ -19,34 +21,53 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
|
|||||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
|
||||||
if (!piecesFile.good()) {
|
Generator generator;
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Generator generator;
|
constexpr std::size_t INITIAL_CAPACITY = 2048;
|
||||||
std::vector<Polyomino> nMinos = generator.generatePolyominoes(polyominoSize);
|
DataBuffer buffer(INITIAL_CAPACITY);
|
||||||
|
|
||||||
// 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();
|
bool isConvex = polyomino.isConvex();
|
||||||
piecesFile.write(&infoByte, 1);
|
bool hasHole = (isConvex) ? false : polyomino.hasHole();
|
||||||
|
std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength();
|
||||||
|
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
|
||||||
char positionByte;
|
for (int i = 0; i < bytesNeeded; i++) {
|
||||||
for (Position position : nMino.getPositions()) {
|
Polyomino::PolyominoData pData = polyominoData >> (i * 8);
|
||||||
positionByte = (position.x << 4) + position.y;
|
unsigned long data =
|
||||||
piecesFile.write(&positionByte, 1);
|
(pData & byteMask).to_ulong();
|
||||||
|
buffer << static_cast<std::uint8_t>(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
DataBuffer compressed = Compress(buffer);
|
||||||
|
|
||||||
|
return compressed.WriteFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const {
|
||||||
@@ -54,6 +75,13 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
|||||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DataBuffer compressed;
|
||||||
|
if(!compressed.ReadFile(filePath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DataBuffer buffer = Decompress(compressed, compressed.GetSize());
|
||||||
|
|
||||||
std::ifstream piecesFile(filePath, std::ios::binary);
|
std::ifstream piecesFile(filePath, std::ios::binary);
|
||||||
if (!piecesFile.good()) {
|
if (!piecesFile.good()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -70,37 +98,38 @@ 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;
|
|
||||||
|
|
||||||
char infoByte;
|
std::uint8_t infoByte;
|
||||||
|
std::uint8_t positionByte;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (piecesFile.get(infoByte)) {
|
while (!buffer.IsFinished()) {
|
||||||
if (piecesFile.eof()) break;
|
// if (piecesFile.eof()) break;
|
||||||
|
buffer >> infoByte;
|
||||||
|
|
||||||
// read piece infos
|
// read piece infos
|
||||||
bool isConvex = (infoByte & convexMask) >> 7;
|
bool isConvex = (infoByte & convexMask) >> 7;
|
||||||
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{};
|
||||||
char positionByte;
|
|
||||||
for (int i = 0; i < polyominoSize; i++) {
|
for (int j = 0; j < bytesNeeded; j++) {
|
||||||
piecesFile.get(positionByte);
|
buffer >> positionByte;
|
||||||
int x = (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);
|
||||||
}
|
}
|
||||||
@@ -119,6 +148,11 @@ 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::exists(dataFolderPath)) {
|
||||||
|
std::filesystem::create_directories(dataFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
if (!std::filesystem::is_directory(dataFolderPath)) {
|
if (!std::filesystem::is_directory(dataFolderPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,22 @@ class PiecesFiles {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a file containing all the pieces of the specified size
|
* Generate a file containing all the pieces of the specified size
|
||||||
* @return If the file could be created
|
* @return If the pieces are saved correctly
|
||||||
*/
|
*/
|
||||||
bool savePieces(int polyominoSize) const;
|
[[nodiscard]] bool savePieces(int polyominoSize) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the content of the vectors by the pieces of the specified size, if the file wasn't found the vectors stays untouched
|
* Generate a file containing all the pieces of the specified size, assuming they have been correctly generated
|
||||||
* @return If the file was found
|
* @return If the piece are saved correctly
|
||||||
*/
|
*/
|
||||||
bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
[[nodiscard]] bool savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the content of the vectors by the pieces of the specified size, if the pieces can't be loaded correctly the vectors stays untouched
|
||||||
|
* @return If the pieces could be loaded correctly
|
||||||
|
*/
|
||||||
|
[[nodiscard]] 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
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
#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;
|
||||||
int maxY = INT_MIN;
|
int maxY = INT_MIN;
|
||||||
for (Position position : positions) {
|
for (const Position position : positions) {
|
||||||
if (position.x < minX) minX = position.x;
|
if (position.x < minX) minX = position.x;
|
||||||
if (position.x > maxX) maxX = position.x;
|
if (position.x > maxX) maxX = position.x;
|
||||||
if (position.y < minY) minY = position.y;
|
if (position.y < minY) minY = position.y;
|
||||||
@@ -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 (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 (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 (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 (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 (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 (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
|
||||||
@@ -156,19 +149,20 @@ void Polyomino::goToSpawnPosition() {
|
|||||||
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 (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 (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());
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
public:
|
||||||
|
static const std::size_t MAX_LENGTH = 16;
|
||||||
|
using PolyominoData = std::bitset<MAX_LENGTH * MAX_LENGTH>;
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
std::set<Position> positions; // the squares composing the polyomino, (0,0) is downleft
|
void updatePosition() {
|
||||||
int length; // the size of the smallest square box in which the polyomino can fit on any rotation
|
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:
|
public:
|
||||||
/**
|
|
||||||
* Creates a polyomino with the specified positions and normalizes it, wheter it is actually a polyonimo is not checked
|
|
||||||
*/
|
|
||||||
Polyomino(const std::set<Position>& positions);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
@@ -74,19 +116,29 @@ class Polyomino {
|
|||||||
/**
|
/**
|
||||||
* 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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -3,23 +3,36 @@
|
|||||||
#include "TextApp.h"
|
#include "TextApp.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||||
|
|
||||||
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 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
|
// CHECK PIECES FILES
|
||||||
//generateFilesForAllSizes(10);
|
|
||||||
|
PiecesFiles pf;
|
||||||
|
bool warned = false;
|
||||||
|
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||||
|
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||||
|
if (!warned) {
|
||||||
|
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
|
||||||
|
warned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pf.savePieces(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LAUNCH APP
|
||||||
|
|
||||||
TextApp UI;
|
TextApp UI;
|
||||||
UI.run();
|
UI.run();
|
||||||
@@ -28,20 +41,20 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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 +76,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 +101,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;
|
||||||
|
|||||||
97
src/Utils/AssetManager.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include "./AssetManager.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
static const unsigned char data_fonts_pressstart_prstart_ttf[] = {
|
||||||
|
#include <data/fonts/pressstart/prstart.ttf.h>
|
||||||
|
};
|
||||||
|
|
||||||
|
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)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Asset& getResource(const std::string& fileName) {
|
||||||
|
return getResource(assetMap.at(fileName));
|
||||||
|
}
|
||||||
30
src/Utils/AssetManager.h
Normal 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_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);
|
||||||
32
xmake.lua
@@ -1,6 +1,8 @@
|
|||||||
add_rules("mode.debug", "mode.release")
|
add_rules("mode.debug", "mode.release")
|
||||||
|
|
||||||
add_requires("sfml 3.0.0")
|
includes("xmake/bin2c.lua")
|
||||||
|
|
||||||
|
add_requires("sfml 3.0.0", "zlib")
|
||||||
|
|
||||||
set_languages("c++20")
|
set_languages("c++20")
|
||||||
|
|
||||||
@@ -8,21 +10,43 @@ set_rundir(".")
|
|||||||
|
|
||||||
target("core")
|
target("core")
|
||||||
set_kind("$(kind)")
|
set_kind("$(kind)")
|
||||||
add_files("src/Pieces/*.cpp")
|
add_files("src/Pieces/*.cpp", "src/Core/*.cpp", "src/Common/*.cpp")
|
||||||
add_files("src/Core/*.cpp")
|
add_packages("zlib")
|
||||||
|
|
||||||
target("text")
|
target("text")
|
||||||
set_kind("binary")
|
|
||||||
set_default(false)
|
set_default(false)
|
||||||
|
set_kind("binary")
|
||||||
add_files("./src/TextUI/*.cpp")
|
add_files("./src/TextUI/*.cpp")
|
||||||
add_deps("core")
|
add_deps("core")
|
||||||
|
|
||||||
|
target("bmark")
|
||||||
|
set_default(false)
|
||||||
|
set_kind("binary")
|
||||||
|
add_files("./src/Benchmark/*.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")
|
||||||
|
|
||||||
|
if is_mode("debug") then
|
||||||
|
add_defines("DEBUG")
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_plat("mingw") then
|
||||||
|
add_ldflags("-static-libstdc++", "-static")
|
||||||
|
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
|
||||||
--
|
--
|
||||||
|
|||||||
124
xmake/bin2c.lua
Normal 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)
|
||||||