biquadris / baseboard.cc
baseboard.cc
Raw
#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);
 }