#include "baseboard.h" #include "cell.h" #include "block.h" #include "level.h" #include "scoreboard.h" #include <algorithm> #include <vector> #include "subscriptions.h" #include "model.h" #include <sstream> BaseBoard::BaseBoard(int width, int height, int seed, int level, int boardId, Scoreboard *sb, BiquadrisModel *m, std::string seq): width{width}, height{height}, seed{seed}, level{level}, boardId{boardId}, movesWithoutClearing{0}, lastNumLinesCleared{0}, sb{sb}, m{m}, currentLevel{std::make_unique<Level>(level, seed, seq)} { init(); } void BaseBoard::init() { for (int r = 0; r < height; ++r) { std::vector<std::shared_ptr<Cell>> row; for (int c = 0; c < width; ++c) { row.push_back(std::make_shared<Cell>(this, r, c)); row[c]->attach(m); } theGrid.push_back(row); } generateNextBlock(); placeCurrBlock(); } BaseBoard::~BaseBoard() {} std::shared_ptr<Board> BaseBoard::getComponent() { return nullptr; } void BaseBoard::restart() { for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { theGrid[j][i]->removeBlock(); } } placeCurrBlock(); notifyObservers(SubscriptionType::BoardModified); } void BaseBoard::generateNextBlock() { char type = currentLevel->nextBlock(); nextBlock.first = type; nextBlock.second = level; notifyObservers(SubscriptionType::ModelInfoChange); } void BaseBoard::deleteLine(int line) { // removes the Block from Cell for (auto cell = theGrid[line].begin(); cell != theGrid[line].end(); ++cell) { (*cell)->getBlock()->decreaseActiveCell(); (*cell)->removeBlock(); } // drops the upper cells down when line(s) are cleared for (int i = line; i > 0; i--) { std::iter_swap(theGrid.begin() + i, theGrid.begin() + i - 1); for (auto cell = theGrid[i].begin(); cell != theGrid[i].end(); ++cell) { (*cell)->setCoords(i, (*cell)->getCol()); } } for (auto cell = theGrid[0].begin(); cell != theGrid[0].end(); ++cell) { (*cell)->setCoords(0, (*cell)->getCol()); } } int BaseBoard::clearLine() { int pivotRow = currentBlock->getPivotRow(); int blockHeight = currentBlock->getHeight(); std::vector<int> rows; int rowCounter = 0; for (int i = pivotRow - blockHeight; i <= pivotRow; i++) { bool flag = true; for (auto cell = theGrid[i].begin(); cell != theGrid[i].end(); ++cell) { if ((*cell)->getType() == ' ') { flag = false; break; } } if (flag) { rows.push_back(i); ++rowCounter; } } // Trigger special action if (rowCounter > 1) notifyObservers(SubscriptionType::SpecialAction); if (rowCounter != 0) { for (int row : rows) { deleteLine(row); } notifyObservers(SubscriptionType::BoardModified); } lastNumLinesCleared = rowCounter; if (rowCounter > 0) { notifyObservers(SubscriptionType::BoardLinesCleared); movesWithoutClearing = 0; blocksCleanup(); } else ++movesWithoutClearing; return rowCounter; } void BaseBoard::move(char direction, int n, int weight) { currentBlock->move(direction, n, weight); if (currentBlock->isDropped()) { drop(); notifyObservers(SubscriptionType::BlockDroppedByItself); } } void BaseBoard::drop() { currentBlock->drop(currentBlock->getPivotRow(), currentBlock->getPivotCol(), currentBlock->getVersion()); blocks.push_back(currentBlock); int linesCleared = clearLine(); // checks for the generation of 1x1 block for level 4 if (level == 4 && movesWithoutClearing != 0 && movesWithoutClearing % 5 == 0) { const int centerCol = width / 2; std::shared_ptr<Block> theBlock = std::make_shared<Block>(level, this, theGrid[0][centerCol].get(), '*'); theBlock->attach(sb); theBlock->drop(theBlock->getPivotRow(), theBlock->getPivotCol(), theBlock->getVersion()); blocks.push_back(theBlock); swap(currentBlock, theBlock); linesCleared = clearLine(); swap(currentBlock, theBlock); if (linesCleared == 0) --movesWithoutClearing; } currentBlock = nullptr; placeCurrBlock(); } void BaseBoard::drawBlock(int row, int col, Block *b) { theGrid[row][col]->fillBlock(b); } void BaseBoard::eraseBlock(int row, int col) { theGrid[row][col]->removeBlock(); } void BaseBoard::placeCurrBlock() { if (currentBlock != nullptr) blocks.push_back(currentBlock); try { currentBlock = std::make_shared<Block>(nextBlock.second, this, theGrid[3][0].get(), nextBlock.first); currentBlock->attach(sb); generateNextBlock(); } catch (InvalidPlacement & e) { restart(); notifyObservers(SubscriptionType::PlayerLose); } } void BaseBoard::replaceCurrBlock(char type, bool safety) { char lastType = currentBlock->getType(); int lastVersion = currentBlock->getVersion(); int lastLevel = currentBlock->getLevel(); int lastPivotRow = currentBlock->getPivotRow(); int lastPivotCol = currentBlock->getPivotCol(); bool valid = false; std::shared_ptr<Block> tempBlock = nullptr; currentBlock->eraseOnBoard(); try { tempBlock = std::make_shared<Block>(level, this, theGrid[lastPivotRow][lastPivotCol].get(), type, 0); valid = true; } catch (InvalidPlacement & e) { if (safety) { currentBlock = std::make_shared<Block>(lastLevel, this, theGrid[lastPivotRow][lastPivotCol].get(), lastType, lastVersion); } else { restart(); notifyObservers(SubscriptionType::PlayerLose); } } if (valid) { currentBlock = tempBlock; } currentBlock->attach(sb); } int BaseBoard::getLevel() const noexcept { return level; } int BaseBoard::getWidth() const noexcept { return width; } int BaseBoard::getHeight() const noexcept { return height; } int BaseBoard::getBoardId() const noexcept { return boardId; } char BaseBoard::getNextBlock() const noexcept { return nextBlock.first; } int BaseBoard::getLastNumLinesCleared() const noexcept { return lastNumLinesCleared; } Block* BaseBoard::getBlock(int row, int col) { return theGrid[row][col]->getBlock(); } void BaseBoard::rotate(bool isClockwise, int n, int weight) { currentBlock->rotate(isClockwise, n, weight); if (currentBlock->isDropped()) { drop(); notifyObservers(SubscriptionType::BlockDroppedByItself); } } void BaseBoard::levelUp() { if (level == currentLevel->MAX_LEVEL) return; ++level; currentLevel->levelUp(); } void BaseBoard::levelDown() { if (level == currentLevel->MIN_LEVEL) return; --level; currentLevel->levelDown(); } void BaseBoard::setSequence(std::string file) { currentLevel->setSequence(file); } void BaseBoard::setRandom(bool r) { currentLevel->setRandom(r); } char BaseBoard::getType(int row, int col) { return theGrid[row][col]->getType(); } std::string BaseBoard::getHint() { return theHint; } void BaseBoard::blocksCleanup() { auto b = blocks.begin(); while (b != blocks.end()) { if ((*b)->getActiveCells() == 0) { if ((*b)->isDropped()) { (*b)->notifyObservers(SubscriptionType::BlockDestroyed); } b = blocks.erase(b); } else { ++b; } } } void BaseBoard::hint() { theHint = ""; for (auto cell : theGrid[height - 1]) { if (cell->getType() == ' ') { std::stringstream ss; ss << "Place block on Column " << cell->getCol() + 1; theHint = ss.str(); break; } } notifyObservers(SubscriptionType::HintChange); }