package at.tetris4j.model.components;
import java.util.Arrays;
import at.tetris4j.view.utils.TetrisKey;
/**
* Class representing a simple Tetris-GameBoard.
*
* @author Manuel Zametter
* @author Florian Genser
*
*/
public class GameBoard {
private static final int DEFAULT_WIDTH = 20;
private boolean isGameOver = false;
private int width;
private int height;
private Block currentBlock;
private BoardPresentation boardPresentation;
private int[][] gameBoard;
public GameBoard(int theHeight) {
this.height = theHeight;
this.width = DEFAULT_WIDTH;
currentBlock = Block.getRandomBlock(width);
gameBoard = new int[height][width];
updateBoardPresentation();
}
private boolean canMoveLeft() {
final int currentY = currentBlock.getY();
final int currentX = currentBlock.getX();
final int[][] block = currentBlock.getBlock();
final int blockHeight = block.length;
final int blockWidth = block[0].length;
if (currentX - 1 < 0) {
return false;
}
for (int j = 0; j < blockWidth; j++) {
for (int i = 0; i < blockHeight; i++) {
if (block[i][j] != 0 && gameBoard[i + currentY][j + currentX - 1] != 0) {
return false;
}
}
}
return true;
}
private boolean canMoveRight() {
final int currentY = currentBlock.getY();
final int currentX = currentBlock.getX();
final int[][] block = currentBlock.getBlock();
final int blockHeight = block.length;
final int blockWidth = block[0].length;
if (currentX + blockWidth >= width) {
return false;
}
for (int j = blockWidth - 1; j >= 0; j--) {
for (int i = 0; i < blockHeight; i++) {
if (block[i][j] != 0 && gameBoard[i + currentY][j + currentX + 1] != 0) {
return false;
}
}
}
return true;
}
public void updateGameBoard() {
moveCurrentBlock(TetrisKey.DOWN);
}
/**
* Moves the current block in a specified direction, if it is possible.
*
* @param direction
* A TetrisKey specifying the direction, in which the current
* block should be moved. Possible Inputs: LEFT;RIGHT;DOWN;
*/
private void moveCurrentBlock(TetrisKey direction) {
switch (direction) {
case DOWN:
currentBlock.moveDown();
break;
case LEFT:
if (canMoveLeft()) {
currentBlock.moveLeft();
}
break;
case RIGHT:
if (canMoveRight()) {
currentBlock.moveRight();
}
break;
default:
break;
}
updateBoardPresentation();
}
/**
* Turn the current block, if it is possible.
*/
public void turnCurrentBlock() {
if (canCurrentBlockTurn()) {
currentBlock.turn();
updateBoardPresentation();
}
}
/**
* Checks whether the current block can turn
*
* @return True if the current block can turn, otherwise false.
*/
private boolean canCurrentBlockTurn() {
int[][] block = currentBlock.getBlock();
int currentX = currentBlock.getX();
int currentY = currentBlock.getY();
int blockWidth = currentBlock.getWidth();
int blockHeight = currentBlock.getHeight();
if (currentX + blockHeight <= width) {
for (int i = currentY, l = 0; i < blockWidth + currentY; i++, l++) {
for (int j = currentX, n = blockHeight - 1; j < blockHeight + currentX; j++, n--) {
if (block[n][l] != 0 && gameBoard[i][j] != 0) {
return false;
}
}
}
return true;
}
return false;
}
private void updateGame() {
if (checkForNewBlock()) {
retireCurrentBlock();
removeFilledRows();
checkForGameOver();
}
}
private boolean checkForNewBlock() {
int[][] myBlock = currentBlock.getBlock();
int currentX = currentBlock.getX();
int currentY = currentBlock.getY();
int blockHeight = myBlock.length;
int blockWidth = myBlock[0].length;
// checks if block is at bottom
if (currentY + blockHeight >= height) {
return true;
}
for (int i = blockHeight - 1; i >= 0; i--) {
for (int j = 0; j < blockWidth; j++) {
if (myBlock[i][j] != 0 && gameBoard[i + currentY + 1][j + currentX] != 0) {
return true;
}
}
}
return false;
}
private void retireCurrentBlock() {
insertBlockIntoBoard(currentBlock, gameBoard);
currentBlock = Block.getRandomBlock(width);
}
/**
* Remove filled Rows from the GameBoard.
*/
private void removeFilledRows() {
for (int i = 0; i < height; i++) {
boolean rowFinished = true;
for (int j = 0; j < width; j++) {
if (gameBoard[i][j] == 0) {
rowFinished = false;
break;
}
}
if (rowFinished) {
removeRow(i);
}
}
}
private void removeRow(int rowIndex) {
int[][] newBoard = new int[height][width];
Arrays.fill(newBoard[0], 0);
for (int i = 1; i <= rowIndex; i++) {
newBoard[i] = gameBoard[i - 1];
}
for (int i = rowIndex + 1; i < height; i++) {
newBoard[i] = gameBoard[i];
}
gameBoard = newBoard;
}
private void checkForGameOver() {
for (int i = 0; i < gameBoard[0].length; i++) {
if (gameBoard[0][i] != 0) {
isGameOver = true;
}
}
}
private void updateBoardPresentation() {
updateGame();
int[][] workingCopyOfBoard = createCopyOfBoard(gameBoard);
insertBlockIntoBoard(currentBlock, workingCopyOfBoard);
this.boardPresentation = new BoardPresentation(workingCopyOfBoard);
}
private int[][] createCopyOfBoard(int[][] board) {
int[][] copyBoard = new int[height][width];
for (int i = 0; i < height; i++) {
System.arraycopy(board[i], 0, copyBoard[i], 0, width);
}
return copyBoard;
}
private void insertBlockIntoBoard(Block block, int[][] board) {
final int[][] blockMatrix = block.getBlock();
final int xPosition = block.getX();
final int yPosition = block.getY();
for (int i = 0; i < blockMatrix.length; i++) {
for (int j = 0; j < blockMatrix[0].length; j++) {
int value = blockMatrix[i][j];
if (value != 0) {
board[yPosition + i][xPosition + j] = value;
}
}
}
}
/**
* Get a BoardPresentation to visualize the current GameState.
*
* @return The current BoardPresentation
*/
public BoardPresentation getBoardPresentation() {
return this.boardPresentation;
}
public void moveLeft() {
moveCurrentBlock(TetrisKey.LEFT);
}
public void moveRight() {
moveCurrentBlock(TetrisKey.RIGHT);
}
public void moveDown() {
moveCurrentBlock(TetrisKey.DOWN);
}
public boolean isGameOver() {
return isGameOver;
}
}