Compare commits
13 Commits
assets
...
e676cd19f6
| Author | SHA1 | Date | |
|---|---|---|---|
| e676cd19f6 | |||
| 8342bf3969 | |||
| 34d781fcfe | |||
| b17a40fda5 | |||
| 17459c4026 | |||
| 3e40ff7252 | |||
| dd8314f76c | |||
| d7f52239f0 | |||
| 72f4ea75ff | |||
| b1b7277666 | |||
| fd6bdc2b09 | |||
| 69b91d6497 | |||
| 6ed85869ae |
56
README.md
56
README.md
@@ -24,11 +24,12 @@ You will find more infos about the Rotation System, the scoring system, or the d
|
||||
|
||||
- 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!
|
||||
- 0° rotations!
|
||||
- AutoRS as the Rotation System!
|
||||
- IRS, IHS, infinite hold, and other leniency mechanics!
|
||||
- Very bland interface!! (i'm not a designer)
|
||||
|
||||
### Available gamemodes
|
||||
@@ -37,6 +38,7 @@ You will find more infos about the Rotation System, the scoring system, or the d
|
||||
- MARATHON : clear 200 lines with increasing gravity!
|
||||
- ULTRA : scores as much as possible in only 2 minutes!
|
||||
- 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!
|
||||
|
||||
### Screenshots
|
||||
@@ -77,9 +79,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 (not updated):
|
||||
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.
|
||||
|
||||
### Package the project
|
||||
|
||||
@@ -90,29 +93,31 @@ If for some reasons you wanna run the command line version (not updated):
|
||||
### One-sided n-polyominoes
|
||||
|
||||
| n | Number | Generation | File storing | File retrieving | File size |
|
||||
| - | - | - | - | - | - |
|
||||
| 1 | 1 | 0s 0.005622ms | 0s 0.750955ms | 0s 0.014016ms | 2 bytes |
|
||||
| 2 | 1 | 0s 0.005758ms | 0s 0.181323ms | 0s 0.012256ms | 3 bytes |
|
||||
| 3 | 2 | 0s 0.017525ms | 0s 0.054497ms | 0s 0.006431ms | 8 bytes |
|
||||
| 4 | 7 | 0s 0.050554ms | 0s 0.067617ms | 0s 0.010984ms | 35 bytes |
|
||||
| 5 | 18 | 0s 0.191971ms | 0s 0.109905ms | 0s 0.021234ms | 108 bytes |
|
||||
| 6 | 60 | 0s 0.651757ms | 0s 0.327465ms | 0s 0.04558ms | 420 bytes |
|
||||
| 7 | 196 | 0s 3.38847ms | 0s 1.94434ms | 0s 0.258777ms | 1568 bytes |
|
||||
| 8 | 704 | 0s 22.7411ms | 0s 10.0103ms | 0s 1.34813ms | 6336 bytes |
|
||||
| 9 | 2500 | 0s 66.2949ms | 0s 20.6137ms | 0s 2.56374ms | 25000 bytes |
|
||||
| 10 | 9189 | 0s 194.764ms | 0s 84.5884ms | 0s 9.64467ms | 101079 bytes |
|
||||
| 11 | 33896 | 0s 759.182ms | 0s 378.494ms | 0s 44.1424ms | 406752 bytes |
|
||||
| 12 | 126759 | 2s 709.277ms | 1s 530.34ms | 0s 155ms | 1647867 bytes |
|
||||
| 13 | 476270 | 10s 668.308ms | 7s 395.512ms | 0s 765.601ms | 6667780 bytes |
|
||||
| 14 | 1802312 | 45s 606.597ms | 32s 28.7977ms | 2s 919.653ms | 27034680 bytes |
|
||||
| 15 | ~6M | ~5mn | ~5mn | ~10s | ~100 MB |
|
||||
| -: | -: | :-: | :-: | :-: | -: |
|
||||
| 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 normalizing and sorting all polyominoes before writing them to the file._
|
||||
_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.
|
||||
If you want to know more details about the generation and storage of polyominoes, [check the documentation](/doc/)!
|
||||
|
||||
Run it yourself by typing:
|
||||
``xmake build benchmark``
|
||||
``xmake run benchmark``
|
||||
``xmake f -m release``
|
||||
``xmake build bmark``
|
||||
``xmake run bmark``
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -121,3 +126,6 @@ Font used: [Press Start](https://www.zone38.net/font/#pressstart).
|
||||
|
||||
Inspired by other modern stacker games such as Techmino, jstris, tetr.io, etc.
|
||||
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 and src/Utils comes from him.
|
||||
|
||||
102
src/Benchmark/main.cpp
Normal file
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
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
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
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
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
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
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);
|
||||
};
|
||||
@@ -297,8 +297,8 @@ void Game::lockPiece() {
|
||||
|
||||
/* clearing one more line is worth 2x more
|
||||
clearing with a spin is worth as much as clearing 2x more lines */
|
||||
long int clearScore = LINE_CLEAR_BASE_SCORE;
|
||||
clearScore = clearScore << (clear.lines << (clear.isSpin));
|
||||
long int clearScore = LINE_CLEAR_BASE_SCORE / 2;
|
||||
clearScore = clearScore << (clear.lines << clear.isSpin);
|
||||
|
||||
if (this->B2BChain && B2BConditionsAreMet) {
|
||||
clearScore *= B2B_SCORE_MULTIPLIER;
|
||||
@@ -366,6 +366,10 @@ bool Game::areBlocksBones() const {
|
||||
return this->parameters.getBoneBlocks();
|
||||
}
|
||||
|
||||
bool Game::isBoardInvisible() const {
|
||||
return this->parameters.getInvisibleBoard();
|
||||
}
|
||||
|
||||
const Board& Game::getBoard() const {
|
||||
return this->board.getBoard();
|
||||
}
|
||||
|
||||
@@ -136,6 +136,11 @@ class Game {
|
||||
*/
|
||||
bool areBlocksBones() const;
|
||||
|
||||
/**
|
||||
* @return If the board is currently invisible
|
||||
*/
|
||||
bool isBoardInvisible() const;
|
||||
|
||||
/**
|
||||
* @return The board
|
||||
*/
|
||||
|
||||
@@ -24,6 +24,8 @@ void GameParameters::reset() {
|
||||
case MARATHON : {this->level = 1; break;}
|
||||
// goes from level 20 to 39
|
||||
case MASTER : {this->level = 20; break;}
|
||||
// goes from level 1 to 19
|
||||
case INVISIBLE : {this->level = 1; break;}
|
||||
// no gravity
|
||||
case ZEN : {this->level = 0; break;}
|
||||
default : this->level = 1;
|
||||
@@ -34,7 +36,7 @@ void GameParameters::reset() {
|
||||
|
||||
void GameParameters::lockedPiece(const LineClear& lineClear) {
|
||||
switch (this->gamemode) {
|
||||
// modes where level increases
|
||||
// modes where level increases with lines
|
||||
case MARATHON :
|
||||
case MASTER : {
|
||||
int previousLines = this->clearedLines;
|
||||
@@ -51,9 +53,23 @@ void GameParameters::lockedPiece(const LineClear& lineClear) {
|
||||
default : this->clearedLines += lineClear.lines;
|
||||
}
|
||||
|
||||
int previousGrade = this->grade;
|
||||
if (!((lineClear.lines == 0) && ((this->grade % 100) == 99))) {
|
||||
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 {
|
||||
@@ -66,6 +82,8 @@ bool GameParameters::hasWon(int framesPassed) const {
|
||||
case MARATHON : return this->clearedLines >= 200;
|
||||
// win once 200 lines have been cleared
|
||||
case MASTER : return this->clearedLines >= 200;
|
||||
// win once 1000 grade has been passed
|
||||
case INVISIBLE : return this->grade >= 1000;
|
||||
// infinite mode
|
||||
case ZEN :
|
||||
default : return false;
|
||||
@@ -84,7 +102,8 @@ void GameParameters::updateStats() {
|
||||
}
|
||||
// 3 for slow-controls gamemodes
|
||||
case MARATHON :
|
||||
case MASTER : {
|
||||
case MASTER :
|
||||
case INVISIBLE : {
|
||||
this->nextQueueLength = 3;
|
||||
break;
|
||||
}
|
||||
@@ -94,10 +113,13 @@ void GameParameters::updateStats() {
|
||||
/* BONE BLOCKS */
|
||||
switch (this->gamemode) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
/* INVISIBLE */
|
||||
this->invisibleBoard = (this->gamemode == INVISIBLE);
|
||||
|
||||
/* GRAVITY */
|
||||
// get gravity for an assumed 20-rows board
|
||||
static const int gravityPerLevel[] = {
|
||||
@@ -152,6 +174,8 @@ void GameParameters::updateStats() {
|
||||
case MARATHON : {this->ARE = 24 - (this->level - 1); break;}
|
||||
// starts at 400ms (24f) at lvl 20 and ends at 083ms (5f) at lvl 39
|
||||
case MASTER : {this->ARE = 24 - (this->level - 20); break;}
|
||||
// fixed at 250ms (15f)
|
||||
case INVISIBLE : {this->ARE = 15; break;}
|
||||
// no ARE by default
|
||||
default : this->ARE = 0;
|
||||
}
|
||||
@@ -228,6 +252,10 @@ bool GameParameters::getBoneBlocks() const {
|
||||
return this->boneBlocks;
|
||||
}
|
||||
|
||||
bool GameParameters::getInvisibleBoard() const {
|
||||
return this->invisibleBoard;
|
||||
}
|
||||
|
||||
int GameParameters::getGravity() const {
|
||||
return this->gravity;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class GameParameters {
|
||||
int grade; // the current amount of points
|
||||
int nextQueueLength; // the number of pieces visibles in the next queue
|
||||
bool boneBlocks; // wheter all blocks are bone blocks
|
||||
bool invisibleBoard; // wheter the board is invisible
|
||||
int gravity; // the gravity at which pieces drop
|
||||
int lockDelay; // the time before the piece lock in place
|
||||
int forcedLockDelay; // the forced time before the piece lock in place
|
||||
@@ -77,9 +78,14 @@ class GameParameters {
|
||||
int getNextQueueLength() const;
|
||||
|
||||
/**
|
||||
* Returns wheter the blocks are currently bone blocks
|
||||
* @return Wheter the blocks are currently bone blocks
|
||||
*/
|
||||
bool getBoneBlocks() const;
|
||||
|
||||
/**
|
||||
* @return Wheter the board is currently invisible
|
||||
*/
|
||||
bool getInvisibleBoard() const;
|
||||
|
||||
/**
|
||||
* @return The current gravity for a 20-line high board
|
||||
|
||||
@@ -11,6 +11,7 @@ enum Gamemode {
|
||||
MARATHON,
|
||||
ULTRA,
|
||||
MASTER,
|
||||
INVISIBLE,
|
||||
ZEN
|
||||
};
|
||||
|
||||
@@ -24,6 +25,7 @@ inline std::string getGamemodeName(Gamemode gamemode) {
|
||||
"MARATHON",
|
||||
"ULTRA",
|
||||
"MASTER",
|
||||
"INVISIBLE",
|
||||
"ZEN"
|
||||
};
|
||||
|
||||
@@ -39,6 +41,7 @@ inline std::string getGamemodeGoal(Gamemode gamemode) {
|
||||
"200 lines",
|
||||
"2 minutes",
|
||||
"200 lines",
|
||||
"1000 grade",
|
||||
"Infinite"
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Player.h"
|
||||
#include "Game.h"
|
||||
|
||||
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
||||
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the app were choosen with this number in mind
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,13 +27,12 @@ PiecesList::PiecesList() {
|
||||
this->pushBackEmptyVectors();
|
||||
}
|
||||
|
||||
bool PiecesList::loadPieces(int size) {
|
||||
if (size < 1) return false;
|
||||
if (size <= this->highestLoadedSize) return true;
|
||||
bool PiecesList::loadPieces(int max_size) {
|
||||
if (max_size < 1) return false;
|
||||
if (max_size <= this->highestLoadedSize) return true;
|
||||
|
||||
PiecesFiles piecesFiles;
|
||||
for (int i = this->highestLoadedSize + 1; i <= size; i++) {
|
||||
|
||||
for (int 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))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,10 @@ class PiecesList {
|
||||
PiecesList();
|
||||
|
||||
/**
|
||||
* Makes the list load all pieces of the specified size
|
||||
* @return If it sucessfully loaded the pieces
|
||||
* Makes the list load all pieces up to the specified size
|
||||
* @return If all pieces up to the specified size are correctly loaded
|
||||
*/
|
||||
bool loadPieces(int size);
|
||||
[[nodiscard]] bool loadPieces(int max_size);
|
||||
|
||||
/**
|
||||
* Selects the specified piece
|
||||
|
||||
@@ -1,12 +1,72 @@
|
||||
#include "AppMenu.h"
|
||||
|
||||
#include "../Settings.h"
|
||||
#include "../PlayerCursor.h"
|
||||
#include "../../Utils/AssetManager.h"
|
||||
|
||||
AppMenu::AppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) : menuStack(menuStack),
|
||||
settings(settings),
|
||||
renderWindow(renderWindow)
|
||||
{
|
||||
#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));
|
||||
}
|
||||
|
||||
@@ -31,54 +31,11 @@ class AppMenu {
|
||||
virtual void drawFrame() const = 0;
|
||||
|
||||
protected:
|
||||
void updateMetaBinds() {
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Enter)) {
|
||||
enterPressed = true;
|
||||
enterReleased = false;
|
||||
}
|
||||
else {
|
||||
enterReleased = enterPressed;
|
||||
enterPressed = false;
|
||||
}
|
||||
void updateMetaBinds();
|
||||
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape)) {
|
||||
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 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();
|
||||
void placeTitle(sf::Text& text, const std::optional<PlayerCursor>& playerCursor, const sf::String& string, float yPos, const std::optional<sf::Vector2u>& cursorPos) const;
|
||||
|
||||
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 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 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::Color getColorOfBlock(Block block, int luminosityShift) const;
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ GameDistributionAppMenu::GameDistributionAppMenu(std::shared_ptr<MenuStack> menu
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1}) {
|
||||
|
||||
for (int i = 1; i <= this->settings->getMaximumPiecesSize(); i++) {
|
||||
for (int i = 1; i <= this->settings->getLoadablePiecesSize(); i++) {
|
||||
this->playerCursor.addRow(i, 1);
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ void GameDistributionAppMenu::drawFrame() const {
|
||||
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->getMaximumPiecesSize() - 3);
|
||||
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});
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ GamePiecesAppMenu::GamePiecesAppMenu(std::shared_ptr<MenuStack> menuStack, std::
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({1, (unsigned int) this->settings->getSelectedPieces().size() + 1u}) {
|
||||
|
||||
for (int i = 1; i <= this->settings->getMaximumPiecesSize(); i++) {
|
||||
for (int i = 1; i <= this->settings->getLoadablePiecesSize(); i++) {
|
||||
this->playerCursor.addRow(i + 1, this->settings->getMenu().readPiecesList().getNumberOfPieces(i) + 4);
|
||||
}
|
||||
|
||||
if (this->settings->getMaximumPiecesSize() < MAXIMUM_PIECES_SIZE) {
|
||||
this->playerCursor.addRow(this->settings->getMaximumPiecesSize() + 2, 1);
|
||||
if (this->settings->getLoadablePiecesSize() < MAXIMUM_PIECES_SIZE) {
|
||||
this->playerCursor.addRow(this->settings->getLoadablePiecesSize() + 2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ void GamePiecesAppMenu::computeFrame() {
|
||||
this->menuStack->push(std::make_shared<GameDistributionAppMenu>(this->menuStack, this->settings, this->renderWindow));
|
||||
}
|
||||
|
||||
if (this->playerCursor.getPosition().y == (this->settings->getMaximumPiecesSize() + 2)) {
|
||||
int newMaxSize = this->settings->getMaximumPiecesSize() + 1;
|
||||
if (this->playerCursor.getPosition().y == (this->settings->getLoadablePiecesSize() + 2)) {
|
||||
int newMaxSize = this->settings->getLoadablePiecesSize() + 1;
|
||||
this->settings->loadPieces(newMaxSize);
|
||||
|
||||
this->playerCursor.removeRow(newMaxSize + 1);
|
||||
@@ -95,8 +95,8 @@ void GamePiecesAppMenu::drawFrame() const {
|
||||
this->drawSelectedPiecesRow(15.f);
|
||||
|
||||
bool drawFromFirstElem = (this->playerCursor.getPosition().y == 1);
|
||||
bool addExtraLine = (this->settings->getMaximumPiecesSize() < MAXIMUM_PIECES_SIZE);
|
||||
int firstElem = std::clamp(((int) this->playerCursor.getPosition().y) - 2, 1, this->settings->getMaximumPiecesSize() - 2 + (addExtraLine ? 1 : 0));
|
||||
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);
|
||||
@@ -130,7 +130,7 @@ void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
|
||||
int pieceSize = getSizeOfPieces(pieceType);
|
||||
if (pieceSize > 0) {
|
||||
if (!(pieceSize > this->settings->getMaximumPiecesSize())) {
|
||||
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());
|
||||
@@ -143,7 +143,7 @@ void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(value > this->settings->getMaximumPiecesSize())) {
|
||||
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()));
|
||||
}
|
||||
@@ -158,7 +158,7 @@ void GamePiecesAppMenu::drawSelectedPiecesRow(float yPos) const {
|
||||
}
|
||||
|
||||
void GamePiecesAppMenu::drawRow(int piecesSize, float yPos, bool drawFromFirstElem) const {
|
||||
if (piecesSize > this->settings->getMaximumPiecesSize()) {
|
||||
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)) {
|
||||
|
||||
@@ -104,17 +104,26 @@ void GamePlayingAppMenu::computeFrame() {
|
||||
|
||||
void GamePlayingAppMenu::drawFrame() const {
|
||||
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);
|
||||
float cellOutlineThickness = this->cellSizeZoom / 4;
|
||||
bool drawActivePiece = (this->game.getActivePiece() != nullptr) && (!this->game.hasLost());
|
||||
|
||||
// board
|
||||
for (int y = this->game.getBoard().getBaseHeight() + 9; y >= 0; y--) {
|
||||
for (int x = 0; x < this->game.getBoard().getWidth(); x++) {
|
||||
Block block = this->game.getBoard().getBlock(Position{x, y});
|
||||
if (isBoardInvisible) block = NOTHING;
|
||||
|
||||
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));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
@@ -122,7 +131,10 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
|
||||
if (drawActivePiece) {
|
||||
// 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()) {
|
||||
Position cellPosition = (this->game.getGhostPiecePosition() + position);
|
||||
|
||||
@@ -133,13 +145,13 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
}
|
||||
|
||||
// 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()));
|
||||
|
||||
for (const Position& position : this->game.getActivePiece()->getPositions()) {
|
||||
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||
|
||||
sf::RectangleShape cell(cellSize);
|
||||
cell.setOutlineThickness(pieceOutlineSize);
|
||||
cell.setOutlineThickness(cellOutlineThickness);
|
||||
cell.setOutlineColor(pieceOultlineColor);
|
||||
cell.setPosition(this->getBoardBlockPosition(cellPosition.x, cellPosition.y));
|
||||
this->renderWindow->draw(cell);
|
||||
@@ -154,7 +166,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
|
||||
if (drawActivePiece) {
|
||||
// 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()) {
|
||||
Position cellPosition = (this->game.getActivePiecePosition() + position);
|
||||
@@ -173,7 +187,9 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
nextBox.position.y -= upShift;
|
||||
|
||||
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);
|
||||
|
||||
int lowestRank = 0;
|
||||
@@ -181,7 +197,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
for (int x = 0; x < this->game.getNextPieces().at(i).getLength(); x++) {
|
||||
sf::RectangleShape cell(nextCellSize);
|
||||
if (this->game.getNextPieces().at(i).getPositions().contains(Position{x, y})) {
|
||||
cell.setFillColor(color);
|
||||
cell.setFillColor(pieceColor);
|
||||
lowestRank = y;
|
||||
}
|
||||
else {
|
||||
@@ -199,9 +215,11 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
// hold box
|
||||
if (this->game.getHeldPiece() != nullptr) {
|
||||
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);
|
||||
|
||||
|
||||
for (int y = 0; y < this->game.getHeldPiece()->getLength(); y++) {
|
||||
for (int x = 0; x < this->game.getHeldPiece()->getLength(); x++) {
|
||||
sf::RectangleShape cell(holdCellSize);
|
||||
@@ -212,7 +230,7 @@ void GamePlayingAppMenu::drawFrame() const {
|
||||
cell.setFillColor(boxColor);
|
||||
}
|
||||
cell.setPosition(sf::Vector2f(this->holdBoxPosition.position.x + (x * this->nextCellSizeZoom),
|
||||
this->holdBoxPosition.position.y + ((this->game.getHeldPiece()->getLength() - y - 1) * this->holdCellSizeZoom)));
|
||||
this->holdBoxPosition.position.y + ((this->game.getHeldPiece()->getLength() - y - 1) * this->holdCellSizeZoom)));
|
||||
this->renderWindow->draw(cell);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
GameSettingsAppMenu::GameSettingsAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({2, 3, 2}) {
|
||||
playerCursor({2, 3, 3}) {
|
||||
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ void GameSettingsAppMenu::computeFrame() {
|
||||
case 2 : {
|
||||
switch (this->playerCursor.getPosition().x) {
|
||||
case 0 : {this->settings->setGamemode(MASTER); break;}
|
||||
case 1 : {this->settings->setGamemode(ZEN); break;}
|
||||
case 1 : {this->settings->setGamemode(INVISIBLE); break;}
|
||||
case 2 : {this->settings->setGamemode(ZEN); break;}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -76,7 +77,8 @@ void GameSettingsAppMenu::drawFrame() const {
|
||||
this->placeText(text, this->playerCursor, "MARATHON", 25.f, 35.f, sf::Vector2u{1, 1});
|
||||
this->placeText(text, this->playerCursor, "ULTRA", 50.f, 35.f, sf::Vector2u{2, 1});
|
||||
this->placeText(text, this->playerCursor, "MASTER", 5.f, 45.f, sf::Vector2u{0, 2});
|
||||
this->placeText(text, this->playerCursor, "ZEN", 25.f, 45.f, sf::Vector2u{1, 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();
|
||||
}
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow) :
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({INFO_SECTIONS_COUNT}),
|
||||
sectionsName(
|
||||
sectionsName({
|
||||
"< ABOUT >",
|
||||
"< PIECES TYPES >",
|
||||
"< 0 DEGREES ROTATIONS >",
|
||||
"< ROTATION SYSTEM >",
|
||||
"< SCORING >"
|
||||
),
|
||||
sectionsContent(
|
||||
}),
|
||||
sectionsContent({
|
||||
"This game is written in C++,\n"
|
||||
"using SFML 3 for the GUI.\n"
|
||||
"It has been inspired by other\n"
|
||||
@@ -67,7 +67,7 @@ InfoAppMenu::InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<S
|
||||
"A spin is detected when the piece is\n"
|
||||
"locked in place, a mini-spin simply\n"
|
||||
"when the last move was a kick."
|
||||
) {
|
||||
}) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,11 @@
|
||||
|
||||
static const int INFO_SECTIONS_COUNT = 5;
|
||||
|
||||
|
||||
class InfoAppMenu : public AppMenu {
|
||||
private:
|
||||
PlayerCursor playerCursor;
|
||||
sf::String sectionsName[INFO_SECTIONS_COUNT];
|
||||
sf::String sectionsContent[INFO_SECTIONS_COUNT];
|
||||
std::array<std::string, INFO_SECTIONS_COUNT> sectionsName;
|
||||
std::array<std::string, INFO_SECTIONS_COUNT> sectionsContent;
|
||||
|
||||
public:
|
||||
InfoAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared_ptr<Settings> settings, std::shared_ptr<sf::RenderWindow> renderWindow);
|
||||
|
||||
@@ -14,7 +14,7 @@ StartUpAppMenu::StartUpAppMenu(std::shared_ptr<MenuStack> menuStack, std::shared
|
||||
AppMenu(menuStack, settings, renderWindow),
|
||||
playerCursor({MAXIMUM_PIECES_SIZE + 1}) {
|
||||
|
||||
this->playerCursor.goToPosition({(unsigned int) std::clamp(this->settings->getMaximumPiecesSize(), MINIMUM_PIECES_SIZE, MAXIMUM_PIECES_SIZE), 0u});
|
||||
this->playerCursor.goToPosition({(unsigned int) std::clamp(this->settings->getLoadablePiecesSize(), MINIMUM_PIECES_SIZE, MAXIMUM_PIECES_SIZE), 0u});
|
||||
}
|
||||
|
||||
void StartUpAppMenu::computeFrame() {
|
||||
|
||||
@@ -27,41 +27,41 @@ Settings::Settings(bool loadPieces) {
|
||||
this->loadSettingsFromFile(loadPieces, {});
|
||||
}
|
||||
|
||||
void Settings::loadPieces(int maximumPiecesSizeRequest) {
|
||||
if (maximumPiecesSizeRequest < MINIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MINIMUM_PIECES_SIZE;
|
||||
void Settings::loadPieces(int loadablePiecesSizeRequest) {
|
||||
if (loadablePiecesSizeRequest < MINIMUM_PIECES_SIZE) {
|
||||
loadablePiecesSizeRequest = MINIMUM_PIECES_SIZE;
|
||||
}
|
||||
else if (maximumPiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||
maximumPiecesSizeRequest = MAXIMUM_PIECES_SIZE;
|
||||
else if (loadablePiecesSizeRequest > MAXIMUM_PIECES_SIZE) {
|
||||
loadablePiecesSizeRequest = MAXIMUM_PIECES_SIZE;
|
||||
}
|
||||
|
||||
bool succeeded = true;
|
||||
int i = 1;
|
||||
while (succeeded && (i <= maximumPiecesSizeRequest)) {
|
||||
while (succeeded && (i <= loadablePiecesSizeRequest)) {
|
||||
succeeded = this->menu.getPiecesList().loadPieces(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (succeeded) {
|
||||
this->maximumPiecesSize = maximumPiecesSizeRequest;
|
||||
this->loadablePiecesSize = loadablePiecesSizeRequest;
|
||||
}
|
||||
this->loadedPieces = succeeded;
|
||||
}
|
||||
|
||||
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest) {
|
||||
void Settings::loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest) {
|
||||
std::ifstream settingsFile("data/config/settings.bin", std::ios::binary);
|
||||
char byte;
|
||||
|
||||
// file format version
|
||||
settingsFile.get(byte);
|
||||
|
||||
// maximum pieces size
|
||||
// loadable pieces size
|
||||
settingsFile.get(byte);
|
||||
this->maximumPiecesSize = byte;
|
||||
this->loadablePiecesSize = byte;
|
||||
|
||||
if (loadPieces) {
|
||||
if (maximumPiecesSizeRequest.has_value()) {
|
||||
this->loadPieces(maximumPiecesSizeRequest.value());
|
||||
if (loadablePiecesSizeRequest.has_value()) {
|
||||
this->loadPieces(loadablePiecesSizeRequest.value());
|
||||
}
|
||||
else {
|
||||
this->loadPieces(byte);
|
||||
@@ -165,8 +165,8 @@ void Settings::saveSettingsToFile() const {
|
||||
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// maximum pieces size
|
||||
byte = this->maximumPiecesSize;
|
||||
// loadable pieces size
|
||||
byte = this->loadablePiecesSize;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// keybind layout
|
||||
@@ -321,7 +321,7 @@ void Settings::confirmSelectedPieces() {
|
||||
int size = getSizeOfPieces(type);
|
||||
|
||||
if (size == 0) {
|
||||
if (!(value > this->maximumPiecesSize)) {
|
||||
if (!(value > this->loadablePiecesSize)) {
|
||||
switch (type) {
|
||||
case CONVEX_PIECES : {this->menu.getPiecesList().selectConvexPieces(value); break;}
|
||||
case HOLELESS_PIECES : {this->menu.getPiecesList().selectHolelessPieces(value); break;}
|
||||
@@ -332,7 +332,7 @@ void Settings::confirmSelectedPieces() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(getSizeOfPieces(type) > this->maximumPiecesSize)) {
|
||||
if (!(getSizeOfPieces(type) > this->loadablePiecesSize)) {
|
||||
this->menu.getPiecesList().selectPiece(size, value);
|
||||
selectedNone = false;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ void Settings::confirmSelectedPieces() {
|
||||
|
||||
bool Settings::increaseDistribution(int size) {
|
||||
if (!this->loadedPieces) return false;
|
||||
if (size < 1 || size > this->maximumPiecesSize) return false;
|
||||
if (size < 1 || size > this->loadablePiecesSize) return false;
|
||||
|
||||
if (this->distributions.at(size) < DISTRIBUTION_MAX) {
|
||||
this->distributions.at(size)++;
|
||||
@@ -358,7 +358,7 @@ bool Settings::increaseDistribution(int size) {
|
||||
|
||||
bool Settings::decreaseDistribution(int size) {
|
||||
if (!this->loadedPieces) return false;
|
||||
if (size < 1 || size > this->maximumPiecesSize) return false;
|
||||
if (size < 1 || size > this->loadablePiecesSize) return false;
|
||||
|
||||
if (this->distributions.at(size) > 0) {
|
||||
this->distributions.at(size)--;
|
||||
@@ -383,8 +383,8 @@ Keybinds& Settings::getKeybinds() {
|
||||
return this->keybinds.at(this->chosenKeybinds);
|
||||
}
|
||||
|
||||
int Settings::getMaximumPiecesSize() const {
|
||||
return this->maximumPiecesSize;
|
||||
int Settings::getLoadablePiecesSize() const {
|
||||
return this->loadablePiecesSize;
|
||||
}
|
||||
|
||||
bool Settings::hasLoadedPieces() const {
|
||||
|
||||
@@ -29,7 +29,7 @@ static const std::pair<PiecesType, int> DEFAULT_SELECTION = {ALL_PIECES, MINIMUM
|
||||
class Settings {
|
||||
private:
|
||||
Menu menu;
|
||||
int maximumPiecesSize;
|
||||
int loadablePiecesSize;
|
||||
bool loadedPieces;
|
||||
std::vector<Keybinds> keybinds;
|
||||
int chosenKeybinds;
|
||||
@@ -42,9 +42,9 @@ class Settings {
|
||||
public:
|
||||
Settings(bool loadPieces);
|
||||
|
||||
void loadPieces(int maximumPiecesSizeRequest);
|
||||
void loadPieces(int loadablePiecesSizeRequest);
|
||||
|
||||
void loadSettingsFromFile(bool loadPieces, std::optional<int> maximumPiecesSizeRequest);
|
||||
void loadSettingsFromFile(bool loadPieces, std::optional<int> loadablePiecesSizeRequest);
|
||||
|
||||
void saveSettingsToFile() const;
|
||||
|
||||
@@ -82,7 +82,7 @@ class Settings {
|
||||
|
||||
Keybinds& getKeybinds();
|
||||
|
||||
int getMaximumPiecesSize() const;
|
||||
int getLoadablePiecesSize() const;
|
||||
|
||||
bool hasLoadedPieces() const;
|
||||
|
||||
|
||||
@@ -4,42 +4,69 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
void resetSettingsFile();
|
||||
void resetKeybindFile(int layout);
|
||||
[[nodiscard]] bool resetSettingsFile();
|
||||
[[nodiscard]] bool resetKeybindFile(int layout);
|
||||
|
||||
|
||||
int main() {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
bool everythingIsOK = true;
|
||||
|
||||
// CHECK PIECES FILES
|
||||
|
||||
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")) {
|
||||
#ifdef NDEBUG
|
||||
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;
|
||||
#ifndef DEBUG
|
||||
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
|
||||
|
||||
std::cout << "INFO: Pieces files for size " << i << " not found, generating..." << std::endl;
|
||||
pf.savePieces(i);
|
||||
everythingIsOK &= pf.savePieces(i);
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
std::cout << "IMPORTANT: you are currently in debug mode, if you wish to use bigger pieces, type 'xmake f -m release'." << std::endl;
|
||||
|
||||
#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 everythingGenerated = true;
|
||||
for (int i = DEBUG_PIECES_SIZE; i <= RELEASE_PIECES_SIZE; i++) {
|
||||
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")) {
|
||||
everythingGenerated = false;
|
||||
releasePiecesGenerated = false;
|
||||
}
|
||||
}
|
||||
if (!everythingGenerated) {
|
||||
std::cout << "NOTE : you do not have all pieces generated, generating can take several minutes." << std::endl;
|
||||
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;
|
||||
resetSettingsFile();
|
||||
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);
|
||||
@@ -48,39 +75,49 @@ int main() {
|
||||
settingsFile.get(byte);
|
||||
if ((unsigned char) byte < CURRENT_FILE_FORMAT_VERSION) {
|
||||
std::cout << "INFO: Files format changed, regenerating..." << std::endl;
|
||||
resetSettingsFile();
|
||||
everythingIsOK &= resetSettingsFile();
|
||||
for (int i = 0; i < NUMBER_OF_KEYBINDS; i++) {
|
||||
resetKeybindFile(i);
|
||||
everythingIsOK &= resetKeybindFile(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 << "INFO: Keybind file n°" << (i + 1) << "/" << NUMBER_OF_KEYBINDS << " not found, generating..." << std::endl;
|
||||
resetKeybindFile(i);
|
||||
}
|
||||
}
|
||||
// LAUNCH APP
|
||||
|
||||
GraphApp UI;
|
||||
UI.run();
|
||||
if (everythingIsOK) {
|
||||
GraphApp UI;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void resetSettingsFile() {
|
||||
std::filesystem::create_directories("data/config");
|
||||
std::ofstream settingsFile("data/config/settings.bin", std::ios::trunc | std::ios::binary);
|
||||
char byte;
|
||||
bool resetSettingsFile() {
|
||||
if (!std::filesystem::exists("data/config")) {
|
||||
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;
|
||||
|
||||
// file format version
|
||||
byte = CURRENT_FILE_FORMAT_VERSION;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
// maximum pieces size
|
||||
// loadable pieces size
|
||||
byte = MINIMUM_PIECES_SIZE;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
@@ -129,17 +166,35 @@ void resetSettingsFile() {
|
||||
}
|
||||
|
||||
// selected pieces
|
||||
byte = ALL_PIECES;
|
||||
byte = DEFAULT_SELECTION.first;
|
||||
settingsFile.write(&byte, 1);
|
||||
byte = 4;
|
||||
byte = DEFAULT_SELECTION.second;
|
||||
settingsFile.write(&byte, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void resetKeybindFile(int layout) {
|
||||
if (layout < 0 || layout > 4) return;
|
||||
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::filesystem::create_directories("data/config/keybinds/layout");
|
||||
std::ofstream layoutFile("data/config/keybinds/layout" + std::to_string(layout) + ".bin", std::ios::trunc | std::ios::binary);
|
||||
std::map<Action, sfKey> keybinds;
|
||||
|
||||
if (layout != 4) {
|
||||
@@ -205,4 +260,6 @@ void resetKeybindFile(int layout) {
|
||||
byte = 0xFF;
|
||||
layoutFile.write(&byte, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
|
||||
}
|
||||
|
||||
// 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 + 1, position.y}, nextAvaibleNumber, candidatePositions);
|
||||
this->tryToAddCandidatePosition(Position{position.x, position.y - 1}, nextAvaibleNumber, candidatePositions);
|
||||
@@ -58,7 +58,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
|
||||
}
|
||||
|
||||
// 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) {
|
||||
this->currentTestedShape.insert(key);
|
||||
this->generate(polyominoSize, val, nextAvaibleNumber, (polyominoSize == this->currentTestedShape.size()) ? std::map<Position, int>() : candidatePositions);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
#include "../Common/Compression.h"
|
||||
|
||||
|
||||
PiecesFiles::PiecesFiles() {
|
||||
@@ -21,10 +21,6 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
||||
if (!piecesFile.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Generator generator;
|
||||
std::vector<Polyomino> polyominoes = generator.generatePolyominoes(polyominoSize);
|
||||
@@ -37,10 +33,9 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyomin
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
std::ofstream piecesFile(filePath, std::ios::trunc | std::ios::binary);
|
||||
if (!piecesFile.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr std::size_t INITIAL_CAPACITY = 2048;
|
||||
DataBuffer buffer(INITIAL_CAPACITY);
|
||||
|
||||
// sorting the polyominoes is done after setting spawn position to ensure the order is always the same
|
||||
for (Polyomino& nMino : polyominoes) {
|
||||
@@ -50,18 +45,22 @@ bool PiecesFiles::savePieces(int polyominoSize, std::vector<Polyomino>& polyomin
|
||||
|
||||
for (const Polyomino& polyomino : polyominoes) {
|
||||
// write the characteristics of the piece
|
||||
char infoByte = (polyomino.isConvex() << 7) + (polyomino.hasHole() << 6) + polyomino.getLength();
|
||||
piecesFile.write(&infoByte, 1);
|
||||
bool isConvex = polyomino.isConvex();
|
||||
bool hasHole = (isConvex) ? false : polyomino.hasHole();
|
||||
std::uint8_t infoByte = (isConvex << 7) + (hasHole << 6) + polyomino.getLength();
|
||||
buffer << infoByte;
|
||||
|
||||
// write the positions of the piece
|
||||
char positionByte;
|
||||
std::uint8_t positionByte;
|
||||
for (const Position position : polyomino.getPositions()) {
|
||||
positionByte = (position.x << 4) + position.y;
|
||||
piecesFile.write(&positionByte, 1);
|
||||
buffer << positionByte;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -69,6 +68,13 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
if (!this->getFilePath(polyominoSize, filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataBuffer compressed;
|
||||
if(!compressed.ReadFile(filePath))
|
||||
return false;
|
||||
|
||||
DataBuffer buffer = Decompress(compressed, compressed.GetSize());
|
||||
|
||||
std::ifstream piecesFile(filePath, std::ios::binary);
|
||||
if (!piecesFile.good()) {
|
||||
return false;
|
||||
@@ -91,10 +97,11 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
char xMask = 0b1111'0000;
|
||||
char yMask = 0b0000'1111;
|
||||
|
||||
char infoByte;
|
||||
std::uint8_t infoByte;
|
||||
int i = 0;
|
||||
while (piecesFile.get(infoByte)) {
|
||||
if (piecesFile.eof()) break;
|
||||
while (!buffer.IsFinished()) {
|
||||
// if (piecesFile.eof()) break;
|
||||
buffer >> infoByte;
|
||||
|
||||
// read piece infos
|
||||
bool isConvex = (infoByte & convexMask) >> 7;
|
||||
@@ -103,9 +110,9 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
|
||||
// read positions
|
||||
std::set<Position> piecePositions;
|
||||
char positionByte;
|
||||
std::uint8_t positionByte;
|
||||
for (int i = 0; i < polyominoSize; i++) {
|
||||
piecesFile.get(positionByte);
|
||||
buffer >> positionByte;
|
||||
int x = ((unsigned char) positionByte & xMask) >> 4;
|
||||
int y = positionByte & yMask;
|
||||
piecePositions.insert(Position{x, y});
|
||||
@@ -135,11 +142,11 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
|
||||
bool PiecesFiles::getFilePath(int polyominoSize, std::string& filePath) const {
|
||||
std::string dataFolderPath = "data/pieces/";
|
||||
|
||||
if (!fs::exists(dataFolderPath)) {
|
||||
fs::create_directories(dataFolderPath);
|
||||
if (!std::filesystem::exists(dataFolderPath)) {
|
||||
std::filesystem::create_directories(dataFolderPath);
|
||||
}
|
||||
|
||||
if (!fs::is_directory(dataFolderPath)) {
|
||||
if (!std::filesystem::is_directory(dataFolderPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,21 +18,21 @@ class PiecesFiles {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Generate a file containing all the pieces of the specified size, assuming they have been correctly generated
|
||||
* @return If the file could be created
|
||||
* @return If the piece are saved correctly
|
||||
*/
|
||||
bool savePieces(int polyominoSize, std::vector<Polyomino>& polyominoes) 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 file wasn't found the vectors stays untouched
|
||||
* @return If the file was found
|
||||
* 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
|
||||
*/
|
||||
bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
||||
[[nodiscard]] bool loadPieces(int polyominoSize, std::vector<Piece>& pieces, std::vector<int>& convexPieces, std::vector<int>& holelessPieces, std::vector<int>& otherPieces) const;
|
||||
|
||||
/**
|
||||
* Puts the path to the piece file of the specified size in order, if the data folder wasn't found the string stays untouched
|
||||
|
||||
@@ -15,7 +15,7 @@ Polyomino::Polyomino(const std::set<Position>& positions) {
|
||||
int maxX = INT_MIN;
|
||||
int minY = INT_MAX;
|
||||
int maxY = INT_MIN;
|
||||
for (Position position : positions) {
|
||||
for (const Position position : positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.x > maxX) maxX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
@@ -40,13 +40,13 @@ Polyomino::Polyomino(const std::set<Position>& positions, int length) :
|
||||
void Polyomino::normalize() {
|
||||
int minX = INT_MAX;
|
||||
int minY = INT_MAX;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
}
|
||||
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
newPositions.insert(Position{position.x - minX, position.y - minY});
|
||||
}
|
||||
this->positions = std::move(newPositions);
|
||||
@@ -54,7 +54,7 @@ void Polyomino::normalize() {
|
||||
|
||||
void Polyomino::rotateCW() {
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
newPositions.insert(Position{position.y, (length - 1) - (position.x)});
|
||||
}
|
||||
this->positions = std::move(newPositions);
|
||||
@@ -62,7 +62,7 @@ void Polyomino::rotateCW() {
|
||||
|
||||
void Polyomino::rotate180() {
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
newPositions.insert(Position{(length - 1) - (position.x), (length - 1) - (position.y)});
|
||||
}
|
||||
this->positions = std::move(newPositions);
|
||||
@@ -70,7 +70,7 @@ void Polyomino::rotate180() {
|
||||
|
||||
void Polyomino::rotateCCW() {
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
newPositions.insert(Position{(length - 1) - (position.y), position.x});
|
||||
}
|
||||
this->positions = std::move(newPositions);
|
||||
@@ -89,7 +89,7 @@ void Polyomino::goToSpawnPosition() {
|
||||
}
|
||||
|
||||
// calculates amount of squares per rows and columns
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
linesCompleteness.at(0).at(position.y) += 1; // 0 = bottom to top = no rotation
|
||||
linesCompleteness.at(1).at((length - 1) - position.x) += 1; // 1 = right to left = CW
|
||||
linesCompleteness.at(2).at((length - 1) - position.y) += 1; // 2 = top to bottom = 180
|
||||
@@ -158,14 +158,14 @@ void Polyomino::goToSpawnPosition() {
|
||||
|
||||
int minX = INT_MAX;
|
||||
int minY = INT_MAX;
|
||||
for (Position position : this->positions) {
|
||||
for (const Position position : this->positions) {
|
||||
if (position.x < minX) minX = position.x;
|
||||
if (position.y < minY) minY = position.y;
|
||||
}
|
||||
|
||||
// center the piece with an up bias
|
||||
std::set<Position> newPositions;
|
||||
for (Position position : positions) {
|
||||
for (const Position position : positions) {
|
||||
newPositions.insert(Position{(position.x - minX) + (verticalEmptyLines / 2), (position.y - minY) + ((horizontalEmptyLines + 1) / 2)});
|
||||
}
|
||||
this->positions = std::move(newPositions);
|
||||
|
||||
@@ -7,37 +7,35 @@
|
||||
#include <cmath>
|
||||
|
||||
static const int MAXIMUM_PIECES_SIZE = 10;
|
||||
static const int BENCHMARK_PIECES_SIZE = 15;
|
||||
|
||||
|
||||
void testGeneratorForAllSizes(int max_size);
|
||||
void testGeneratorForOneSize(int size);
|
||||
void printPiecesByTypesForOneSize(int size);
|
||||
void readStatsFromFilesForAllSizes(int max_size);
|
||||
|
||||
void benchmarking(int min_size, int max_size);
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
#ifdef BENCHMARK
|
||||
benchmarking(1, BENCHMARK_PIECES_SIZE);
|
||||
#else
|
||||
// dev: generate files if it hasn't been done before, UI will NOT generate the files
|
||||
//generateFilesForAllSizes(10);
|
||||
// CHECK PIECES FILES
|
||||
|
||||
PiecesFiles pf;
|
||||
for (int i = 1; i <= MAXIMUM_PIECES_SIZE; i++) {
|
||||
if (!std::filesystem::exists("data/pieces/" + std::to_string(i) + "minos.bin")) {
|
||||
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;
|
||||
pf.savePieces(i);
|
||||
warned = true;
|
||||
}
|
||||
|
||||
pf.savePieces(i);
|
||||
}
|
||||
}
|
||||
|
||||
TextApp UI;
|
||||
UI.run();
|
||||
#endif
|
||||
// LAUNCH APP
|
||||
|
||||
TextApp UI;
|
||||
UI.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -118,66 +116,3 @@ void readStatsFromFilesForAllSizes(int max_size) {
|
||||
std::cout << "Others " << i << "-minos : " << otherPieces.size() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void benchmarking(int min_size, int max_size) {
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::duration;
|
||||
using std::chrono::milliseconds;
|
||||
|
||||
std::cout << "| n | Number | Generation | File storing | File retrieving | File size |" << std::endl;
|
||||
std::cout << "| - | - | - | - | - | - |" << std::endl;
|
||||
|
||||
Generator gen;
|
||||
PiecesFiles pf;
|
||||
|
||||
for (int i = min_size; i <= max_size; i++) {
|
||||
std::cout << "| " << i;
|
||||
|
||||
auto t1 = high_resolution_clock::now();
|
||||
std::vector<Polyomino> polyominoes = gen.generatePolyominoes(i);
|
||||
auto t2 = high_resolution_clock::now();
|
||||
duration<double, std::milli> ms_double = t2 - t1;
|
||||
std::cout << " | " << polyominoes.size();
|
||||
std::flush(std::cout);
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
t1 = high_resolution_clock::now();
|
||||
pf.savePieces(i, polyominoes);
|
||||
t2 = high_resolution_clock::now();
|
||||
ms_double = t2 - t1;
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
polyominoes.clear();
|
||||
polyominoes.shrink_to_fit();
|
||||
|
||||
std::vector<Piece> pieces;
|
||||
std::vector<int> convexPieces;
|
||||
std::vector<int> holelessPieces;
|
||||
std::vector<int> otherPieces;
|
||||
|
||||
t1 = high_resolution_clock::now();
|
||||
pf.loadPieces(i, pieces, convexPieces, holelessPieces, otherPieces);
|
||||
t2 = high_resolution_clock::now();
|
||||
ms_double = t2 - t1;
|
||||
std::cout << " | " << (int) ms_double.count() / 1000 << "s " << std::fmod(ms_double.count(), 1000) << "ms ";
|
||||
std::flush(std::cout);
|
||||
|
||||
pieces.clear();
|
||||
pieces.shrink_to_fit();
|
||||
convexPieces.clear();
|
||||
convexPieces.shrink_to_fit();
|
||||
holelessPieces.clear();
|
||||
holelessPieces.shrink_to_fit();
|
||||
otherPieces.clear();
|
||||
otherPieces.shrink_to_fit();
|
||||
|
||||
std::string filePath;
|
||||
pf.getFilePath(i, filePath);
|
||||
int fileSize = std::filesystem::file_size(filePath);
|
||||
std::cout << " | " << fileSize << " bytes |" << std::endl;
|
||||
std::flush(std::cout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,44 @@
|
||||
|
||||
#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_fonts_pressstart_prstart_ttf[] = {
|
||||
#include <data/fonts/pressstart/prstart.ttf.h>
|
||||
static const unsigned char data_images_keybinds_Rotate180_png[] = {
|
||||
#include <data/images/keybinds/Rotate180.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_Moveright_png[] = {
|
||||
#include <data/images/keybinds/Moveright.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_Retry_png[] = {
|
||||
#include <data/images/keybinds/Retry.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_RotateCW_png[] = {
|
||||
#include <data/images/keybinds/RotateCW.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Pause_png[] = {
|
||||
#include <data/images/keybinds/Pause.png.h>
|
||||
static const unsigned char data_images_keybinds_Moveright_png[] = {
|
||||
#include <data/images/keybinds/Moveright.png.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_Hold_png[] = {
|
||||
@@ -34,57 +50,41 @@ static const unsigned char data_images_keybinds_Softdrop_png[] = {
|
||||
#include <data/images/keybinds/Softdrop.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_RotateCCW_png[] = {
|
||||
#include <data/images/keybinds/RotateCCW.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Moveleft_png[] = {
|
||||
#include <data/images/keybinds/Moveleft.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Rotate180_png[] = {
|
||||
#include <data/images/keybinds/Rotate180.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Retry_png[] = {
|
||||
#include <data/images/keybinds/Retry.png.h>
|
||||
};
|
||||
|
||||
static const unsigned char data_images_keybinds_Harddrop_png[] = {
|
||||
#include <data/images/keybinds/Harddrop.png.h>
|
||||
static const unsigned char data_images_keybinds_Pause_png[] = {
|
||||
#include <data/images/keybinds/Pause.png.h>
|
||||
};
|
||||
|
||||
static const Asset assets[] = {
|
||||
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
|
||||
{data_fonts_pressstart_prstart_ttf, sizeof(data_fonts_pressstart_prstart_ttf)},
|
||||
{data_fonts_pressstart_prstartk_ttf, sizeof(data_fonts_pressstart_prstartk_ttf)},
|
||||
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
|
||||
{data_images_keybinds_Rotate0_png, sizeof(data_images_keybinds_Rotate0_png)},
|
||||
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
|
||||
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
|
||||
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
|
||||
{data_images_keybinds_RotateCW_png, sizeof(data_images_keybinds_RotateCW_png)},
|
||||
{data_images_keybinds_Pause_png, sizeof(data_images_keybinds_Pause_png)},
|
||||
{data_images_keybinds_Moveright_png, sizeof(data_images_keybinds_Moveright_png)},
|
||||
{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_Hold_png, sizeof(data_images_keybinds_Hold_png)},
|
||||
{data_images_keybinds_Softdrop_png, sizeof(data_images_keybinds_Softdrop_png)},
|
||||
{data_images_keybinds_RotateCCW_png, sizeof(data_images_keybinds_RotateCCW_png)},
|
||||
{data_images_keybinds_Moveleft_png, sizeof(data_images_keybinds_Moveleft_png)},
|
||||
{data_images_keybinds_Rotate180_png, sizeof(data_images_keybinds_Rotate180_png)},
|
||||
{data_images_keybinds_Retry_png, sizeof(data_images_keybinds_Retry_png)},
|
||||
{data_images_keybinds_Harddrop_png, sizeof(data_images_keybinds_Harddrop_png)},
|
||||
{data_images_keybinds_Pause_png, sizeof(data_images_keybinds_Pause_png)},
|
||||
|
||||
};
|
||||
|
||||
static const std::map<std::string, AssetName> assetMap = {
|
||||
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
|
||||
{"data/fonts/pressstart/prstart.ttf", AssetName::data_fonts_pressstart_prstart_ttf},
|
||||
{"data/fonts/pressstart/prstartk.ttf", AssetName::data_fonts_pressstart_prstartk_ttf},
|
||||
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
|
||||
{"data/images/keybinds/Rotate0.png", AssetName::data_images_keybinds_Rotate0_png},
|
||||
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
|
||||
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
|
||||
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
|
||||
{"data/images/keybinds/RotateCW.png", AssetName::data_images_keybinds_RotateCW_png},
|
||||
{"data/images/keybinds/Pause.png", AssetName::data_images_keybinds_Pause_png},
|
||||
{"data/images/keybinds/Moveright.png", AssetName::data_images_keybinds_Moveright_png},
|
||||
{"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/Hold.png", AssetName::data_images_keybinds_Hold_png},
|
||||
{"data/images/keybinds/Softdrop.png", AssetName::data_images_keybinds_Softdrop_png},
|
||||
{"data/images/keybinds/RotateCCW.png", AssetName::data_images_keybinds_RotateCCW_png},
|
||||
{"data/images/keybinds/Moveleft.png", AssetName::data_images_keybinds_Moveleft_png},
|
||||
{"data/images/keybinds/Rotate180.png", AssetName::data_images_keybinds_Rotate180_png},
|
||||
{"data/images/keybinds/Retry.png", AssetName::data_images_keybinds_Retry_png},
|
||||
{"data/images/keybinds/Harddrop.png", AssetName::data_images_keybinds_Harddrop_png},
|
||||
{"data/images/keybinds/Pause.png", AssetName::data_images_keybinds_Pause_png},
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -9,19 +9,19 @@ struct Asset {
|
||||
};
|
||||
|
||||
enum class AssetName {
|
||||
data_fonts_pressstart_prstartk_ttf,
|
||||
data_fonts_pressstart_prstart_ttf,
|
||||
data_fonts_pressstart_prstartk_ttf,
|
||||
data_images_keybinds_Rotate180_png,
|
||||
data_images_keybinds_Rotate0_png,
|
||||
data_images_keybinds_Moveright_png,
|
||||
data_images_keybinds_RotateCCW_png,
|
||||
data_images_keybinds_Retry_png,
|
||||
data_images_keybinds_RotateCW_png,
|
||||
data_images_keybinds_Pause_png,
|
||||
data_images_keybinds_Moveright_png,
|
||||
data_images_keybinds_Harddrop_png,
|
||||
data_images_keybinds_Moveleft_png,
|
||||
data_images_keybinds_Hold_png,
|
||||
data_images_keybinds_Softdrop_png,
|
||||
data_images_keybinds_RotateCCW_png,
|
||||
data_images_keybinds_Moveleft_png,
|
||||
data_images_keybinds_Rotate180_png,
|
||||
data_images_keybinds_Retry_png,
|
||||
data_images_keybinds_Harddrop_png,
|
||||
data_images_keybinds_Pause_png,
|
||||
|
||||
};
|
||||
|
||||
|
||||
35
xmake.lua
35
xmake.lua
@@ -2,7 +2,7 @@ add_rules("mode.debug", "mode.release")
|
||||
|
||||
includes("xmake/bin2c.lua")
|
||||
|
||||
add_requires("sfml 3.0.0")
|
||||
add_requires("sfml 3.0.0", "zlib")
|
||||
|
||||
set_languages("c++20")
|
||||
|
||||
@@ -10,8 +10,20 @@ set_rundir(".")
|
||||
|
||||
target("core")
|
||||
set_kind("$(kind)")
|
||||
add_files("src/Pieces/*.cpp")
|
||||
add_files("src/Core/*.cpp")
|
||||
add_files("src/Pieces/*.cpp", "src/Core/*.cpp", "src/Common/*.cpp")
|
||||
add_packages("zlib")
|
||||
|
||||
target("text")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
|
||||
target("bmark")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/Benchmark/*.cpp")
|
||||
add_deps("core")
|
||||
|
||||
target("graph")
|
||||
set_default(true)
|
||||
@@ -26,18 +38,13 @@ target("graph")
|
||||
add_deps("core")
|
||||
add_packages("sfml")
|
||||
|
||||
target("text")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
if is_mode("debug") then
|
||||
add_defines("DEBUG")
|
||||
end
|
||||
|
||||
target("benchmark")
|
||||
set_default(false)
|
||||
set_kind("binary")
|
||||
add_files("./src/TextUI/*.cpp")
|
||||
add_deps("core")
|
||||
add_defines("BENCHMARK")
|
||||
if is_plat("mingw") then
|
||||
add_ldflags("-static-libstdc++", "-static")
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
|
||||
Reference in New Issue
Block a user