fixed game logic
This commit is contained in:
@@ -26,6 +26,8 @@ void Bag::jumpToNextBag() {
|
||||
this->currentBag.push_back(pieceIndex);
|
||||
}
|
||||
this->nextBag.clear();
|
||||
|
||||
this->prepareNext();
|
||||
}
|
||||
|
||||
Piece Bag::lookNext() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user