fixed game logic

This commit is contained in:
2025-03-05 19:02:51 +01:00
parent 2fbe4a6052
commit 74797e935a
15 changed files with 186 additions and 138 deletions

View File

@@ -26,6 +26,8 @@ void Bag::jumpToNextBag() {
this->currentBag.push_back(pieceIndex);
}
this->nextBag.clear();
this->prepareNext();
}
Piece Bag::lookNext() {

View File

@@ -64,6 +64,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
if (this->leftARETime == 0) {
if (AREJustEnded) {
this->lost = this->board.spawnNextPiece();
this->resetPiece(true);
}
/* IRS and IHS */
@@ -73,33 +74,17 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
if (this->initialActions.contains(HOLD)) {
if (this->board.hold(initialRotation)) {
this->subVerticalPosition = 0;
this->totalLockDelay = 0;
this->heldARR = 0;
}
else {
this->lost = true;
}
this->lost = (!this->board.hold(initialRotation));
}
else {
if (initialRotation != NONE || this->initialActions.contains(ROTATE_0)) {
Position before = this->board.getActivePiecePosition();
if (this->board.rotate(initialRotation)) {
this->totalLockDelay = 0;
if (before != this->board.getActivePiecePosition()) {
this->subVerticalPosition = 0;
}
}
else {
this->lost = true;
}
if ((initialRotation != NONE) || this->initialActions.contains(ROTATE_0)) {
this->lost = (!this->board.rotate(initialRotation));
}
}
if (this->lost) {
if (initialRotation == NONE && (!this->initialActions.contains(ROTATE_0))) {
this->board.rotate(NONE);
if (initialRotation == NONE) {
this->lost = (!this->board.rotate(initialRotation));
}
}
if (this->lost) {
@@ -110,23 +95,22 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
/* HOLD */
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
if (this->board.hold()) {
this->subVerticalPosition = 0;
this->totalLockDelay = 0;
this->heldARR = 0;
this->resetPiece(false);
}
}
/* MOVE LEFT/RIGHT */
Position before = this->board.getActivePiecePosition();
if (playerActions.contains(MOVE_LEFT)) {
this->movePiece(-1);
if (this->heldDAS >= 0) {
if (playerActions.contains(MOVE_LEFT) && (!heldActions.contains(MOVE_LEFT))) this->movePiece(-1);
else if (playerActions.contains(MOVE_RIGHT)) this->movePiece(1);
else this->heldDAS = 0;
}
else if (playerActions.contains(MOVE_RIGHT)) {
this->movePiece(1);
}
else {
this->heldDAS = 0;
else if (this->heldDAS < 0) {
if (playerActions.contains(MOVE_RIGHT) && (!heldActions.contains(MOVE_RIGHT))) this->movePiece(1);
else if (playerActions.contains(MOVE_LEFT)) this->movePiece(-1);
else this->heldDAS = 0;
}
if (before != this->board.getActivePiecePosition()) {
@@ -134,31 +118,17 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
}
/* ROTATIONS */
before = this->board.getActivePiecePosition();
if (playerActions.contains(ROTATE_0) && (!this->heldActions.contains(ROTATE_0))) {
if (this->board.rotate(NONE)) {
this->totalLockDelay = 0;
}
this->rotatePiece(NONE);
}
if (playerActions.contains(ROTATE_CW) && (!this->heldActions.contains(ROTATE_CW))) {
if (this->board.rotate(CLOCKWISE)) {
this->totalLockDelay = 0;
}
this->rotatePiece(CLOCKWISE);
}
if (playerActions.contains(ROTATE_180) && (!this->heldActions.contains(ROTATE_180))) {
if (this->board.rotate(DOUBLE)) {
this->totalLockDelay = 0;
}
this->rotatePiece(DOUBLE);
}
if (playerActions.contains(ROTATE_CCW) && (!this->heldActions.contains(ROTATE_CCW))) {
if (this->board.rotate(COUNTERCLOCKWISE)) {
this->totalLockDelay = 0;
}
}
if (before != this->board.getActivePiecePosition()) {
this->subVerticalPosition = 0;
this->rotatePiece(COUNTERCLOCKWISE);
}
/* SOFT DROP */
@@ -238,45 +208,61 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
this->initialActions.insert(action);
}
if (playerActions.contains(MOVE_LEFT)) {
this->heldDAS = std::min(-1, this->heldDAS - 1);
if (this->heldDAS >= 0) {
if (playerActions.contains(MOVE_LEFT)) this->heldDAS = -1;
else if (playerActions.contains(MOVE_RIGHT)) this->heldDAS++;
else this->heldDAS = 0;
}
else if (playerActions.contains(MOVE_RIGHT)) {
this->heldDAS = std::max(1, this->heldDAS + 1);
}
else {
this->heldDAS = 0;
else if (this->heldDAS < 0) {
if (playerActions.contains(MOVE_RIGHT)) this->heldDAS = +1;
else if (playerActions.contains(MOVE_LEFT)) this->heldDAS--;
else this->heldDAS = 0;
}
}
}
void Game::resetPiece(bool newPiece) {
int appliedDAS = this->parameters.getDAS();
this->subVerticalPosition = 0;
this->totalLockDelay = 0;
if (newPiece) {
this->totalForcedLockDelay = 0;
}
if (abs(this->heldDAS) > appliedDAS) {
this->heldDAS = (this->heldDAS > 0) ? (+appliedDAS) : (-appliedDAS);
}
this->heldARR = 0;
}
void Game::movePiece(int movement) {
std::cout << movement << std::endl;
int appliedDAS = this->parameters.getDAS();
int appliedARR = this->parameters.getARR();
if ((this->heldDAS * movement) <= 0) {
this->heldDAS = movement;
this->heldARR = 0;
if (movement == -1) this->board.moveLeft();
if (movement == 1) this->board.moveRight();
}
else {
this->heldDAS += movement;
}
if (abs(this->heldDAS) == appliedDAS + 1) {
if (movement == -1) this->board.moveLeft();
if (movement == 1) this->board.moveRight();
}
if (abs(this->heldDAS) > appliedDAS + 1) {
if (abs(this->heldDAS) > appliedDAS) {
// ARR=0 -> instant movement
if (appliedARR == 0) {
if (movement == -1) while (this->board.moveLeft());
if (movement == 1) while (this->board.moveRight());
}
// ARR>1 -> move by specified amount
else {
this->heldARR++;
if (this->heldARR == appliedARR) {
if (abs(this->heldDAS) > appliedDAS + 1) {
this->heldARR++;
}
if ((this->heldARR == appliedARR) || (abs(this->heldDAS) == (appliedDAS + 1))) {
this->heldARR = 0;
if (movement == -1) this->board.moveLeft();
if (movement == 1) this->board.moveRight();
@@ -285,6 +271,17 @@ void Game::movePiece(int movement) {
}
}
void Game::rotatePiece(Rotation rotation) {
Position before = this->board.getActivePiecePosition();
if (this->board.rotate(rotation)) {
this->totalLockDelay = 0;
if (before != this->board.getActivePiecePosition()) {
this->subVerticalPosition = 0;
}
}
}
void Game::lockPiece() {
LineClear clear = this->board.lockPiece();
this->parameters.clearLines(clear.lines);
@@ -300,16 +297,12 @@ void Game::lockPiece() {
this->score += clearScore;
}
this->B2BChain = B2BConditionsAreMet;
this->subVerticalPosition = 0;
this->totalLockDelay = 0;
this->totalForcedLockDelay = 0;
this->heldARR = 0;
if (!this->hasWon()) {
this->leftARETime = this->parameters.getARE();
if (this->leftARETime == 0) {
this->lost = this->board.spawnNextPiece();
this->resetPiece(true);
}
}
}

View File

@@ -61,10 +61,20 @@ class Game {
private:
/**
* Move the piece in the specified direction
* Resets the piece's parameter
*/
void resetPiece(bool newPiece);
/**
* Moves the piece in the specified direction (1 for right and -1 for left)
*/
void movePiece(int movement);
/**
* Rotates the piece with the specified rotation
*/
void rotatePiece(Rotation rotation);
/**
* Locks the piece, updates level and score and spawns the next piece if necessary
*/

View File

@@ -4,17 +4,17 @@
#include "Player.h"
#include "Game.h"
static const int FRAMES_PER_SECOND = 60; // the number of frames per second, all the values in the game 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
static const int DEFAULT_BOARD_WIDTH = 10; // the default width of the board when starting the menu
static const int DEFAULT_BOARD_HEIGHT = 20; // the default height of the board when starting the menu
/**
* The interface between the UI and the core of the game
* The interface between an UI and the core of the app
*/
class Menu {
private:
std::shared_ptr<PiecesList> piecesList; // the list of pieces in the game
std::shared_ptr<PiecesList> piecesList; // the list of pieces used by the app
Player playerControls; // the controls of the player
int boardWidth; // the width of the board for the next game
int boardHeight; // the height of the board for the next game

View File

@@ -11,19 +11,19 @@
Generator::Generator() {
}
std::vector<Polyomino> Generator::generatePolyominos(int polyominoSize) {
this->validPolyominos.clear();
std::vector<Polyomino> Generator::generatePolyominoes(int polyominoSize) {
this->validPolyominoes.clear();
this->currentTestedShape.clear();
// a polyomino has at least 1 square
if (polyominoSize < 1) return this->validPolyominos;
if (polyominoSize < 1) return this->validPolyominoes;
// always place the first cell at (0, 0)
this->currentTestedShape.insert(Position{0, 0});
std::map<Position, int> candidatePositions;
this->generate(polyominoSize, 0, 1, candidatePositions);
return this->validPolyominos;
return this->validPolyominoes;
}
void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions) {
@@ -31,7 +31,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
if (polyominoSize == this->currentTestedShape.size()) {
Polyomino candidate(this->currentTestedShape);
// we sort the rotations of the polyominos
// we sort the rotations of the polyominoes
std::vector<Polyomino> candidateRotations;
candidateRotations.reserve(4);
for (int i = 0; i < 4; i++) {
@@ -43,7 +43,7 @@ void Generator::generate(int polyominoSize, int lastAddedPositionNumber, int nex
// we keep the polyomino only if it was generated in its lowest rotation
if (candidate == candidateRotations.at(0)) {
this->validPolyominos.push_back(candidate);
this->validPolyominoes.push_back(candidate);
}
return;

View File

@@ -8,11 +8,11 @@
/**
* A generator of one-sided polyominos of any size
* A generator of one-sided polyominoes of any size
*/
class Generator {
private:
std::vector<Polyomino> validPolyominos; // the list of already generated polyominos
std::vector<Polyomino> validPolyominoes; // the list of already generated polyominoes
std::set<Position> currentTestedShape; // the polyomino being created
public:
@@ -22,14 +22,14 @@ class Generator {
Generator();
/**
* Generates the list of all one-sided polyominos of the specified size
* @return The list of polyominos
* Generates the list of all one-sided polyominoes of the specified size
* @return The list of polyominoes
*/
std::vector<Polyomino> generatePolyominos(int polyominoSize);
std::vector<Polyomino> generatePolyominoes(int polyominoSize);
private:
/**
* Generates all one-sided polyominos of the specified size using the current tested shape
* Generates all one-sided polyominoes of the specified size using the current tested shape
*/
void generate(int polyominoSize, int lastAddedPositionNumber, int nextAvaibleNumber, std::map<Position, int> candidatePositions);

View File

@@ -25,9 +25,9 @@ bool PiecesFiles::savePieces(int polyominoSize) const {
}
Generator generator;
std::vector<Polyomino> nMinos = generator.generatePolyominos(polyominoSize);
std::vector<Polyomino> nMinos = generator.generatePolyominoes(polyominoSize);
// sorting the polyominos is done after setting spawn position to ensure the order is always the same
// sorting the polyominoes is done after setting spawn position to ensure the order is always the same
for (Polyomino& nMino : nMinos) {
nMino.goToSpawnPosition();
}
@@ -64,7 +64,7 @@ bool PiecesFiles::loadPieces(int polyominoSize, std::vector<Piece>& pieces, std:
holelessPieces.clear();
otherPieces.clear();
// we shift the first color of each size so that the small polyominos (size 1-2-3) don't all have the same color
// we shift the first color of each size so that the small polyominoes (size 1-2-3) don't all have the same color
Block pieceBlock = firstPieceBlockType();
for (int i = 0; i < polyominoSize; i++) {
nextPieceBlockType(pieceBlock);

View File

@@ -100,7 +100,7 @@ class Polyomino {
bool operator<(const Polyomino& other) const;
/**
* Equality operator, two polyominos are equal if their positions are the same, that means two polyominos of the same shape at different places will not be equal
* Equality operator, two polyominoes are equal if their positions are the same, that means two polyominoes of the same shape at different places will not be equal
* @return If the polyomino is equal to another
*/
bool operator==(const Polyomino& other) const;

View File

@@ -24,7 +24,7 @@ TextApp::TextApp() {
this->gameMenu.getPiecesList().loadPieces(MAXIMUM_PIECE_SIZE);
this->gameMenu.getPiecesList().selectAllPieces(DEFAULT_PIECE_SIZE);
this->gameMenu.getPlayerControls().setDAS(FRAMES_PER_INPUT - 1);
this->gameMenu.getPlayerControls().setDAS(FRAMES_PER_INPUT);
this->gameMenu.getPlayerControls().setARR(FRAMES_PER_INPUT);
this->gameMenu.getPlayerControls().setSDR(0);
}
@@ -130,7 +130,6 @@ void TextApp::seeKeybinds() const {
std::cout << "Rotate 0/CW/180/CCW: c/cw/cc/ccw" << std::endl;
std::cout << "\n";
std::cout << "To do several actions at the same time, separe them with blank spaces." << std::endl;
std::cout << "Remember that for certains actions like hard dropping, you need to release it before using it again, even if a different piece spawned.";
std::string waiting;
std::getline(std::cin, waiting);
}
@@ -171,28 +170,47 @@ void TextApp::startGame() const {
}
std::set<Action> playerActions;
std::set<Action> lastFrameActions;
std::set<Action> metaActions;
for (std::string action : actions) {
if (this->keybinds.contains(action)) {
playerActions.insert(this->keybinds.at(action));
try {
Action playerAction = this->keybinds.at(action);
if (playerAction == PAUSE || playerAction == RETRY || playerAction == quit) {
metaActions.insert(playerAction);
}
else {
if (playerAction == SOFT_DROP || playerAction == MOVE_LEFT || playerAction == MOVE_RIGHT) {
playerActions.insert(playerAction);
lastFrameActions.insert(playerAction);
}
else if (playerAction == HOLD || playerAction == HARD_DROP) {
lastFrameActions.insert(playerAction);
}
else {
playerActions.insert(playerAction);
}
}
}
catch (std::exception ignored) {}
}
if (playerActions.contains(PAUSE)) {
if (metaActions.contains(PAUSE)) {
paused = (!paused);
}
if (!paused) {
if (playerActions.contains(QUIT)) {
if (metaActions.contains(QUIT)) {
quit = true;
}
else if (playerActions.contains(RETRY)) {
else if (metaActions.contains(RETRY)) {
game.reset();
game.start();
}
else {
for (int i = 0; i < FRAMES_PER_INPUT; i++) {
for (int i = 0; i < (FRAMES_PER_INPUT - 1); i++) {
game.nextFrame(playerActions);
}
game.nextFrame(lastFrameActions);
}
}

View File

@@ -37,11 +37,11 @@ void testGeneratorForAllSizes(int amount) {
for (int i = 1; i <= amount; i++) {
auto t1 = high_resolution_clock::now();
std::vector<Polyomino> n_minos = generator.generatePolyominos(i);
std::vector<Polyomino> n_minos = generator.generatePolyominoes(i);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
std::cout << "generated " << n_minos.size() << " polyominos of size " << i << " in " << ms_double.count() << "ms" << std::endl;
std::cout << "generated " << n_minos.size() << " polyominoes of size " << i << " in " << ms_double.count() << "ms" << std::endl;
}
}
@@ -55,7 +55,7 @@ void testGeneratorForOneSize(int size) {
std::cout << "Generating " << size << "-minos" << std::endl;
for (int i = 0; i < 10; i++) {
auto t1 = high_resolution_clock::now();
std::vector<Polyomino> n_minos = generator.generatePolyominos(size);
std::vector<Polyomino> n_minos = generator.generatePolyominoes(size);
auto t2 = high_resolution_clock::now();
duration<double, std::milli> ms_double = t2 - t1;
@@ -65,7 +65,7 @@ void testGeneratorForOneSize(int size) {
void testGeneratorByprintingAllNminos(int n) {
Generator generator;
std::vector<Polyomino> n_minos = generator.generatePolyominos(n);
std::vector<Polyomino> n_minos = generator.generatePolyominoes(n);
for (Polyomino& n_mino : n_minos) {
n_mino.goToSpawnPosition();