ajouté interface textuelle
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 162 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
@@ -7,11 +7,13 @@ We will only talk about pieces and not polyominos. In this project, pieces are a
|
||||
|
||||
Each frame, the UI will translate the user's input into a series of action to apply to the game. The list of action is the following:
|
||||
|
||||
- Quit the game
|
||||
- Pause
|
||||
- Retry
|
||||
- Hold
|
||||
- Move left
|
||||
- Move right
|
||||
- Rotate 0°
|
||||
- Rotate clockwise (CW)
|
||||
- Rotate 180°
|
||||
- Rotate counter-clockwise (CCW)
|
||||
@@ -44,11 +46,14 @@ Since this game uses polyomino of high sizes which are very unplayable, we will
|
||||
|
||||
1. Before rotating, mark every position containing the piece or touching the piece, we will call the set of all theses positions the ``safePositions``
|
||||
2. Rotate the piece, if it fit stop the algorithm
|
||||
3. Try fitting the piece, going from the center to the sides, that means we try to move the piece 1 position right, then 1 position left, then 2 position right, etc. until it fit (and then stop the algorithm), if at one point a position doesn't touch one of the ``safePositions`` we stop trying in this direction
|
||||
3. Try fitting the piece, going from the center to the sides, that means we try to move the piece 1 position right, then 1 position left, then 2 position right, etc. until it fit (and then stop the algorithm), if at one point none of the squares of the pieces are in one of the ``safePositions`` we stop trying in this direction
|
||||
4. Move the piece one line down, and repeat step 3 again, until we hit a line were the first position (shifted by 0 positions horizontally) touched none of the ``safePositions``
|
||||
5. Do the same as step 4 but now we move the piece one line up every time
|
||||
6. Cancel the rotation
|
||||
|
||||
Kicking is primarly designed for rotating, but it is also applied when a piece spawns into a wall.
|
||||
0° rotations will first try to move the piece one position down, and if it can't try kicking the piece, only registering a kick if the piece couldn't move down.
|
||||
|
||||
## Detecting spins
|
||||
|
||||
Another common mechanic of stacker games that goes alongside kicking is spinning. A spin is a special move (a move is calculated once a piece has been locked to the board) which usually happen when the last move a piece did was a kick or a rotation and the piece is locked in place or is locked in certain corners, but the rules varies a lot from game to game.
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/**
|
||||
* The list of actions that can be taken by the player
|
||||
*/
|
||||
enum Action {
|
||||
QUIT,
|
||||
PAUSE,
|
||||
RETRY,
|
||||
HOLD,
|
||||
@@ -12,7 +15,33 @@ enum Action {
|
||||
HARD_DROP,
|
||||
MOVE_LEFT,
|
||||
MOVE_RIGHT,
|
||||
ROTATE_0,
|
||||
ROTATE_CW,
|
||||
ROTATE_180,
|
||||
ROTATE_CCW
|
||||
};
|
||||
|
||||
|
||||
static const std::string ACTION_NAMES[] = { // name for each action
|
||||
"Quit",
|
||||
"Pause",
|
||||
"Retry",
|
||||
"Hold",
|
||||
"Soft drop",
|
||||
"Hard drop",
|
||||
"Move left",
|
||||
"Move right",
|
||||
"Rotate 0°",
|
||||
"Rotate CW",
|
||||
"Rotate 180°",
|
||||
"Rotate CCW"
|
||||
};
|
||||
|
||||
/**
|
||||
* Stream output operator, adds the name of the action
|
||||
* @return A reference to the output stream
|
||||
*/
|
||||
inline std::ostream& operator<<(std::ostream& os, const Action action) {
|
||||
os << ACTION_NAMES[action];
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ Block Board::getBlock(const Position& position) const {
|
||||
return this->grid.at(position.y).at(position.x);
|
||||
}
|
||||
|
||||
std::vector<std::vector<Block>> Board::getBlocks() const {
|
||||
const std::vector<std::vector<Block>>& Board::getBlocks() const {
|
||||
return this->grid;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,14 +44,14 @@ class Board {
|
||||
void clearBoard();
|
||||
|
||||
/**
|
||||
* @return A copy of the block at the specified position
|
||||
* @return The block at the specified position
|
||||
*/
|
||||
Block getBlock(const Position& position) const;
|
||||
|
||||
/**
|
||||
* @return A copy of the grid
|
||||
* @return The grid
|
||||
*/
|
||||
std::vector<std::vector<Block>> getBlocks() const;
|
||||
const std::vector<std::vector<Block>>& getBlocks() const;
|
||||
|
||||
/**
|
||||
* @return The width of the grid
|
||||
|
||||
@@ -25,7 +25,7 @@ Game::Game(Gamemode gamemode, const Player& controls, int boardWidth, int boardH
|
||||
|
||||
void Game::start() {
|
||||
this->started = true;
|
||||
this->lost = this->board.spawnNextPiece();
|
||||
this->leftARETime = 1;
|
||||
}
|
||||
|
||||
void Game::reset() {
|
||||
@@ -48,7 +48,6 @@ void Game::initialize() {
|
||||
this->heldDAS = 0;
|
||||
this->heldARR = 0;
|
||||
this->subVerticalPosition = 0;
|
||||
this->leftARETime = 0;
|
||||
this->totalLockDelay = 0;
|
||||
this->totalForcedLockDelay = 0;
|
||||
}
|
||||
@@ -64,14 +63,14 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
|
||||
if (this->leftARETime == 0) {
|
||||
if (AREJustEnded) {
|
||||
this->board.spawnNextPiece();
|
||||
this->lost = this->board.spawnNextPiece();
|
||||
}
|
||||
|
||||
/* IRS and IHS */
|
||||
Rotation initialRotation = NONE
|
||||
+ (this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE
|
||||
+ (this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE
|
||||
+ (this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE;
|
||||
+ ((this->initialActions.contains(ROTATE_CW)) ? CLOCKWISE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_180)) ? DOUBLE : NONE)
|
||||
+ ((this->initialActions.contains(ROTATE_CCW)) ? COUNTERCLOCKWISE : NONE);
|
||||
|
||||
if (this->initialActions.contains(HOLD)) {
|
||||
if (this->board.hold(initialRotation)) {
|
||||
@@ -79,12 +78,34 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
this->totalLockDelay = 0;
|
||||
this->heldARR = 0;
|
||||
}
|
||||
else {
|
||||
this->lost = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (initialRotation != NONE) {
|
||||
this->board.rotate(initialRotation);
|
||||
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 (this->lost) {
|
||||
if (initialRotation == NONE && (!this->initialActions.contains(ROTATE_0))) {
|
||||
this->board.rotate(NONE);
|
||||
}
|
||||
}
|
||||
if (this->lost) {
|
||||
this->framesPassed++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* HOLD */
|
||||
if (playerActions.contains(HOLD) && (!this->heldActions.contains(HOLD))) {
|
||||
@@ -96,25 +117,48 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
}
|
||||
|
||||
/* MOVE LEFT/RIGHT */
|
||||
Position before = this->board.getActivePiecePosition();
|
||||
|
||||
if (playerActions.contains(MOVE_LEFT)) {
|
||||
this->movePiece(-1, (this->heldDAS >= 0));
|
||||
this->movePiece(-1);
|
||||
}
|
||||
if (playerActions.contains(MOVE_RIGHT)) {
|
||||
this->movePiece(1, (this->heldDAS <= 0));
|
||||
else if (playerActions.contains(MOVE_RIGHT)) {
|
||||
this->movePiece(1);
|
||||
}
|
||||
else {
|
||||
this->heldDAS = 0;
|
||||
}
|
||||
|
||||
if (before != this->board.getActivePiecePosition()) {
|
||||
this->totalLockDelay = 0;
|
||||
}
|
||||
|
||||
/* ROTATIONS */
|
||||
before = this->board.getActivePiecePosition();
|
||||
|
||||
if (playerActions.contains(ROTATE_0) && (!this->heldActions.contains(ROTATE_0))) {
|
||||
if (this->board.rotate(NONE)) {
|
||||
this->totalLockDelay = 0;
|
||||
}
|
||||
}
|
||||
if (playerActions.contains(ROTATE_CW) && (!this->heldActions.contains(ROTATE_CW))) {
|
||||
this->board.rotate(CLOCKWISE);
|
||||
if (this->board.rotate(CLOCKWISE)) {
|
||||
this->totalLockDelay = 0;
|
||||
}
|
||||
}
|
||||
if (playerActions.contains(ROTATE_180) && (!this->heldActions.contains(ROTATE_180))) {
|
||||
this->board.rotate(DOUBLE);
|
||||
if (this->board.rotate(DOUBLE)) {
|
||||
this->totalLockDelay = 0;
|
||||
}
|
||||
}
|
||||
if (playerActions.contains(ROTATE_CCW) && (!this->heldActions.contains(ROTATE_CCW))) {
|
||||
this->board.rotate(COUNTERCLOCKWISE);
|
||||
if (this->board.rotate(COUNTERCLOCKWISE)) {
|
||||
this->totalLockDelay = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (before != this->board.getActivePiecePosition()) {
|
||||
this->subVerticalPosition = 0;
|
||||
}
|
||||
|
||||
/* SOFT DROP */
|
||||
@@ -149,7 +193,7 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
else {
|
||||
/* GRAVITY */
|
||||
// parameters.getGravity() gives the gravity for an assumed 20-line high board
|
||||
int appliedGravity = this->parameters.getGravity() * (this->board.getBoard().getBaseHeight() / 20.0);
|
||||
int appliedGravity = this->parameters.getGravity() * std::max((double) this->board.getBoard().getBaseHeight() / 20.0, 1.0);
|
||||
|
||||
this->subVerticalPosition += appliedGravity;
|
||||
while (this->subVerticalPosition >= SUBPX_PER_ROW) {
|
||||
@@ -171,16 +215,18 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
}
|
||||
}
|
||||
|
||||
// remove initial actions only once they've been applied
|
||||
if (AREJustEnded) {
|
||||
this->initialActions.clear();
|
||||
}
|
||||
}
|
||||
|
||||
this->framesPassed++;
|
||||
if (this->lost) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// update remembered actions
|
||||
// update remembered actions for next frame
|
||||
if ((!this->started) || this->leftARETime > 0) {
|
||||
for (Action action : playerActions) {
|
||||
this->initialActions.insert(action);
|
||||
@@ -189,19 +235,24 @@ void Game::nextFrame(const std::set<Action>& playerActions) {
|
||||
|
||||
this->heldActions = playerActions;
|
||||
|
||||
if (this->leftARETime > 0) {
|
||||
if (playerActions.contains(MOVE_LEFT)) {
|
||||
this->heldDAS = std::min(-1, this->heldDAS - 1);
|
||||
}
|
||||
if (playerActions.contains(MOVE_RIGHT)) {
|
||||
else if (playerActions.contains(MOVE_RIGHT)) {
|
||||
this->heldDAS = std::max(1, this->heldDAS + 1);
|
||||
}
|
||||
else {
|
||||
this->heldDAS = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::movePiece(int movement, bool resetDirection) {
|
||||
if (resetDirection) {
|
||||
void Game::movePiece(int movement) {
|
||||
int appliedDAS = this->parameters.getDAS();
|
||||
int appliedARR = this->parameters.getARR();
|
||||
|
||||
if ((this->heldDAS * movement) <= 0) {
|
||||
this->heldDAS = movement;
|
||||
this->heldARR = 0;
|
||||
}
|
||||
@@ -209,9 +260,12 @@ void Game::movePiece(int movement, bool resetDirection) {
|
||||
this->heldDAS += movement;
|
||||
}
|
||||
|
||||
if (abs(this->heldDAS) > this->parameters.getDAS()) {
|
||||
int appliedARR = this->parameters.getARR();
|
||||
if (abs(this->heldDAS) == appliedDAS + 1) {
|
||||
if (movement == -1) this->board.moveLeft();
|
||||
if (movement == 1) this->board.moveRight();
|
||||
}
|
||||
|
||||
if (abs(this->heldDAS) > appliedDAS + 1) {
|
||||
// ARR=0 -> instant movement
|
||||
if (appliedARR == 0) {
|
||||
if (movement == -1) while (this->board.moveLeft());
|
||||
@@ -233,7 +287,6 @@ void Game::lockPiece() {
|
||||
LineClear clear = this->board.lockPiece();
|
||||
this->parameters.clearLines(clear.lines);
|
||||
|
||||
// update B2B and score
|
||||
bool B2BConditionsAreMet = ((clear.lines > B2B_MIN_LINE_NUMBER) || clear.isSpin || clear.isMiniSpin);
|
||||
if (clear.lines > 0) {
|
||||
/* clearing one more line is worth 2x more
|
||||
@@ -246,16 +299,14 @@ void Game::lockPiece() {
|
||||
}
|
||||
this->B2BChain = B2BConditionsAreMet;
|
||||
|
||||
// reset active piece
|
||||
this->subVerticalPosition = 0;
|
||||
this->totalLockDelay = 0;
|
||||
this->totalForcedLockDelay = 0;
|
||||
this->heldARR = 0;
|
||||
|
||||
// check for ARE
|
||||
this->leftARETime = this->parameters.getARE();
|
||||
if (this->leftARETime == 0) {
|
||||
this->board.spawnNextPiece();
|
||||
this->lost = this->board.spawnNextPiece();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,22 +342,26 @@ bool Game::areBlocksBones() const {
|
||||
return this->parameters.getBoneBlocks();
|
||||
}
|
||||
|
||||
Board Game::getBoard() const {
|
||||
Position Game::ghostPiecePosition() const {
|
||||
return this->board.lowestPosition();
|
||||
}
|
||||
|
||||
const Board& Game::getBoard() const {
|
||||
return this->board.getBoard();
|
||||
}
|
||||
|
||||
Piece Game::getActivePiece() const {
|
||||
const std::shared_ptr<Piece>& Game::getActivePiece() const {
|
||||
return this->board.getActivePiece();
|
||||
}
|
||||
|
||||
Position Game::getActivePiecePosition() const {
|
||||
const Position& Game::getActivePiecePosition() const {
|
||||
return this->board.getActivePiecePosition();
|
||||
}
|
||||
|
||||
Piece Game::getHeldPiece() const {
|
||||
const std::shared_ptr<Piece>& Game::getHeldPiece() const {
|
||||
return this->board.getHeldPiece();
|
||||
}
|
||||
|
||||
std::vector<Piece> Game::getNextPieces() const {
|
||||
const std::vector<Piece>& Game::getNextPieces() const {
|
||||
return this->board.getNextPieces();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class Game {
|
||||
/**
|
||||
* Move the piece in the specified direction
|
||||
*/
|
||||
void movePiece(int movement, bool resetDirection);
|
||||
void movePiece(int movement);
|
||||
|
||||
/**
|
||||
* Locks the piece, updates level and score and spawns the next piece if necessary
|
||||
@@ -112,27 +112,32 @@ class Game {
|
||||
bool areBlocksBones() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the board
|
||||
* @return The position of the ghost piece
|
||||
*/
|
||||
Board getBoard() const;
|
||||
Position ghostPiecePosition() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the active piece
|
||||
* @return The board
|
||||
*/
|
||||
Piece getActivePiece() const;
|
||||
const Board& getBoard() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the active piece position
|
||||
* @return A pointer to the active piece, can be null
|
||||
*/
|
||||
Position getActivePiecePosition() const;
|
||||
const std::shared_ptr<Piece>& getActivePiece() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the held piece
|
||||
* @return The position of the active piece
|
||||
*/
|
||||
Piece getHeldPiece() const;
|
||||
const Position& getActivePiecePosition() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the next pieces queue
|
||||
* @return A pointer to the held piece, can be null
|
||||
*/
|
||||
std::vector<Piece> getNextPieces() const;
|
||||
const std::shared_ptr<Piece>& getHeldPiece() const;
|
||||
|
||||
/**
|
||||
* @return The next piece queue, can be empty
|
||||
*/
|
||||
const std::vector<Piece>& getNextPieces() const;
|
||||
};
|
||||
|
||||
@@ -72,15 +72,22 @@ bool GameBoard::moveDown() {
|
||||
|
||||
bool GameBoard::rotate(Rotation rotation) {
|
||||
Piece stored = *this->activePiece;
|
||||
this->rotate(rotation);
|
||||
this->activePiece->rotate(rotation);
|
||||
|
||||
// before trying to kick, check if the piece can rotate without kicking
|
||||
if (rotation == NONE) {
|
||||
if (this->moveDown()) {
|
||||
this->isLastMoveKick = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!this->activePieceInWall()) {
|
||||
this->isLastMoveKick = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// get the list of positions that touches the original piece
|
||||
std::set<Position> safePositions;
|
||||
for (Position position : stored.getPositions()) {
|
||||
Position positionInGrid(position + this->activePiecePosition);
|
||||
@@ -91,7 +98,10 @@ bool GameBoard::rotate(Rotation rotation) {
|
||||
safePositions.insert(positionInGrid + Position{-1, 0});
|
||||
}
|
||||
|
||||
// try kicking the piece down
|
||||
// first try kicking the piece down
|
||||
if (rotation == NONE) {
|
||||
this->activePiecePosition.y -= 1;
|
||||
}
|
||||
bool suceeded = this->tryKicking(true, safePositions);
|
||||
if (suceeded) {
|
||||
this->isLastMoveKick = true;
|
||||
@@ -99,6 +109,9 @@ bool GameBoard::rotate(Rotation rotation) {
|
||||
}
|
||||
|
||||
// if it doesn't work try kicking the piece up
|
||||
if (rotation == NONE) {
|
||||
this->activePiecePosition.y += 1;
|
||||
}
|
||||
suceeded = this->tryKicking(false, safePositions);
|
||||
if (suceeded) {
|
||||
this->isLastMoveKick = true;
|
||||
@@ -107,7 +120,10 @@ bool GameBoard::rotate(Rotation rotation) {
|
||||
|
||||
// if it still doesn't work, abort the rotation
|
||||
this->activePiece = std::make_shared<Piece>(stored);
|
||||
return false;
|
||||
if (rotation == NONE) {
|
||||
this->isLastMoveKick = false;
|
||||
}
|
||||
return (rotation == NONE);
|
||||
}
|
||||
|
||||
bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePositions) {
|
||||
@@ -118,9 +134,9 @@ bool GameBoard::tryKicking(bool testingBottom, const std::set<Position>& safePos
|
||||
// we try from the center to the sides as long as the kicked piece touches the original
|
||||
bool overlapsLeft = true;
|
||||
bool overlapsRight = true;
|
||||
int i = 0;
|
||||
int i = (j == 0) ? 1 : 0;
|
||||
do {
|
||||
// check right before right arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations
|
||||
// check right before left arbitrarly, we don't decide this with rotations since it would still be arbitrary with 180° rotations
|
||||
if (overlapsRight) {
|
||||
Position shift{+i, j};
|
||||
if (!this->activePieceOverlaps(safePositions, shift)) {
|
||||
@@ -175,14 +191,15 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
}
|
||||
|
||||
this->goToSpawnPosition();
|
||||
|
||||
Piece stored = *this->activePiece;
|
||||
Position storedPosition = this->activePiecePosition;
|
||||
this->goToSpawnPosition();
|
||||
this->rotate(initialRotation);
|
||||
|
||||
// if the piece can't spawn, abort initial rotation
|
||||
if (this->activePieceInWall()) {
|
||||
this->activePiece = std::make_shared<Piece>(stored);
|
||||
this->goToSpawnPosition();
|
||||
|
||||
// if the piece still can't spawn, abort holding
|
||||
if (this->activePieceInWall()) {
|
||||
@@ -190,6 +207,7 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
this->activePiece = nullptr;
|
||||
}
|
||||
std::swap(this->activePiece, this->heldPiece);
|
||||
this->activePiecePosition = storedPosition;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -200,6 +218,8 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
this->nextQueue.erase(this->nextQueue.begin());
|
||||
}
|
||||
|
||||
this->heldPiece->defaultRotation();
|
||||
|
||||
// this piece has done nothing yet
|
||||
this->isLastMoveKick = false;
|
||||
|
||||
@@ -207,10 +227,7 @@ bool GameBoard::hold(Rotation initialRotation) {
|
||||
}
|
||||
|
||||
bool GameBoard::spawnNextPiece() {
|
||||
// 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());
|
||||
|
||||
@@ -219,13 +236,22 @@ bool GameBoard::spawnNextPiece() {
|
||||
// this piece has done nothing yet
|
||||
this->isLastMoveKick = false;
|
||||
|
||||
return !this->activePieceInWall();
|
||||
return this->activePieceInWall();
|
||||
}
|
||||
|
||||
bool GameBoard::touchesGround() {
|
||||
bool GameBoard::touchesGround() const {
|
||||
return this->activePieceInWall(Position{0, -1});
|
||||
}
|
||||
|
||||
Position GameBoard::lowestPosition() const {
|
||||
Position shift = Position{0, -1};
|
||||
while (!activePieceInWall(shift)) {
|
||||
shift.y -= 1;
|
||||
}
|
||||
shift.y += 1;
|
||||
return (this->activePiecePosition + shift);
|
||||
}
|
||||
|
||||
LineClear GameBoard::lockPiece() {
|
||||
bool isLockedInPlace = (this->activePieceInWall(Position{0, 1}) && this->activePieceInWall(Position{1, 0})
|
||||
&& this->activePieceInWall(Position{-1, 0}) && this->activePieceInWall(Position{0, -1}));
|
||||
@@ -248,23 +274,23 @@ void GameBoard::addGarbageRows(int number) {
|
||||
}
|
||||
}
|
||||
|
||||
Board GameBoard::getBoard() const {
|
||||
const Board& GameBoard::getBoard() const {
|
||||
return this->board;
|
||||
}
|
||||
|
||||
Piece GameBoard::getActivePiece() const {
|
||||
return *this->activePiece;
|
||||
const std::shared_ptr<Piece>& GameBoard::getActivePiece() const {
|
||||
return this->activePiece;
|
||||
}
|
||||
|
||||
Position GameBoard::getActivePiecePosition() const {
|
||||
const Position& GameBoard::getActivePiecePosition() const {
|
||||
return this->activePiecePosition;
|
||||
}
|
||||
|
||||
Piece GameBoard::getHeldPiece() const {
|
||||
return *this->heldPiece;
|
||||
const std::shared_ptr<Piece>& GameBoard::getHeldPiece() const {
|
||||
return this->heldPiece;
|
||||
}
|
||||
|
||||
std::vector<Piece> GameBoard::getNextPieces() const {
|
||||
const std::vector<Piece>& GameBoard::getNextPieces() const {
|
||||
return this->nextQueue;
|
||||
}
|
||||
|
||||
@@ -293,6 +319,8 @@ void GameBoard::goToSpawnPosition() {
|
||||
|
||||
// center the piece horizontally, biased towards left
|
||||
this->activePiecePosition.x = (this->board.getWidth() - this->activePiece->getLength()) / 2;
|
||||
|
||||
this->activePiece->defaultRotation();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const GameBoard& gameboard) {
|
||||
|
||||
@@ -60,7 +60,7 @@ class GameBoard {
|
||||
bool moveDown();
|
||||
|
||||
/**
|
||||
* Tries rotating the piece and kicking it if necessary
|
||||
* Tries rotating the piece and kicking it if necessary, if it's a 0° rotation, it will forcefully try kicking
|
||||
* @return If it suceeded
|
||||
*/
|
||||
bool rotate(Rotation rotation);
|
||||
@@ -89,7 +89,13 @@ class GameBoard {
|
||||
* Checks is the active piece as a wall directly below one of its position
|
||||
* @return If it touches a ground
|
||||
*/
|
||||
bool touchesGround();
|
||||
bool touchesGround() const;
|
||||
|
||||
/**
|
||||
* Computes what the piece position would be if it were to be dropped down as much as possible
|
||||
* @return The lowest position before hitting a wall
|
||||
*/
|
||||
Position lowestPosition() const;
|
||||
|
||||
/**
|
||||
* Locks the active piece into the board and clears lines if needed
|
||||
@@ -103,34 +109,34 @@ class GameBoard {
|
||||
void addGarbageRows(int number);
|
||||
|
||||
/**
|
||||
* @return A copy of the board
|
||||
* @return The board
|
||||
*/
|
||||
Board getBoard() const;
|
||||
const Board& getBoard() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the active piece
|
||||
* @return A pointer to the active piece, can be null
|
||||
*/
|
||||
Piece getActivePiece() const;
|
||||
const std::shared_ptr<Piece>& getActivePiece() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the position of the active piece
|
||||
* @return The position of the active piece
|
||||
*/
|
||||
Position getActivePiecePosition() const;
|
||||
const Position& getActivePiecePosition() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the held piece
|
||||
* @return A pointer to the held piece, can be null
|
||||
*/
|
||||
Piece getHeldPiece() const;
|
||||
const std::shared_ptr<Piece>& getHeldPiece() const;
|
||||
|
||||
/**
|
||||
* @return A copy of the next piece queue
|
||||
* @return The next piece queue, can be empty
|
||||
*/
|
||||
std::vector<Piece> getNextPieces() const;
|
||||
const std::vector<Piece>& getNextPieces() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Checks if one of the active piece's positions touches a wall in the board
|
||||
* @return If the active piece is in a wall
|
||||
* @return If the active piece spawned in a wall
|
||||
*/
|
||||
bool activePieceInWall(const Position& shift = Position{0, 0}) const;
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ void GameParameters::updateStats() {
|
||||
if (this->level < 0) {
|
||||
this->gravity = gravityPerLevel[0];
|
||||
}
|
||||
else if (this->gravity > 20) {
|
||||
else if (this->level > 20) {
|
||||
this->gravity = gravityPerLevel[20];
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -77,12 +77,12 @@ class PiecesList {
|
||||
int getNumberOfPieces(int size) const;
|
||||
|
||||
/**
|
||||
* @return The indexes of all selected pieces
|
||||
* @return A copy of the indexes of all selected pieces
|
||||
*/
|
||||
std::vector<std::pair<int, int>> getSelectedPieces() const;
|
||||
|
||||
/**
|
||||
* @return The piece corresponding to the specified index
|
||||
* @return A copy of the piece corresponding to the specified index
|
||||
*/
|
||||
Piece getPiece(const std::pair<int, int>& pieceIndex) const;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
Piece::Piece(const Polyomino& polyomino, Block blockType) :
|
||||
polyomino(polyomino),
|
||||
blockType(blockType) {
|
||||
|
||||
this->rotationState = NONE;
|
||||
}
|
||||
|
||||
void Piece::rotate(Rotation rotation) {
|
||||
@@ -21,9 +23,22 @@ void Piece::rotate(Rotation rotation) {
|
||||
this->polyomino.rotate180();
|
||||
if (rotation == COUNTERCLOCKWISE)
|
||||
this->polyomino.rotateCCW();
|
||||
|
||||
this->rotationState += rotation;
|
||||
}
|
||||
|
||||
std::set<Position> Piece::getPositions() const {
|
||||
void Piece::defaultRotation() {
|
||||
if (this->rotationState == CLOCKWISE)
|
||||
this->polyomino.rotateCCW();
|
||||
if (this->rotationState == DOUBLE)
|
||||
this->polyomino.rotate180();
|
||||
if (this->rotationState == COUNTERCLOCKWISE)
|
||||
this->polyomino.rotateCW();
|
||||
|
||||
this->rotationState = NONE;
|
||||
}
|
||||
|
||||
const std::set<Position>& Piece::getPositions() const {
|
||||
return this->polyomino.getPositions();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class Piece {
|
||||
private:
|
||||
Polyomino polyomino; // a polyomino representing the piece, (0, 0) is downleft
|
||||
Block blockType; // the block type of the piece
|
||||
Rotation rotationState; // the current rotation of the piece
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -28,9 +29,14 @@ class Piece {
|
||||
void rotate(Rotation rotation);
|
||||
|
||||
/**
|
||||
* @return A copy of the list of positions of the piece
|
||||
* Rotates the piece to its default rotation
|
||||
*/
|
||||
std::set<Position> getPositions() const;
|
||||
void defaultRotation();
|
||||
|
||||
/**
|
||||
* @return The list of positions of the piece
|
||||
*/
|
||||
const std::set<Position>& getPositions() const;
|
||||
|
||||
/**
|
||||
* @return The length of the piece
|
||||
|
||||
@@ -267,7 +267,7 @@ void Polyomino::tryToInsertPosition(std::set<Position>& emptyPositions, const Po
|
||||
tryToInsertPosition(emptyPositions, Position{candidate.x - 1, candidate.y});
|
||||
}
|
||||
|
||||
std::set<Position> Polyomino::getPositions() const {
|
||||
const std::set<Position>& Polyomino::getPositions() const {
|
||||
return this->positions;
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ bool Polyomino::operator<(const Polyomino& other) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Polyomino::operator ==(const Polyomino& other) const {
|
||||
bool Polyomino::operator==(const Polyomino& other) const {
|
||||
return this->positions == other.positions;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ class Polyomino {
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return A copy of the positions of the polyomino
|
||||
* @return The positions of the polyomino
|
||||
*/
|
||||
std::set<Position> getPositions() const;
|
||||
const std::set<Position>& getPositions() const;
|
||||
|
||||
/**
|
||||
* @return The length of the polyomino
|
||||
@@ -103,7 +103,7 @@ class Polyomino {
|
||||
* 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
|
||||
* @return If the polyomino is equal to another
|
||||
*/
|
||||
bool operator ==(const Polyomino& other) const;
|
||||
bool operator==(const Polyomino& other) const;
|
||||
|
||||
/**
|
||||
* Stream output operator, adds a 2D grid representing the polyomino
|
||||
|
||||
@@ -62,6 +62,14 @@ inline bool operator==(const Position& left, const Position& right) {
|
||||
return (left.x == right.x) && (left.y == right.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator, two positions aren't equal if their coordinates aren't
|
||||
* @return If the two positions aren't equals
|
||||
*/
|
||||
inline bool operator!=(const Position& left, const Position& right) {
|
||||
return (left.x != right.x) || (left.y != right.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream output operator, adds the coordinates of the position to the stream
|
||||
* @return A reference to the output stream
|
||||
|
||||
@@ -17,7 +17,7 @@ enum Rotation {
|
||||
* @return A rotation corresponding to doing both rotations
|
||||
*/
|
||||
inline Rotation operator+(const Rotation& left, const Rotation& right) {
|
||||
return Rotation((left + right) % 4);
|
||||
return Rotation(((int) left + (int) right) % 4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
323
src/TextUI/TextApp.cpp
Normal file
323
src/TextUI/TextApp.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
#include "TextApp.h"
|
||||
|
||||
#include "../Core/Menu.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
|
||||
static const int FRAMES_PER_INPUT = FRAMES_PER_SECOND / 2;
|
||||
static const int MAXIMUM_PIECE_SIZE = 10;
|
||||
static const int DEFAULT_PIECE_SIZE = 4;
|
||||
static const int MAXIMUM_BOARD_WIDTH = 30;
|
||||
static const int MAXIMYM_BOARD_HEIGHT = 40;
|
||||
static const Gamemode DEFAULT_GAMEMODE = SPRINT;
|
||||
|
||||
|
||||
TextApp::TextApp() {
|
||||
this->defaultKeybinds();
|
||||
|
||||
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().setARR(FRAMES_PER_INPUT);
|
||||
this->gameMenu.getPlayerControls().setSDR(0);
|
||||
}
|
||||
|
||||
void TextApp::run() {
|
||||
bool quit = false;
|
||||
std::string answer;
|
||||
int selectedAnswer = 0;
|
||||
while (!quit) {
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "===| WELCOME TO JMINOS! |===" << std::endl;
|
||||
std::cout << "1- Change pieces" << std::endl;
|
||||
std::cout << "2- Change board" << std::endl;
|
||||
std::cout << "3- See controls" << std::endl;
|
||||
std::cout << "4- Start game" << std::endl;
|
||||
std::cout << "5- Quit" << std::endl;
|
||||
std::cout << "Choice: ";
|
||||
std::getline(std::cin, answer);
|
||||
try {
|
||||
selectedAnswer = std::stoi(answer);
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
|
||||
switch (selectedAnswer) {
|
||||
case 1 : {this->choosePieces(); break;}
|
||||
case 2 : {this->chooseBoardSize(); break;}
|
||||
case 3 : {this->seeKeybinds(); break;}
|
||||
case 4 : {this->startGame(); break;}
|
||||
case 5 : {quit = true; break;}
|
||||
default : std::cout << "Invalid answer!" << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "===| SEE YA NEXT TIME! |===";
|
||||
}
|
||||
|
||||
void TextApp::choosePieces() {
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "Choose which piece sizes to play with (from 1 to " << MAXIMUM_PIECE_SIZE << "), separate mutltiple sizes with blank spaces." << std::endl;
|
||||
std::cout << "Choice: ";
|
||||
std::string answer;
|
||||
std::getline(std::cin, answer);
|
||||
|
||||
this->gameMenu.getPiecesList().unselectAll();
|
||||
std::cout << "Selected pieces of sizes:";
|
||||
std::stringstream answerStream(answer);
|
||||
for (std::string size; std::getline(answerStream, size, ' ');) {
|
||||
try {
|
||||
int selectedSize = std::stoi(size);
|
||||
if (selectedSize >= 1 && selectedSize <= MAXIMUM_PIECE_SIZE) {
|
||||
if (this->gameMenu.getPiecesList().selectAllPieces(selectedSize)) {
|
||||
std::cout << " " << selectedSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
}
|
||||
|
||||
std::string waiting;
|
||||
std::getline(std::cin, waiting);
|
||||
}
|
||||
|
||||
void TextApp::chooseBoardSize() {
|
||||
std::string answer;
|
||||
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "Current board width and height: " << this->gameMenu.getBoardWidth() << "x" << this->gameMenu.getBoardHeight() << std::endl;
|
||||
|
||||
std::cout << "Choose the width of the board (from 1 to " << MAXIMUM_BOARD_WIDTH << ")." << std::endl;
|
||||
std::cout << "Choice: ";
|
||||
std::getline(std::cin, answer);
|
||||
try {
|
||||
int selectedSize = std::stoi(answer);
|
||||
if (selectedSize >= 1 && selectedSize <= MAXIMUM_BOARD_WIDTH) {
|
||||
this->gameMenu.setBoardWidth(selectedSize);
|
||||
}
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
|
||||
std::cout << "Choose the height of the board (from 1 to " << MAXIMYM_BOARD_HEIGHT << ")." << std::endl;
|
||||
std::cout << "Choice: ";
|
||||
std::getline(std::cin, answer);
|
||||
try {
|
||||
int selectedSize = std::stoi(answer);
|
||||
if (selectedSize >= 1 && selectedSize <= MAXIMYM_BOARD_HEIGHT) {
|
||||
this->gameMenu.setBoardHeight(selectedSize);
|
||||
}
|
||||
}
|
||||
catch (std::exception ignored) {}
|
||||
|
||||
std::cout << "New board width and height: " << this->gameMenu.getBoardWidth() << "x" << this->gameMenu.getBoardHeight();
|
||||
|
||||
std::string waiting;
|
||||
std::getline(std::cin, waiting);
|
||||
}
|
||||
|
||||
void TextApp::seeKeybinds() const {
|
||||
std::cout << "\n\n\n";
|
||||
std::cout << "Quit/Pause/Retry: quit/pause/retry" << std::endl;
|
||||
std::cout << "Hold : h" << std::endl;
|
||||
std::cout << "Soft/Hard drop : sd/hd" << std::endl;
|
||||
std::cout << "Move left/right : l/r" << std::endl;
|
||||
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);
|
||||
}
|
||||
|
||||
void TextApp::defaultKeybinds() {
|
||||
this->keybinds.clear();
|
||||
this->keybinds.insert({"quit", QUIT});
|
||||
this->keybinds.insert({"pause", PAUSE});
|
||||
this->keybinds.insert({"retry", RETRY});
|
||||
this->keybinds.insert({"h", HOLD});
|
||||
this->keybinds.insert({"sd", SOFT_DROP});
|
||||
this->keybinds.insert({"hd", HARD_DROP});
|
||||
this->keybinds.insert({"l", MOVE_LEFT});
|
||||
this->keybinds.insert({"r", MOVE_RIGHT});
|
||||
this->keybinds.insert({"c", ROTATE_0});
|
||||
this->keybinds.insert({"cw", ROTATE_CW});
|
||||
this->keybinds.insert({"cc", ROTATE_180});
|
||||
this->keybinds.insert({"ccw", ROTATE_CCW});
|
||||
}
|
||||
|
||||
void TextApp::startGame() const {
|
||||
Game game = this->gameMenu.startGame(DEFAULT_GAMEMODE);
|
||||
game.start();
|
||||
|
||||
std::cout << "\n\n\n";
|
||||
this->printGame(game);
|
||||
|
||||
bool quit = false;
|
||||
bool paused = false;
|
||||
std::string answer;
|
||||
while (!quit) {
|
||||
std::cout << "Actions: ";
|
||||
std::getline(std::cin, answer);
|
||||
std::stringstream answerStream(answer);
|
||||
std::vector<std::string> actions;
|
||||
for (std::string action; std::getline(answerStream, action, ' ');) {
|
||||
actions.push_back(action);
|
||||
}
|
||||
|
||||
std::set<Action> playerActions;
|
||||
for (std::string action : actions) {
|
||||
if (this->keybinds.contains(action)) {
|
||||
playerActions.insert(this->keybinds.at(action));
|
||||
}
|
||||
}
|
||||
|
||||
if (playerActions.contains(PAUSE)) {
|
||||
paused = (!paused);
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
if (playerActions.contains(QUIT)) {
|
||||
quit = true;
|
||||
}
|
||||
else if (playerActions.contains(RETRY)) {
|
||||
game.reset();
|
||||
game.start();
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < FRAMES_PER_INPUT; i++) {
|
||||
game.nextFrame(playerActions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n\n\n";
|
||||
if (paused) {
|
||||
std::cout << "--<[PAUSED]>--" << std::endl;
|
||||
}
|
||||
this->printGame(game);
|
||||
|
||||
if (game.hasLost()) {
|
||||
quit = true;
|
||||
std::cout << "You lost!" << std::endl;
|
||||
}
|
||||
else if (game.hasWon()) {
|
||||
quit = true;
|
||||
std::cout << "You won!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextApp::printGame(const Game& game) const {
|
||||
int maxHeight = game.getBoard().getGridHeight();
|
||||
if (game.getActivePiece() != nullptr) {
|
||||
for (const Position& position : game.getActivePiece()->getPositions()) {
|
||||
maxHeight = std::max(maxHeight, position.y + game.getActivePiecePosition().y);
|
||||
}
|
||||
}
|
||||
|
||||
bool lineCountPrinted = false;
|
||||
bool holdBoxStartedPrinting = false;
|
||||
bool holdBoxFinishedPrinting = false;
|
||||
bool nextQueueStartedPrinting = false;
|
||||
bool nextQueueFinishedPrinting = false;
|
||||
int nextQueuePrintedPiece;
|
||||
int printedPieceLineHeight;
|
||||
|
||||
for (int y = maxHeight; y >= 0; y--) {
|
||||
for (int x = 0; x < game.getBoard().getWidth(); x++) {
|
||||
bool isActivePieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.getActivePiecePosition()));
|
||||
bool isGhostPieceHere = (game.getActivePiece() != nullptr) && (game.getActivePiece()->getPositions().contains(Position{x, y} - game.ghostPiecePosition()));
|
||||
Block block = (isActivePieceHere || isGhostPieceHere) ? game.getActivePiece()->getBlockType() : game.getBoard().getBlock(Position{x, y});
|
||||
|
||||
if (isActivePieceHere || isGhostPieceHere) {
|
||||
std::cout << getConsoleColorCode(block);
|
||||
}
|
||||
else {
|
||||
std::cout << getResetConsoleColorCode();
|
||||
}
|
||||
|
||||
if (block != NOTHING && (!(isGhostPieceHere && !isActivePieceHere))) {
|
||||
std::cout << "*";
|
||||
}
|
||||
else {
|
||||
if (y < game.getBoard().getBaseHeight()) {
|
||||
if (isGhostPieceHere) {
|
||||
std::cout << "=";
|
||||
}
|
||||
else {
|
||||
std::cout << "-";
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cout << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (y < game.getBoard().getGridHeight()) {
|
||||
std::cout << " ";
|
||||
if (!lineCountPrinted) {
|
||||
std::cout << getResetConsoleColorCode() << "Lines: " << game.getClearedLines();
|
||||
lineCountPrinted = true;
|
||||
}
|
||||
else if (!holdBoxFinishedPrinting) {
|
||||
if (!holdBoxStartedPrinting) {
|
||||
std::cout << getResetConsoleColorCode() << "Hold:";
|
||||
printedPieceLineHeight = (game.getHeldPiece() == nullptr) ? -1 : (game.getHeldPiece()->getLength() - 1);
|
||||
holdBoxStartedPrinting = true;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < game.getHeldPiece()->getLength(); i++) {
|
||||
if (game.getHeldPiece()->getPositions().contains(Position{i, printedPieceLineHeight})) {
|
||||
std::cout << getConsoleColorCode(game.getHeldPiece()->getBlockType()) << "*";
|
||||
}
|
||||
else {
|
||||
std::cout << getResetConsoleColorCode() << "-";
|
||||
}
|
||||
}
|
||||
printedPieceLineHeight--;
|
||||
}
|
||||
if (printedPieceLineHeight < 0) {
|
||||
holdBoxFinishedPrinting = true;
|
||||
}
|
||||
}
|
||||
else if (!nextQueueFinishedPrinting) {
|
||||
if (!nextQueueStartedPrinting) {
|
||||
std::cout << getResetConsoleColorCode() << "Next:";
|
||||
printedPieceLineHeight = (game.getNextPieces().size() == 0) ? -1 : (game.getNextPieces().at(0).getLength() - 1);
|
||||
nextQueuePrintedPiece = 0;
|
||||
nextQueueStartedPrinting = true;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < game.getNextPieces().at(nextQueuePrintedPiece).getLength(); i++) {
|
||||
if (game.getNextPieces().at(nextQueuePrintedPiece).getPositions().contains(Position{i, printedPieceLineHeight})) {
|
||||
std::cout << getConsoleColorCode(game.getNextPieces().at(nextQueuePrintedPiece).getBlockType()) << "*";
|
||||
}
|
||||
else {
|
||||
std::cout << getResetConsoleColorCode() << "-";
|
||||
}
|
||||
}
|
||||
printedPieceLineHeight--;
|
||||
}
|
||||
if (printedPieceLineHeight < 0) {
|
||||
nextQueuePrintedPiece++;
|
||||
if (nextQueuePrintedPiece >= game.getNextPieces().size()) {
|
||||
nextQueueFinishedPrinting = true;
|
||||
}
|
||||
else {
|
||||
printedPieceLineHeight = game.getNextPieces().at(nextQueuePrintedPiece).getLength() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
std::cout << getResetConsoleColorCode();
|
||||
}
|
||||
58
src/TextUI/TextApp.h
Normal file
58
src/TextUI/TextApp.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Core/Menu.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
/**
|
||||
* Textual interface for the app
|
||||
*/
|
||||
class TextApp {
|
||||
private:
|
||||
Menu gameMenu; // the interface with the core of the app
|
||||
std::map<std::string, Action> keybinds; // what the player needs to type to perform in-game actions
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initializes the app with default settings
|
||||
*/
|
||||
TextApp();
|
||||
|
||||
/**
|
||||
* Runs the app
|
||||
*/
|
||||
void run();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Sub-menu to select which pieces to play with
|
||||
*/
|
||||
void choosePieces();
|
||||
|
||||
/**
|
||||
* Sub-menu to change the size of the board
|
||||
*/
|
||||
void chooseBoardSize();
|
||||
|
||||
/**
|
||||
* Sub-menu to see the in-game controls
|
||||
*/
|
||||
void seeKeybinds() const;
|
||||
|
||||
/**
|
||||
* Sets the controls to their default values
|
||||
*/
|
||||
void defaultKeybinds();
|
||||
|
||||
/**
|
||||
* Starts a new game with the current settings
|
||||
*/
|
||||
void startGame() const;
|
||||
|
||||
/**
|
||||
* Prints the current state of a game to the console
|
||||
*/
|
||||
void printGame(const Game& game) const;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "../Core/Menu.h"
|
||||
#include "../Pieces/Generator.h"
|
||||
#include "../Pieces/PiecesFiles.h"
|
||||
#include "TextApp.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -18,13 +18,8 @@ void readStatsFromFilesForAllSizes(int amount);
|
||||
int main(int argc, char** argv) {
|
||||
std::srand(std::time(NULL));
|
||||
|
||||
Menu menu;
|
||||
menu.getPiecesList().loadPieces(4);
|
||||
menu.getPiecesList().selectAllPieces(4);
|
||||
Game game = menu.startGame(SPRINT);
|
||||
game.start();
|
||||
|
||||
loadFromFilesForOneSize(13);
|
||||
TextApp UI;
|
||||
UI.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user