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