package generator;
import java.util.Random;
import engine.Board;
import engine.Scheme;
/**
* Class is used to generate blocks on the board.
*/
public final class BlocksGenerator {
private static final int NUM_COMBINATIONS = 5;
private static final int COMBINATION_2H = 0;
private static final int COMBINATION_2V = 1;
private static final int COMBINATION_3LV = 2;
private static final int COMBINATION_3RV = 3;
private static final int COMBINATION_3H = 4;
private static Random rand = new Random();
/**
* Generate a one configuration of new blocks and moves, which execution
* lead to the given board. The configuration of blocks is random.
*
* @param destinationBoard
* @return the scheme with generated blocks
*/
public static Scheme generateBlocks(Board destinationBoard) {
Scheme s = new Scheme(new Board(destinationBoard));
int mix = rand.nextInt(NUM_COMBINATIONS);
mix = COMBINATION_2H;
generateBlock(mix, s);
return s;
}
/*
* Generates a one configuration of new blocks and moves, which execution
* leads to the given board. The configuration of the blocks is given.
*
* @param mix - the configuration of the new blocks
* @param s - the given scheme
*/
private static void generateBlock(int mix, Scheme s) {
int width = s.start.width, height = s.start.height;
int[][] goodPositions = new int[width][height];
int total = 0;
for (int i=0; i<width; i++)
for (int j=0; j<height; j++)
if ((goodPositions[i][j] = goodPosition(mix,i,j, s)) >= 0) total++;
if (total > 0) {
int n = rand.nextInt(total);
for (int i=0; i<width; i++)
for (int j=0; j<height; j++)
if (goodPositions[i][j] >= 0) {
if (n==0) { addMix(mix,i,j,goodPositions[i][j],s); i = width; break; }
n--;
}
}
}
/*
* Adds a new blocks of the given configuration and color in fixed fields.
*
* @param mix - the configuration of the blocks
* @param x - coordinate of the field
* @param y - coordinate of the field
* @param color - the color of the blocks
* @param s - scheme, it is returned with new blocks
*/
private static void addMix(int mix, int x, int y, int color, Scheme s) {
Board board = s.start;
switch (mix) {
case COMBINATION_2H:
insertBlock(x, y, color, board);
insertBlock(x+1, y, color, board);
MixGenerator.moveBlock(x,y,true,s);
MixGenerator.moveBlock(x+1,y,true,s);
break;
case COMBINATION_2V:
insertBlock(x, y, color, board);
insertBlock(x, y-1, color, board);
MixGenerator.moveBlock(x, y-1, true, s);
break;
case COMBINATION_3LV:
insertBlock(x, y, color, board);
insertBlock(x, y-1, color, board);
insertBlock(x-1, y-1, color, board);
MixGenerator.moveBlock(x, y-1, true, s);
break;
case COMBINATION_3RV:
insertBlock(x, y, color, board);
insertBlock(x, y-1, color, board);
insertBlock(x+1, y-1, color, board);
MixGenerator.moveBlock(x, y-1, true, s);
break;
case COMBINATION_3H:
insertBlock(x, y, color, board);
insertBlock(x+1, y, color, board);
insertBlock(x+2, y, color, board);
MixGenerator.moveBlock(x+1, y, true, s);
break;
default:
assert false;
break;
}
}
/*
* Checks if the blocks of given configuration can be inserted in the fixed
* fields and return possible color of the blocks.
*
* @param mix - the configuration of blocks
* @param x - coordinate of the field
* @param y - coordinate of the field
* @param s - scheme
* @return the color of blocks which can be add in the fixed fields or -1 if
* the blocks can't be inserted.
*/
private static int goodPosition(int mix, int x, int y, Scheme s) {
Board board = s.start; int color=-1;
switch (mix) {
case COMBINATION_2H: {
if (board.getType(x, y) == Board.CONCRETE) color = board.getColor(x, y);
if (board.getType(x+1, y) == Board.CONCRETE)
if (color==-1 || board.getColor(x+1, y)==color) color = board.getColor(x+1, y);
else return -1;
if (color==-1) color = rand.nextInt(Board.NUM_COLORS);
boolean canMove = false;
boolean block1 = canInsertBlock(x,y,color, true, board, new int[]{0,0,0,0});
if (block1) insertBlock(x, y, color, board);
boolean block2 = canInsertBlock(x+1,y,color, true, board, new int[]{0,0,0,1});
if (block2) insertBlock(x+1, y, color, board);
if (block1 && block2)
canMove = MixGenerator.canMoveBlock(x, y, true, s) ||
MixGenerator.canMoveBlock(x+1, y, true, s);
if (block2) deleteBlock(x+1,y, board);
if (block1) deleteBlock(x,y, board);
if (canMove) return color;
else return -1;
}
case COMBINATION_2V: {
if (board.getType(x, y) == Board.CONCRETE) color = board.getColor(x, y);
if (board.getType(x, y-1) == Board.CONCRETE)
if (color==-1 || board.getColor(x, y-1)==color) color = board.getColor(x, y-1);
else return -1;
if (color==-1) color = rand.nextInt(Board.NUM_COLORS);
boolean canMove = false;
boolean block1 = canInsertBlock(x,y,color, false, board, new int[]{0,0,0,0});
if (block1) insertBlock(x, y, color, board);
boolean block2 = canInsertBlock(x,y-1,color, false, board, new int[]{0,0,1,0});
if (block2) insertBlock(x, y-1, color, board);
if (block1 && block2)
canMove = MixGenerator.canMoveBlock(x, y-1, true, s);
if (block2) deleteBlock(x,y-1, board);
if (block1) deleteBlock(x,y, board);
if (canMove) return color;
else return -1;
}
case COMBINATION_3LV: {
if (board.getType(x, y) == Board.CONCRETE) color = board.getColor(x, y);
if (board.getType(x, y-1) == Board.CONCRETE)
if (color==-1 || board.getColor(x, y-1)==color) color = board.getColor(x, y-1);
else return -1;
if (board.getType(x-1, y-1) == Board.CONCRETE)
if (color==-1 || board.getColor(x-1, y-1)==color) color = board.getColor(x-1, y-1);
else return -1;
if (color==-1) color = rand.nextInt(Board.NUM_COLORS);
boolean canMove = false;
boolean block1 = canInsertBlock(x,y,color, false, board, new int[]{0,0,0,0});
if (block1) insertBlock(x, y, color, board);
boolean block2 = canInsertBlock(x,y-1,color, false, board, new int[]{0,0,1,0});
if (block2) insertBlock(x, y-1, color, board);
boolean block3 = canInsertBlock(x-1,y-1,color, true, board, new int[]{0,1,0,0});
if (block3) insertBlock(x-1, y-1, color, board);
if (block1 && block2 && block3)
canMove = MixGenerator.canMoveBlock(x, y-1, true, s);
if (block3) deleteBlock(x-1,y-1, board);
if (block2) deleteBlock(x,y-1, board);
if (block1) deleteBlock(x,y, board);
if (canMove) return color;
return -1;
}
case COMBINATION_3RV: {
if (board.getType(x, y) == Board.CONCRETE) color = board.getColor(x, y);
if (board.getType(x, y-1) == Board.CONCRETE)
if (color==-1 || board.getColor(x, y-1)==color) color = board.getColor(x, y-1);
else return -1;
if (board.getType(x+1, y-1) == Board.CONCRETE)
if (color==-1 || board.getColor(x+1, y-1)==color) color = board.getColor(x+1, y-1);
else return -1;
if (color==-1) color = rand.nextInt(Board.NUM_COLORS);
boolean canMove = false;
boolean block1 = canInsertBlock(x,y,color, false, board, new int[]{0,0,0,0});
if (block1) insertBlock(x, y, color, board);
boolean block2 = canInsertBlock(x,y-1,color, false, board, new int[]{0,0,1,0});
if (block2) insertBlock(x, y-1, color, board);
boolean block3 = canInsertBlock(x+1,y-1,color, true, board, new int[]{0,0,0,1});
if (block3) insertBlock(x+1, y-1, color, board);
if (block1 && block2 && block3)
canMove = MixGenerator.canMoveBlock(x, y-1, true, s);
if (block3) deleteBlock(x+1,y-1, board);
if (block2) deleteBlock(x,y-1, board);
if (block1) deleteBlock(x,y, board);
if (canMove) return color;
else return -1;
}
case COMBINATION_3H: {
if (board.getType(x, y) == Board.CONCRETE) color = board.getColor(x, y);
if (board.getType(x+1, y) == Board.CONCRETE)
if (color==-1 || board.getColor(x+1, y)==color) color = board.getColor(x+1, y);
else return -1;
if (board.getType(x+2, y) == Board.CONCRETE)
if (color==-1 || board.getColor(x+2, y)==color) color = board.getColor(x+2, y);
else return -1;
if (color==-1) color = rand.nextInt(Board.NUM_COLORS);
boolean canMove = false;
boolean block1 = canInsertBlock(x,y,color, true, board, new int[]{0,0,0,0});
if (block1) insertBlock(x, y, color, board);
boolean block2 = canInsertBlock(x+1,y,color, false, board, new int[]{0,0,0,1});
if (block2) insertBlock(x+1, y, color, board);
boolean block3 = canInsertBlock(x+2,y,color, true, board, new int[]{0,0,0,1});
if (block3) insertBlock(x+2, y, color, board);
if (block1 && block2 && block3)
canMove = MixGenerator.canMoveBlock(x+1, y, true, s);
if (block3) deleteBlock(x+2,y, board);
if (block2) deleteBlock(x+1,y, board);
if (block1) deleteBlock(x,y, board);
if (canMove) return color;
else return -1;
}
}
assert false;
return -1;
}
/*
* Removes block from given field.
*
* @param x - coordinate of the field
* @param y - coordinate of the field
* @param board
*/
private static void deleteBlock(int x, int y, Board board) {
int color = board.getColor(x, y);
if (board.getType(x, y) == Board.BLOCK_CONCRETE)
board.setElement(x, y, Board.CONCRETE, color);
else {
board.setElement(x, y, Board.EMPTY);
int h = y;
while (board.isBlock(x,h-1)) {
if (board.getType(x, h-1) == Board.BLOCK_BALLOON) {
int t = h-1;
while (board.getType(x, t-1) == Board.BLOCK_BALLOON) t--;
if (!board.isBlock(x, t-1)) break;
}
board.setElement(x, h, board.getType(x, h-1), board.getColor(x, h-1));
h--;
}
board.setElement(x, h, Board.EMPTY);
}
}
/*
* Inserts a block in the given field.
*
* @param x - coordinate of the field
* @param y - coordinate of the field
* @param color - color of the block
* @param board
*/
private static void insertBlock(int x, int y, int color, Board board) {
if (board.isEmpty(x, y)) board.setElement(x, y, Board.BLOCK_SPHERE, (byte) color);
else if (board.getType(x, y) == Board.CONCRETE)
board.setElement(x, y, Board.BLOCK_CONCRETE, (byte) color);
else {
int h = y;
while (h>0 && board.isBlock(x, h-1)) h--;
while (h <= y) {
board.setElement(x, h-1, board.getType(x, h),board.getColor(x, h));
h++;
}
board.setElement(x, y, Board.BLOCK_SPHERE, (byte) color);
}
}
/*
* Checks if a block can be inserted in the given field.
*
* @param x - coordinate of the field
* @param y - coordinate of the field
* @param color - color of the block
* @param canBeBlock - true if block from the field can be raised
* @param board
* @param tab - define where blocks of the same color can be
* @return true if the block can be inserted
*/
private static boolean canInsertBlock(int x, int y, int color, boolean canBeBlock,
Board board, int[] tab) {
if (!(board.isEmpty(x, y) || board.isBlock(x, y) && canBeBlock
|| board.getType(x, y) == Board.CONCRETE)) return false;
if (board.getType(x, y) == Board.CONCRETE && color != board.getColor(x, y) ||
board.isBlock(x, y) && board.getColor(x, y) == color)
return false;
if (tab[2] == 0 && board.isBlock(x, y+1) && board.getColor(x, y+1) == color ||
!(board.isSolid(x, y+1) || board.isBlock(x, y+1) ||
board.getType(x, y+1) == Board.CONCRETE)) return false;
if (tab[3] == 0 && board.isBlock(x-1, y) && board.getColor(x-1, y) == color ||
tab[1] == 0 && board.isBlock(x+1, y) && board.getColor(x+1, y) == color ||
tab[0] == 0 && board.isBlock(x, y-1) && board.getColor(x, y-1) == color)
return false;
if (board.isBlock(x, y)) {
int h = y;
while (h>0 && board.isBlock(x, h-1)) h--;
if (!board.isEmpty(x, h-1)) return false;
while (h <= y && MixGenerator.correctMove(x, h-1, x, h, -1, -1, board, true))
h++;
if (h != y+1) return false;
}
return true;
}
}