#include "block.h" #include "board.h" #include "cell.h" #include "shapes.h" #include "scoreboard.h" #include "subscriptions.h" #include <algorithm> #include <iostream> using namespace std; int Block::NUM_SHAPES = 4; Block::Block(int level, Board *b, Cell *c, char type, int version) : activeCells{0}, level{level}, version{version}, pivotRow{c->getRow()}, pivotCol{c->getCol()}, boardWidth{b->getWidth()}, boardHeight{b->getHeight()}, dropped{false}, type{type}, theShape{make_unique<Shapes>(type)}, theBoard{b} { initBlock(); } Block::~Block() {} void Block::initBlock() { if (validate(version, pivotRow, pivotCol)) { applyToShape(&Block::draw); } else { throw InvalidPlacement{}; } } bool Block::isDropped() const noexcept { return dropped; } int Block::getPivotRow() const noexcept { return pivotRow; } int Block::getPivotCol() const noexcept { return pivotCol; } int Block::getVersion() const noexcept { return version; } char Block::getType() const noexcept { return type; } int Block::getLevel() const noexcept { return level; } int Block::getBoardId() const noexcept { return theBoard->getBoardId(); } int Block::getHeight() const noexcept { return theShape->getHeight(version); } int Block::getActiveCells() const noexcept { return activeCells; } void Block::eraseOnBoard() { applyToShape(&Block::erase); } bool Block::withinBoard(int row, int col) { return (row >= 0 && row < boardHeight && col >= 0 && col < boardWidth); } bool Block::filled(int row, int col) { return theBoard->getType(row, col) != ' '; } bool Block::isItself(int row, int col) { return theBoard->getBlock(row, col) == this; } bool Block::validate(int version, int currPivotRow, int currPivotCol) { for (auto pos : theShape->getLayout(version)) { if (!(withinBoard(pos.first + currPivotRow, pos.second + currPivotCol)) || (filled(pos.first + currPivotRow, pos.second + currPivotCol) && !isItself(pos.first + currPivotRow, pos.second + currPivotCol))) return false; } return true; } void Block::draw(int row, int col) { theBoard->drawBlock(row, col, this); ++activeCells; } void Block::erase(int row, int col) { theBoard->eraseBlock(row, col); --activeCells; } // erases the old block position/state on the board, // modifies the PivotRow and PivotCol of block // draw the block in the new position/state on the board. void Block::changeOnBoard(int newPivotRow, int newPivotCol, int newVersion) { applyToShape(&Block::erase); pivotCol = newPivotCol; pivotRow = newPivotRow; version = newVersion; applyToShape(&Block::draw); } void Block::move(char direction, int step, int weight) { if (dropped) return; weight += level >= 3 ? 1 : 0; int newPivotCol = pivotCol; int newPivotRow = pivotRow; for (int i = 0; i < step; ++i) { int incPivotRow = 0; int incPivotCol = 0; if (direction == 'l') { incPivotCol = -1; } else if (direction == 'r') { incPivotCol = 1; } else { incPivotRow = 1; } if (validate(version, newPivotRow + incPivotRow, newPivotCol + incPivotCol)) { newPivotCol += incPivotCol; newPivotRow += incPivotRow; } } int downwardMove = weightDown(newPivotRow, newPivotCol, version, weight); if (downwardMove == weight) { newPivotRow += weight; changeOnBoard(newPivotRow, newPivotCol, version); } else { drop(newPivotRow, newPivotCol, version); } } void Block::rotate(bool isClockwise, int amount, int weight) { if (dropped) return; weight += level >= 3 ? 1 : 0; int newVersion = version; for (int i = 0; i < amount; ++i) { int desiredShape = (isClockwise ? newVersion + 1 : newVersion + 3) % Block::NUM_SHAPES; if (validate(desiredShape, pivotRow, pivotCol)) { newVersion = desiredShape; } else { break; } } int downwardMove = weightDown(pivotRow, pivotCol, newVersion, weight); if (downwardMove == weight) { changeOnBoard(pivotRow + downwardMove, pivotCol, newVersion); } else { drop(pivotRow, pivotCol, newVersion); } } void Block::drop(int currPivotRow, int currPivotCol, int currVersion) { if (dropped) return; while (validate(currVersion, currPivotRow + 1, currPivotCol)) { ++currPivotRow; } changeOnBoard(currPivotRow, currPivotCol, currVersion); dropped = true; } int Block::weightDown(int currPivotRow, int currPivotCol, int currVersion, int weight) { int step = 0; for (int i = 0; i < weight; ++i) { if (validate(currVersion, currPivotRow + step + 1, currPivotCol)) { ++step; } else { break; } } return step; } void Block::applyToShape(void (Block::*func)(int, int)) { for (auto pos : theShape->getLayout(version)) { (this->*func)(pos.first + pivotRow, pos.second + pivotCol); } } void Block::decreaseActiveCell() { --activeCells;}