mis en place la PR de simon sur les couleurs

This commit is contained in:
2025-02-28 18:42:04 +01:00
parent f0f391ade6
commit 26f501f7e8
29 changed files with 713 additions and 665 deletions

View File

@@ -2,11 +2,13 @@
#include "../Pieces/Piece.h"
#include <Vector>
#include <vector>
#include <cstdlib>
Bag::Bag(const std::vector<Piece>& pieces) : pieces(pieces) {
Bag::Bag(const std::vector<Piece>& pieces) :
pieces(pieces) {
// initialize bags
this->currentBag.clear();
for (int i = 0; i < this->pieces.size(); i++) {
@@ -19,7 +21,6 @@ Bag::Bag(const std::vector<Piece>& pieces) : pieces(pieces) {
}
Piece Bag::lookNext() {
// return the next piece
return this->pieces.at(this->next);
}

View File

@@ -2,7 +2,7 @@
#include "../Pieces/Piece.h"
#include <Vector>
#include <vector>
/**
@@ -10,11 +10,11 @@
*/
class Bag {
private:
std::vector<Piece> pieces; // the pieces the bag can dispense
int next; // the next piece to give
std::vector<Piece> pieces; // the pieces the bag can dispense
int next; // the next piece to give
std::vector<int> currentBag; // the list of pieces that are still to be taken out before starting a new bag
std::vector<int> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
std::vector<int> nextBag; // the list of pieces that have been taken out of the current bag and have been placed in the next
public:
/**
* Creates a new bag of the specified list of pieces
@@ -22,12 +22,14 @@ class Bag {
Bag(const std::vector<Piece>& pieces);
/**
* Looks at what the next picked piece will be
* Looks at what the next picked piece will be, without removing it from the bag
* @return The next piece
*/
Piece lookNext();
/**
* Picks a new piece from the current bag
* Picks a new piece from the current bag, removing it from the bag
* @return The next piece
*/
Piece getNext();

View File

@@ -2,13 +2,25 @@
#include "../Pieces/Piece.h"
#include <Vector>
#include <Set>
#include <vector>
#include <set>
#include <iostream>
Board::Board(int width, int height) : width(width), height(height) {
std::vector<Color> emptyRow;
static constexpr std::vector<Block> getEmptyRow(int width) {
std::vector<Block> emptyRow;
for (int i = 0; i < width; i ++) {
emptyRow.push_back(NOTHING);
}
return emptyRow;
}
Board::Board(int width, int height) :
width(width),
height(height) {
std::vector<Block> emptyRow;
for (int i = 0; i < width; i ++) {
emptyRow.push_back(NOTHING);
}
@@ -20,13 +32,13 @@ Board::Board(int width, int height) : width(width), height(height) {
}
}
void Board::addBlock(const Cell& position, Color block) {
void Board::addBlock(const Position& position, Block block) {
// if the block is out of bounds we discard it
if (position.x < 0 || position.x >= this->width || position.y < 0) return;
// resize the grid if needed
if (position.y >= this->grid.size()) {
std::vector<Color> emptyRow;
std::vector<Block> emptyRow;
for (int i = 0; i < width; i ++) {
emptyRow.push_back(NOTHING);
}
@@ -35,29 +47,26 @@ void Board::addBlock(const Cell& position, Color block) {
}
}
// change the block in the grid
this->grid.at(position.y).at(position.x) = block;
}
int Board::clearRows() {
std::vector<Color> emptyRow;
std::vector<Block> emptyRow;
for (int i = 0; i < width; i ++) {
emptyRow.push_back(NOTHING);
}
// check from top to bottom
// check from top to bottom, so that erasing lines don't screw up the looping
int clearedLines = 0;
for (int j = this->grid.size() - 1; j >= 0; j--) {
// check if a line has a block on every column
bool isFull = true;
bool lineIsFull = true;
for (int i = 0; i < this->width; i++) {
if (this->grid.at(j).at(i) == NOTHING) {
isFull = false;
lineIsFull = false;
}
}
// if it has, erase it and add a new row at the top
if (isFull) {
if (lineIsFull) {
this->grid.erase(this->grid.begin() + j);
if(this->grid.size() < height) this->grid.push_back(emptyRow);
clearedLines++;
@@ -67,18 +76,15 @@ int Board::clearRows() {
return clearedLines;
}
Color Board::getBlock(const Cell& position) const {
// if the block is out of bounds
if (position.x < 0 || position.x >= this->width || position.y < 0) return OUT_OF_BOUND;
Block Board::getBlock(const Position& position) const {
if (position.x < 0 || position.x >= this->width || position.y < 0) return OUT_OF_BOUNDS;
// if the block is higher than the current grid, since it can grow indefinitely we do as if it was there but empty
if (position.y >= this->grid.size()) return NOTHING;
// else get the color in the grid
return this->grid.at(position.y).at(position.x);
}
std::vector<std::vector<Color>> Board::getBlocks() const {
std::vector<std::vector<Block>> Board::getBlocks() const {
return this->grid;
}
@@ -95,11 +101,10 @@ int Board::getWidth() const {
}
std::ostream& operator<<(std::ostream& os, const Board& board) {
// print the board
for (int y = board.grid.size() - 1; y >= 0; y--) {
for (int x = 0; x < board.width; x++) {
Color block = board.grid.at(y).at(x);
os << COLOR_CODES[block];
Block block = board.grid.at(y).at(x);
os << getConsoleColorCode(block);
if (block != NOTHING) {
os << "*";
}
@@ -110,8 +115,7 @@ std::ostream& operator<<(std::ostream& os, const Board& board) {
os << std::endl;
}
// reset console color
os << COLOR_RESET;
os << getResetConsoleColorCode();
return os;
}

View File

@@ -2,7 +2,7 @@
#include "../Pieces/Piece.h"
#include <Vector>
#include <vector>
#include <iostream>
@@ -11,9 +11,9 @@
*/
class Board {
private:
std::vector<std::vector<Color>> grid; // the grid, (0,0) is downleft
int width; // the width of the grid
int height; // the base height of the grid, which can extends indefinitely
std::vector<std::vector<Block>> grid; // the grid, (0,0) is downleft
int width; // the width of the grid
int height; // the base height of the grid, which can extends indefinitely
public:
/**
@@ -22,42 +22,44 @@ class Board {
Board(int width, int height);
/**
* Change the color of the specified block, if the block is out of bounds it is simply ignored
* Change the block of the specified block, if the block is out of bounds it is simply ignored
*/
void addBlock(const Cell& position, Color block);
void addBlock(const Position& position, Block block);
/**
* Clears any complete row and moves down the rows on top, returns the number of cleared rows
* Clears any complete row and moves down the rows on top
* @return The number of cleared rows
*/
int clearRows();
/**
* Returns the color of the block at the specified position
* @return The block of the block at the specified position
*/
Color getBlock(const Cell& position) const;
Block getBlock(const Position& position) const;
/**
* Returns a copy of the grid
* @return A copy of the grid
*/
std::vector<std::vector<Color>> getBlocks() const;
std::vector<std::vector<Block>> getBlocks() const;
/**
* Returns the actual height of the grid
* @return The actual height of the grid
*/
int getGridHeight() const;
/**
* Returns the base height of the grid
* @return The base height of the grid
*/
int getBaseHeight() const;
/**
* Returns the width of the grid
* @return The width of the grid
*/
int getWidth() const;
/**
* Stream output operator, adds a 2D grid representing the board
* @return A reference to the output stream
*/
friend std::ostream& operator<<(std::ostream& os, const Board& board);
};

View File

@@ -4,17 +4,21 @@
#include "GameParameters.h"
#include "Action.h"
#include <Vector>
#include <vector>
#include <algorithm>
static const int SUBPX_PER_ROW = 60; // the number of position the active piece can take "between" two rows
static const int SOFT_DROP_SCORE = 1; // the score gained by line soft dropped
static const int HARD_DROP_SCORE = 2; // the score gained by line hard dropped
static const int SUBPX_PER_ROW = 60; // the number of position the active piece can take "between" two rows
static const int SOFT_DROP_SCORE = 1; // the score gained by line soft dropped
static const int HARD_DROP_SCORE = 2; // the score gained by line hard dropped
static const int LINE_CLEAR_BASE_SCORE = 100; // the score value of clearing a single line
static const int B2B_SCORE_MULTIPLIER = 2; // by how much havaing B2B on multiplies the score of the line clear
static const int B2B_MIN_LINE_NUMBER = 4; // the minimum number of lines needed to be cleared at once to gain B2B (without a spin)
static const int B2B_SCORE_MULTIPLIER = 2; // by how much havaing B2B on multiplies the score of the line clear
static const int B2B_MIN_LINE_NUMBER = 4; // the minimum number of lines needed to be cleared at once to gain B2B (without a spin)
Game::Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardHeight, const std::vector<Piece>& bag) : parameters(gamemode, controls), board(boardWidth, boardHeight, bag, parameters.getNextQueueLength()) {
Game::Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardHeight, const std::vector<Piece>& bag) :
parameters(gamemode, controls),
board(boardWidth, boardHeight, bag, parameters.getNextQueueLength()) {
// the game has not yet started
this->started = false;
this->lost = false;
@@ -221,17 +225,17 @@ void Game::lockPiece() {
this->parameters.clearLines(clear.lines);
// update B2B and score
bool B2BConditions = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
bool B2BConditionsAreMet = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
if (clear.lines > 0) {
/* 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));
if (this->B2BChain && B2BConditions) clearScore *= B2B_SCORE_MULTIPLIER;
if (this->B2BChain && B2BConditionsAreMet) clearScore *= B2B_SCORE_MULTIPLIER;
this->score += clearScore;
}
this->B2BChain = B2BConditions;
this->B2BChain = B2BConditionsAreMet;
// reset active piece
this->subVerticalPosition = 0;
@@ -286,7 +290,7 @@ Piece Game::getActivePiece() {
return this->board.getActivePiece();
}
Cell Game::getActivePiecePosition() {
Position Game::getActivePiecePosition() {
return this->board.getActivePiecePosition();
}

View File

@@ -4,7 +4,7 @@
#include "GameParameters.h"
#include "Action.h"
#include <Vector>
#include <vector>
/**
@@ -12,22 +12,22 @@
*/
class Game {
private:
GameParameters parameters; // the current parameters of the game
GameBoard board; // the board in which the game is played
bool started; // wheter the game has started
bool lost; // wheter the game is lost
long int score; // the current score
int framesPassed; // how many frames have passed since the start of the game
bool B2BChain; // wheter the player is currently on a B2B chain
std::set<Action> heldActions; // the list of actions that were pressed last frame
GameParameters parameters; // the current parameters of the game
GameBoard board; // the board in which the game is played
bool started; // wheter the game has started
bool lost; // wheter the game is lost
long int score; // the current score
int framesPassed; // how many frames have passed since the start of the game
bool B2BChain; // wheter the player is currently on a B2B chain
std::set<Action> heldActions; // the list of actions that were pressed last frame
std::set<Action> initialActions; // the list of actions that have been pressed while there was no active piece
int heldDAS; // the number of frames DAS has been held, positive for right or negative for left
int heldARR; // the number of frames ARR has been held
int subVerticalPosition; // how far the active piece is to go down one line
int leftARETime; // how many frames are left before ARE period finishes
int totalLockDelay; // how many frames has the active piece touched the ground without moving
int totalForcedLockDelay; // how many frames the active piece has touched the ground since the last spawned piece
int heldDAS; // the number of frames DAS has been held, positive for right or negative for left
int heldARR; // the number of frames ARR has been held
int subVerticalPosition; // how far the active piece is to go down one line
int leftARETime; // how many frames are left before ARE period finishes
int totalLockDelay; // how many frames has the active piece touched the ground without moving
int totalForcedLockDelay; // how many frames the active piece has touched the ground since the last spawned piece
public:
/**
* Initialize the parameters and creates a new board
@@ -40,85 +40,85 @@ class Game {
void start();
/**
* Advance to the next frame while excecuting the actions taken by the player,
* Advances to the next frame while excecuting the actions taken by the player,
* this is where the main game logic takes place
*/
void nextFrame(const std::set<Action>& playerActions);
private:
/**
* Move the piece in the specified direction
* Movse the piece in the specified direction
*/
void movePiece(int movement, bool resetDirection);
/**
* Locks the piece, updates level and score and spawn the next piece if necessary
* Locks the piece, updates level and score and spawns the next piece if necessary
*/
void lockPiece();
public:
/**
* Returns wheter the player has won
* @return If the player has won
*/
bool hasWon();
/**
* Returns wheter the player has lost
* @return If the player has lost
*/
bool hasLost();
/**
* Returns the current level
* @return The current level
*/
int getLevel();
/**
* Returns the current number of cleared lines
* @return The current number of cleared lines
*/
int getClearedLines();
/**
* Returns the number of frames passed since the start of the game
* @return The number of frames passed since the start of the game
*/
int getFramesPassed();
/**
* Returns the current score
* @return The current score
*/
int getScore();
/**
* Returns wheter the player is currently on a B2B chain
* @return If the player is currently on a B2B chain
*/
bool isOnB2BChain();
/**
* Returns wheter all blocks are currently bone blocks
* @return If all blocks are currently bone blocks
*/
bool areBlocksBones();
/**
* Returns a copy of the board
* @return A copy of the board
*/
Board getBoard();
/**
* Returns a copy of the active piece
* @return A copy of the active piece
*/
Piece getActivePiece();
/**
* Returns a copy of the active piece position
* @return A copy of the active piece position
*/
Cell getActivePiecePosition();
Position getActivePiecePosition();
/**
* Returns a copy of the held piece
* @return A copy of the held piece
*/
Piece getHeldPiece();
/**
* Return a copy of the next pieces queue
* @return A copy of the next pieces queue
*/
std::vector<Piece> getNextPieces();
};

View File

@@ -5,13 +5,16 @@
#include "Bag.h"
#include "LineClear.h"
#include <Vector>
#include <Set>
#include <vector>
#include <set>
#include <memory>
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength) : board(boardWidth, boardHeight), generator(bag), nextQueueLength(nextQueueLength) {
// initialize queue
GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength) :
board(boardWidth, boardHeight),
generator(bag),
nextQueueLength(nextQueueLength) {
this->nextQueue.clear();
for (int i = 0; i < nextQueueLength; i++) {
this->nextQueue.push_back(this->generator.getNext());
@@ -19,8 +22,7 @@ GameBoard::GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>&
}
bool GameBoard::moveLeft() {
// check if the piece can be moved one cell left
if (this->isActivePieceInWall(Cell{-1, 0})) {
if (this->isActivePieceInWall(Position{-1, 0})) {
return false;
}
else {
@@ -31,8 +33,7 @@ bool GameBoard::moveLeft() {
}
bool GameBoard::moveRight() {
// check if the piece can be moved one cell right
if (this->isActivePieceInWall(Cell{1, 0})) {
if (this->isActivePieceInWall(Position{1, 0})) {
return false;
}
else {
@@ -43,8 +44,7 @@ bool GameBoard::moveRight() {
}
bool GameBoard::moveDown() {
// check if the piece can be moved one cell down
if (this->isActivePieceInWall(Cell{0, -1})) {
if (this->isActivePieceInWall(Position{0, -1})) {
return false;
}
else {
@@ -55,36 +55,35 @@ bool GameBoard::moveDown() {
}
bool GameBoard::rotate(Rotation rotation) {
// copy the original piece before rotating it
Piece stored = *this->activePiece;
this->rotate(rotation);
// check if the piece can rotate
// before trying to kick, check if the piece can rotate without kicking
if (!this->isActivePieceInWall()) {
this->isLastMoveKick = false;
return true;
}
// get the list of cells that touches the original piece
std::set<Cell> safeCells;
for (Cell cell : stored.getPositions()) {
Cell cellInGrid(cell + this->activePiecePosition);
safeCells.insert(cellInGrid);
safeCells.insert(cellInGrid + Cell{0, 1});
safeCells.insert(cellInGrid + Cell{1, 0});
safeCells.insert(cellInGrid + Cell{0, -1});
safeCells.insert(cellInGrid + Cell{-1, 0});
// get the list of positions that touches the original piece
std::set<Position> safePositions;
for (Position position : stored.getPositions()) {
Position positionInGrid(position + this->activePiecePosition);
safePositions.insert(positionInGrid);
safePositions.insert(positionInGrid + Position{0, 1});
safePositions.insert(positionInGrid + Position{1, 0});
safePositions.insert(positionInGrid + Position{0, -1});
safePositions.insert(positionInGrid + Position{-1, 0});
}
// try kicking the piece down
bool suceeded = this->tryKicking(true, safeCells);
bool suceeded = this->tryKicking(true, safePositions);
if (suceeded) {
this->isLastMoveKick = true;
return true;
}
// if it doesn't work try kicking the piece up
suceeded = this->tryKicking(false, safeCells);
suceeded = this->tryKicking(false, safePositions);
if (suceeded) {
this->isLastMoveKick = true;
return true;
@@ -95,8 +94,8 @@ bool GameBoard::rotate(Rotation rotation) {
return false;
}
bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells) {
// we try from the original height of the piece, moving vertically as long as the kicked piece touches the original
bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePositions) {
// we try from the original height of the piece, moving vertically as long as the kicked piece touches the original at least once on this row
bool overlapsVertically = true;
int j = 0;
do {
@@ -107,13 +106,11 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
do {
// check right before right arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations
if (overlapsRight) {
Cell shift{+i, j};
// the kicked position must touch the original piece
if (!this->activePieceOverlapsOneCell(safeCells, shift)) {
Position shift{+i, j};
if (!this->activePieceOverlapsOnePosition(safePositions, shift)) {
overlapsLeft = false;
}
else {
// if the position is valid we place the active piece there
if (!this->isActivePieceInWall(shift)) {
this->activePiecePosition += shift;
return true;
@@ -123,8 +120,8 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
// do the same on the left side
if (overlapsLeft) {
Cell shift{-i, j};
if (!this->activePieceOverlapsOneCell(safeCells, shift)) {
Position shift{-i, j};
if (!this->activePieceOverlapsOnePosition(safePositions, shift)) {
overlapsLeft = false;
}
else {
@@ -138,12 +135,10 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
i++;
} while (overlapsLeft && overlapsRight);
// test if no position touched the original piece
if (i == 1) {
overlapsVertically = false;
}
// move one line up or down
(testingBottom) ? j-- : j++;
} while (overlapsVertically);
@@ -151,15 +146,11 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Cell>& safeCells)
}
bool GameBoard::hold(Rotation initialRotation) {
// swap with held piece
std::swap(this->activePiece, this->heldPiece);
// if it's the first time holding try the next piece
bool isFirstTimeHolding = false;
if (this->activePiece == nullptr) {
isFirstTimeHolding = true;
// if no pieces in next queue look at what the next would be
bool isFirstTimeHolding = (this->activePiece == nullptr);
if (isFirstTimeHolding) {
// try with the next piece in queue since there is no piece in the hold box yet
if (this->nextQueueLength == 0) {
this->activePiece = std::make_shared<Piece>(this->generator.lookNext());
}
@@ -168,10 +159,8 @@ bool GameBoard::hold(Rotation initialRotation) {
}
}
// set the spawned piece to the correct position
this->goToSpawnPosition();
// apply initial rotation
Piece stored = *this->activePiece;
this->rotate(initialRotation);
@@ -189,14 +178,10 @@ bool GameBoard::hold(Rotation initialRotation) {
}
}
// if it's the first time holding, confirm we keep this piece
if (isFirstTimeHolding) {
if (this->nextQueueLength == 0) {
this->generator.getNext();
}
else {
this->spawnNextPiece();
}
// confirm we keep the piece we tried with
this->nextQueue.push_back(this->generator.getNext());
this->nextQueue.erase(this->nextQueue.begin());
}
// this piece has done nothing yet
@@ -206,39 +191,34 @@ bool GameBoard::hold(Rotation initialRotation) {
}
bool GameBoard::spawnNextPiece() {
// add a piece to the queue
// generate a new piece
this->nextQueue.push_back(this->generator.getNext());
// get next piece from queue
this->activePiece = std::make_shared<Piece>(this->nextQueue.front());
this->nextQueue.erase(this->nextQueue.begin());
// set the spawned piece to the correct position
this->goToSpawnPosition();
// this piece has done nothing yet
this->isLastMoveKick = false;
// returns wheter the piece can spawn correctly
return !this->isActivePieceInWall();
}
bool GameBoard::touchesGround() {
return this->isActivePieceInWall(Cell{0, -1});
return this->isActivePieceInWall(Position{0, -1});
}
LineClear GameBoard::lockPiece() {
// check if the piece is locked in place
bool isLocked = (this->isActivePieceInWall(Cell{0, 1}) && this->isActivePieceInWall(Cell{1, 0}) &&
this->isActivePieceInWall(Cell{-1, 0}) && this->isActivePieceInWall(Cell{0, -1}));
bool isLockedInPlace = (this->isActivePieceInWall(Position{0, 1}) && this->isActivePieceInWall(Position{1, 0})
&& this->isActivePieceInWall(Position{-1, 0}) && this->isActivePieceInWall(Position{0, -1}));
// put the piece in the board
for (Cell cell : this->activePiece->getPositions()) {
this->board.addBlock(cell + this->activePiecePosition, this->activePiece->getColor());
for (Position position : this->activePiece->getPositions()) {
this->board.addBlock(position + this->activePiecePosition, this->activePiece->getBlockType());
}
// check for lines to clear
return LineClear{this->board.clearRows(), isLocked, (!isLocked) && this->isLastMoveKick};
return LineClear{this->board.clearRows(), isLockedInPlace, (!isLockedInPlace) && this->isLastMoveKick};
}
Board GameBoard::getBoard() const {
@@ -249,7 +229,7 @@ Piece GameBoard::getActivePiece() const {
return *this->activePiece;
}
Cell GameBoard::getActivePiecePosition() const {
Position GameBoard::getActivePiecePosition() const {
return this->activePiecePosition;
}
@@ -261,31 +241,28 @@ std::vector<Piece> GameBoard::getNextPieces() const {
return this->nextQueue;
}
bool GameBoard::isActivePieceInWall(const Cell& shift) const {
// check if every cell of the active piece is in an empty spot
for (Cell cell : this->activePiece->getPositions()) {
if (this->board.getBlock(cell + this->activePiecePosition + shift) != NOTHING) return true;
bool GameBoard::isActivePieceInWall(const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (this->board.getBlock(position + this->activePiecePosition + shift) != NOTHING) return true;
}
return false;
}
bool GameBoard::activePieceOverlapsOneCell(const std::set<Cell>& safeCells, const Cell& shift) const {
// check if one cell of the translated active piece overlaps with one cell of the given piece set
for (Cell cell : this->activePiece->getPositions()) {
if (safeCells.contains(cell + shift)) return true;
bool GameBoard::activePieceOverlapsOnePosition(const std::set<Position>& safePositions, const Position& shift) const {
for (Position position : this->activePiece->getPositions()) {
if (safePositions.contains(position + this->activePiecePosition + shift)) return true;
}
return false;
}
void GameBoard::goToSpawnPosition() {
// get the lowest cell of the piece
int lowestCell = this->activePiece->getLength() - 1;
for (Cell cell : this->activePiece->getPositions()) {
if (cell.y < lowestCell) lowestCell = cell.y;
int lowestPosition = this->activePiece->getLength() - 1;
for (Position position : this->activePiece->getPositions()) {
if (position.y < lowestPosition) lowestPosition = position.y;
}
// set the piece one line above the board
this->activePiecePosition.y = this->board.getBaseHeight() - lowestCell;
this->activePiecePosition.y = this->board.getBaseHeight() - lowestPosition;
// center the piece horizontally, biased towards left
this->activePiecePosition.x = (this->board.getWidth() - this->activePiece->getLength()) / 2;
@@ -294,15 +271,13 @@ void GameBoard::goToSpawnPosition() {
std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
// print over the board (only the active piece if it is there)
if (gameboard.activePiece != nullptr) {
Block pieceBlockType = gameboard.activePiece->getBlockType();
os << getConsoleColorCode(pieceBlockType);
// change to the color of the active piece
Color pieceColor = gameboard.activePiece->getColor();
os << COLOR_CODES[pieceColor];
// print only the cell were the active piece is
// print only the position were the active piece is
for (int y = gameboard.activePiecePosition.y + gameboard.activePiece->getLength() - 1; y >= gameboard.board.getBaseHeight(); y--) {
for (int x = 0; x < gameboard.board.getWidth(); x++) {
bool hasActivePiece = gameboard.activePiece->getPositions().contains(Cell{x, y} - gameboard.activePiecePosition);
bool hasActivePiece = gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition);
if (hasActivePiece) {
os << "*";
}
@@ -315,21 +290,19 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
}
// print the board
Color pieceColor = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getColor();
Block pieceBlockType = (gameboard.activePiece == nullptr) ? NOTHING : gameboard.activePiece->getBlockType();
for (int y = gameboard.board.getBaseHeight() - 1; y >= 0; y--) {
for (int x = 0; x < gameboard.board.getWidth(); x++) {
bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->getPositions().contains(Cell{x, y} - gameboard.activePiecePosition);
bool hasActivePiece = (gameboard.activePiece == nullptr) ? false : gameboard.activePiece->getPositions().contains(Position{x, y} - gameboard.activePiecePosition);
// if the active piece is on this cell, print it
// the active piece takes visual priority over the board
if (hasActivePiece) {
os << COLOR_CODES[pieceColor];
os << getConsoleColorCode(pieceBlockType);
os << "*";
}
// else print the cell of the board
else {
Color block = gameboard.board.getBlock(Cell{x, y});
os << COLOR_CODES[block];
Block block = gameboard.board.getBlock(Position{x, y});
os << getConsoleColorCode(block);
if (block != NOTHING) {
os << "*";
}
@@ -341,7 +314,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
os << std::endl;
}
// print held piece
// print hold box
os << "Hold:" << std::endl;
if (!(gameboard.heldPiece == nullptr)) {
os << *gameboard.heldPiece;
@@ -353,8 +326,7 @@ std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
os << piece;
}
// reset console color
os << COLOR_RESET;
os << getResetConsoleColorCode();
return os;
}

View File

@@ -5,7 +5,7 @@
#include "Bag.h"
#include "LineClear.h"
#include <Vector>
#include <vector>
#include <memory>
@@ -14,15 +14,15 @@
*/
class GameBoard {
private:
Board board; // the board in which pieces moves, (0, 0) is downleft
Bag generator; // the piece generator
Board board; // the board in which pieces moves, (0, 0) is downleft
Bag generator; // the piece generator
std::shared_ptr<Piece> activePiece; // the piece currently in the board
Cell activePiecePosition; // the position of the piece currently in the board
std::shared_ptr<Piece> heldPiece; // a piece being holded
int nextQueueLength; // the number of next pieces seeable at a time
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
bool isLastMoveKick; // wheter the last action the piece did was kicking
Position activePiecePosition; // the position of the piece currently in the board
std::shared_ptr<Piece> heldPiece; // a piece being holded
int nextQueueLength; // the number of next pieces seeable at a time
std::vector<Piece> nextQueue; // the list of the next pieces to spawn in the board
bool isLastMoveKick; // wheter the last action the piece did was kicking
public:
/**
* Creates a new board, generator, and next queue
@@ -30,88 +30,98 @@ class GameBoard {
GameBoard(int boardWidth, int boardHeight, const std::vector<Piece>& bag, int nextQueueLength);
/**
* Try moving the piece one cell to the left, and returns wheter it was sucessfull
* Tries moving the piece one position to the left
* @return If it suceeded
*/
bool moveLeft();
/**
* Try moving the piece one cell to the right, and returns wheter it was sucessfull
* Tries moving the piece one position to the right
* @return If it suceeded
*/
bool moveRight();
/**
* Try moving the piece one cell down, and returns wheter it was sucessfull
* Tries moving the piece one position down
* @return If it suceeded
*/
bool moveDown();
/**
* Try rotating the piece and kicking it if necessary, and returns wheter it was sucessfull
* Tries rotating the piece and kicking it if necessary
* @return If it suceeded
*/
bool rotate(Rotation rotation);
private:
/**
* Try kicking the piece, testing position either above or below the piece's initial position
* Tries kicking the piece, testing position either above or below the piece's initial position
* @return If it suceeded
*/
bool tryKicking(bool testingBottom, const std::set<Cell>& safeCells);
bool tryKicking(bool testingBottom, const std::set<Position>& safePositions);
public:
/**
* Try holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece,
* and returns wheter it was sucessfull
* Tries holding the active piece or swapping it if one was already stocked, while trying to apply an initial rotation to the newly spawned piece
* @return If it suceeded
*/
bool hold(Rotation initialRotation = NONE);
/**
* Spawns the next piece from the queue, and returns wheter it spawns in a wall
* Spawns the next piece from the queue
* @return If it spawned in a wall
*/
bool spawnNextPiece();
/**
* Returns wheter the active piece is touching walls directly below it
* Checks is the active piece as a wall directly below one of its position
* @return If it touches a ground
*/
bool touchesGround();
/**
* Lock the active piece into the board and returns the resulting line clear
* Locks the active piece into the board and clears lines if needed
* @return The resulting line clear
*/
LineClear lockPiece();
/**
* Returns a copy of the board
* @return A copy of the board
*/
Board getBoard() const;
/**
* Returns a copy of the active piece
* @return A copy of the active piece
*/
Piece getActivePiece() const;
/**
* Returns a copy of the position of the active piece
* @return A copy of the position of the active piece
*/
Cell getActivePiecePosition() const;
Position getActivePiecePosition() const;
/**
* Returns a copy of the held piece
* @return A copy of the held piece
*/
Piece getHeldPiece() const;
/**
* Returns a copy of the next piece queue
* @return A copy of the next piece queue
*/
std::vector<Piece> getNextPieces() const;
private:
/**
* Returns wheter the translated active piece is in a wall
* Checks if one of the active piece's positions touches a wall in the board
* @return If the active piece is in a wall
*/
bool isActivePieceInWall(const Cell& shift = Cell{0, 0}) const;
bool isActivePieceInWall(const Position& shift = Position{0, 0}) const;
/**
* Returns wheter the translated active piece overlaps with at least one of the cells
* Check if one of the active piece's positions shifted by a specified position would overlap with a set of positions
* @return If the shifted active piece overlaps with one of the position
*/
bool activePieceOverlapsOneCell(const std::set<Cell>& safeCells, const Cell& shift = Cell{0, 0}) const;
bool activePieceOverlapsOnePosition(const std::set<Position>& safePositions, const Position& shift = Position{0, 0}) const;
/**
* Sets the active piece to its spawn position
@@ -121,6 +131,7 @@ class GameBoard {
public:
/**
* Stream output operator, adds the board, the hold box and the next queue
* @return A reference to the output stream
*/
friend std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard);
};

View File

@@ -4,7 +4,10 @@
#include "Player.h"
GameParameters::GameParameters(Gamemode gamemode, const Player& controls) : gamemode(gamemode), controls(controls) {
GameParameters::GameParameters(Gamemode gamemode, const Player& controls) :
gamemode(gamemode),
controls(controls) {
// initialize lines and level
this->clearedLines = 0;
switch (this->gamemode) {
@@ -85,34 +88,37 @@ void GameParameters::updateStats() {
}
/* GRAVITY */
if (level >= 20) {
// all levels above 20 are instant gravity
this->gravity = 20 * 60;
// get gravity for an assumed 20-rows board
static const int gravityPerLevel[] = {
0, // LVL0
1, // 60f/line, 20s total
2, // 30f/line, 10s total
3, // 20f/line, 6.66s total
4, // 15f/line, 5s total
5, // 12f/line, 4s total
6, // 10f/line, 3.33 total
7, // 8.57f/line, 2.85s total
8, // 7.5f/line, 2.5s total
10, // 6f/line, 2s total
12, // 5f/line, 1.66s total
14, // 4.28f/line, 1.42s total
17, // 3.52f/line, 1.17s total
20, // 3f/line, 60f total
24, // 2.5f/line, 50f total
30, // 2f/line, 40f total
40, // 1.5f/line, 30f total
1 * 60, // 1line/f, 20f total
2 * 60, // 2line/f, 10f total
4 * 60 // 4line/f, 5f total
};
if (this->level < 0) {
this->gravity = gravityPerLevel[0];
}
else if (this->gravity > 20) {
this->gravity = gravityPerLevel[20];
}
else {
// get gravity for an assumed 20-rows board
switch (this->level) {
case 1 : {this->gravity = 1; break;} // 60f/line, 20s total
case 2 : {this->gravity = 2; break;} // 30f/line, 10s total
case 3 : {this->gravity = 3; break;} // 20f/line, 6.66s total
case 4 : {this->gravity = 4; break;} // 15f/line, 5s total
case 5 : {this->gravity = 5; break;} // 12f/line, 4s total
case 6 : {this->gravity = 6; break;} // 10f/line, 3.33 total
case 7 : {this->gravity = 7; break;} // 8.57f/line, 2.85s total
case 8 : {this->gravity = 8; break;} // 7.5f/line, 2.5s total
case 9 : {this->gravity = 10; break;} // 6f/line, 2s total
case 10 : {this->gravity = 12; break;} // 5f/line, 1.66s total
case 11 : {this->gravity = 14; break;} // 4.28f/line, 1.42s total
case 12 : {this->gravity = 17; break;} // 3.52f/line, 1.17s total
case 13 : {this->gravity = 20; break;} // 3f/line, 60f total
case 14 : {this->gravity = 24; break;} // 2.5f/line, 50f total
case 15 : {this->gravity = 30; break;} // 2f/line, 40f total
case 16 : {this->gravity = 40; break;} // 1.5f/line, 30f total
case 17 : {this->gravity = 1 * 60; break;} // 1line/f, 20f total
case 18 : {this->gravity = 2 * 60; break;} // 2line/f, 10f total
case 19 : {this->gravity = 4 * 60; break;} // 4line/f, 5f total
default : this->gravity = 1;
}
this->gravity = gravityPerLevel[this->level];
}
/* LOCK DELAY */

View File

@@ -10,20 +10,20 @@
*/
class GameParameters {
private:
Gamemode gamemode; // the current gamemode
Player controls; // the player's controls
int clearedLines; // the number of cleared lines
int level; // the current level
Gamemode gamemode; // the current gamemode
Player controls; // the player's controls
int clearedLines; // the number of cleared lines
int level; // the current level
int nextQueueLength; // the number of pieces visibles in the next queue
bool boneBlocks; // wheter all blocks are bone blocks
int gravity; // the gravity at which pieces drop
int lockDelay; // the time before the piece lock in place
bool boneBlocks; // wheter all blocks are bone blocks
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
int ARE; // the time before the next piece spawn
int lineARE; // the time before the next piece spawn, after clearing a line
int DAS; // the time before the piece repeats moving
int ARR; // the rate at which the piece repeats moving
int SDR; // the rate at which the piece soft drops
int ARE; // the time before the next piece spawn
int lineARE; // the time before the next piece spawn, after clearing a line
int DAS; // the time before the piece repeats moving
int ARR; // the rate at which the piece repeats moving
int SDR; // the rate at which the piece soft drops
public:
/**
@@ -32,12 +32,13 @@ class GameParameters {
GameParameters(Gamemode gamemode, const Player& controls);
/**
* Count the newly cleared lines and update level and stats if needed
* Counts the newly cleared lines and update level and stats if needed
*/
void clearLines(int lineNumber);
/**
* Returns wheter the game ended
* Checks if the game ended based on the current states and time passed, accorind to the gamemode
* @return If the player has won
*/
bool hasWon(int framesPassed);
@@ -49,17 +50,17 @@ class GameParameters {
public:
/**
* Returns the current number of cleared line
* @return The current number of cleared line
*/
int getClearedLines();
/**
* Returns the current level
* @return The current level
*/
int getLevel();
/**
* Returns the length of the next queue
* @return The length of the next queue
*/
int getNextQueueLength();
@@ -69,42 +70,42 @@ class GameParameters {
bool getBoneBlocks();
/**
* Returns the current gravity for a 20-line high board
* @return The current gravity for a 20-line high board
*/
int getGravity();
/**
* Returns the current lock delay
* @return The current lock delay
*/
int getLockDelay();
/**
* Returns the current forced lock delay
* @return The current forced lock delay
*/
int getForcedLockDelay();
/**
* Returns the current ARE
* @return The current ARE
*/
int getARE();
/**
* Returns the current line ARE
* @return The current line ARE
*/
int getLineARE();
/**
* Returns the current DAS
* @return The current DAS
*/
int getDAS();
/**
* Returns the current ARR
* @return The current ARR
*/
int getARR();
/**
* Returns the current SDR
* @return The current SDR
*/
int getSDR();
};

View File

@@ -5,7 +5,7 @@
* Specify how many lines were cleared and how
*/
struct LineClear {
int lines; // the number of lines cleared
bool isSpin; // if the move was a spin
int lines; // the number of lines cleared
bool isSpin; // if the move was a spin
bool isMiniSpin; // if the move was a spin mini
};

View File

@@ -1,11 +1,11 @@
#include "Player.h"
static const int DAS_MIN_VALUE = 0;
static const int DAS_MAX_VALUE = 30;
static const int ARR_MIN_VALUE = 0;
static const int ARR_MAX_VALUE = 30;
static const int SDR_MIN_VALUE = 0;
static const int SDR_MAX_VALUE = 6;
static const int DAS_MIN_VALUE = 0; // 0ms
static const int DAS_MAX_VALUE = 30; // 500ms
static const int ARR_MIN_VALUE = 0; // 0ms
static const int ARR_MAX_VALUE = 30; // 500ms
static const int SDR_MIN_VALUE = 0; // 0ms
static const int SDR_MAX_VALUE = 6; // 100ms
Player::Player() {

View File

@@ -17,32 +17,35 @@ class Player {
Player();
/**
* Try setting DAS to the desired value, and returns wheter it is possible
* Try setting DAS to the desired value
* @return If it is possible
*/
bool setDAS(int DAS);
/**
* Try setting ARR to the desired value, and returns wheter it is possible
* Try setting ARR to the desired value
* @return If it is possible
*/
bool setARR(int ARR);
/**
* Try setting SDR to the desired value, and returns wheter it is possible
* Try setting SDR to the desired value
* @return If it is possible
*/
bool setSDR(int SDR);
/**
* Returns DAS value
* @return DAS value
*/
int getDAS();
/**
* Returns ARR value
* @return ARR value
*/
int getARR();
/**
* Returns SDR value
* @return SDR value
*/
int getSDR();
};