package generator;
import java.util.Random;
import engine.Board;
import engine.Scheme;
/**
* Class is used to moving the blocks on the board.
*/
public final class MixGenerator {
private static Random rand = new Random();
/**
* Generates a new situation with moves, which execution leads to the given board.
* The blocks can't be raised.
*
* @param pbb - probability of moving a block
* @param originalScheme - the scheme, which contains the board
* @return scheme with the new board and the moves
*/
public static Scheme generateEquivalent(double pbb, Scheme originalScheme) {
Scheme s = new Scheme(new Board(originalScheme.start));
int width = s.start.width, height = s.start.height;
int[] rows = MixGenerator.randomPermutation(height);
for (int y : rows) {
int[] columns = MixGenerator.randomPermutation(width);
boolean[] row = new boolean[width];
for(int x : columns)
if (!row[x] && rand.nextDouble() < pbb)
row[moveBlock(x, y, false, s)] = true;
}
s.optimizeMoves();
return s;
}
/**
* Generates a new situation with moves, which execution leads to the board from
* given scheme. The blocks can be raised.
*
* @param pbb - probability of moving a block
* @param originalScheme - the given scheme
* @return scheme with the new board and the moves
*/
public static Scheme generatePrevious(double pbb, Scheme originalScheme) {
Scheme s = new Scheme(new Board(originalScheme.start));
int width = s.start.width, height = s.start.height;
for (int y = 0; y < height; y++) {
int[] columns = MixGenerator.randomPermutation(width);
boolean[] row = new boolean[width];
for(int x : columns)
if (!row[x] && rand.nextDouble() < pbb)
row[moveBlock(x, y, true, s)] = true;
}
s.optimizeMoves();
return s;
}
/**
* Moves the block into other field.
*
* @param x - coordinate of the block
* @param y - coordinate of the block
* @param canJump -true if block can be raised
* @param s - the given scheme
* @return new field's x of the block
*/
public static int moveBlock(int x, int y, boolean canJump, Scheme s) {
if (!s.start.isBlock(x,y) || s.start.isBlock(x,y-1) ||
s.start.getType(x, y+1) == Board.BLOCK_BALLOON) return x;
int i=0, j=0, color = s.start.getColor(x,y), type = s.start.getType(x, y);
if (type == Board.BLOCK_GLASS) canJump = false;
// columns
while (x-1+i >=0 && s.start.isEmpty(x-1+i,y) &&
(!s.start.isBlock(x-1+i,y+1) || s.start.getColor(x-1+i,y+1) != color) &&
(!s.start.isBlock(x-1+i,y-1) || s.start.getColor(x-1+i,y-1) != color) &&
(!s.start.isBlock(x-1+i-1,y) || s.start.getColor(x-1+i-1,y) != color) &&
(type != Board.BLOCK_BALLOON && (s.start.isBlock(x-1+i,y+1) ||
s.start.isSolid(x-1+i,y+1) || s.start.getType(x-1+i,y+1) == Board.CONCRETE)
|| type == Board.BLOCK_BALLOON && (s.start.isBlock(x-1+i,y-1) ||
s.start.isSolid(x-1+i,y-1) || s.start.getType(x-1+i,y-1) == Board.CONCRETE)))
i--;
while (x+1+j < s.start.width && s.start.isEmpty(x+1+j,y) &&
(!s.start.isBlock(x+1+j,y+1) || s.start.getColor(x+1+j,y+1) != color) &&
(!s.start.isBlock(x+1+j,y-1) || s.start.getColor(x+1+j,y-1) != color) &&
(!s.start.isBlock(x+1+j+1,y) || s.start.getColor(x+1+j+1,y) != color) &&
(type != Board.BLOCK_BALLOON && (s.start.isBlock(x+1+j,y+1) ||
s.start.isSolid(x+1+j,y+1) ||s.start.getType(x+1+j,y+1) == Board.CONCRETE)
|| type == Board.BLOCK_BALLOON && (s.start.isBlock(x+1+j,y-1) ||
s.start.isSolid(x+1+j,y-1) ||s.start.getType(x+1+j,y-1) == Board.CONCRETE)))
j++;
if (!canJump) {
if (j==0 && i==0) return x;
int pos = rand.nextInt(j-i) + i;
if (pos >= 0) pos++;
s.start.setElement(x,y,Board.EMPTY);
s.start.setElement(x+pos,y,type,color);
while (pos > 0) {x++; s.moves.addFirst(new Scheme.Move(false,x,y)); pos--; }
while (pos < 0) {x--; s.moves.addFirst(new Scheme.Move(true,x,y)); pos++; }
return pos+x;
}
// jumps
int[][] fields = new int[s.start.width][s.start.height];
int total = 0;
for (int k = 0; k < j-i+1; k++) {
int h = y, pos = k+i+x;
boolean lok = false, rok = false;
while (h>0 && s.start.isEmpty(pos, h-1)) h--;
while (h <= y) {
lok = correctMove(pos-1, h-1, pos-1, h, -1, -1, s.start, lok);
rok = correctMove(pos+1, h-1, pos+1, h, -1, -1, s.start, rok);
if (correctMove(pos-1, h, pos, y, color, type, s.start, lok) && fields[pos-1][h] == 0) {
fields[pos-1][h] = -1; total++;
}
if (correctMove(pos+1, h, pos, y, color, type, s.start, rok) && fields[pos+1][h] == 0) {
fields[pos+1][h] = 1; total++;
}
h++;
}
}
if (total < 1) return x;
int newX = -1, newY = -1, t, c, posY = -1, r = rand.nextInt(total);
for (int k=0; k < s.start.width; k++)
for (int l=0; l < s.start.height; l++)
if (fields[k][l] != 0)
if (r == 0) {newX = k; posY = newY = l; k = s.start.width; break;}
else r--;
// move
while (!s.start.isEmpty(newX, posY) && posY > 0) {
t = s.start.getType(newX, posY);
c = s.start.getColor(newX, posY);
s.start.setElement(newX, posY, s.start.getType(x, y), s.start.getColor(x, y));
s.start.setElement(x, y, t, c);
posY--;
}
s.start.setElement(newX, posY, s.start.getType(x, y), s.start.getColor(x, y));
s.start.setElement(x, y, Board.EMPTY);
int pos = (fields[newX][newY] == 1)? newX-1 : newX+1;
while (pos-x > 0) {x++; s.moves.addFirst(new Scheme.Move(false,x,y)); }
while (pos-x < 0) {x--; s.moves.addFirst(new Scheme.Move(true,x,y)); }
if (fields[newX][newY] == 1) s.moves.addFirst(new Scheme.Move(false,newX,newY));
else s.moves.addFirst(new Scheme.Move(true,newX,newY));
return newX;
}
/**
* Checks if the block can be moved in other field.
*
* @param x - coordinate of the block
* @param y - coordinate of the block
* @param canJump -true if the block can be raised
* @param s - the given scheme
* @return true if the block can be moved
*/
public static boolean canMoveBlock(int x, int y, boolean canJump, Scheme s) {
if (!s.start.isBlock(x,y) || s.start.isBlock(x,y-1) ||
s.start.getType(x, y+1) == Board.BLOCK_BALLOON) return false;
int color = s.start.getColor(x, y), type = s.start.getType(x, y);
if (type == Board.BLOCK_GLASS) canJump = false;
if (!canJump)
return correctMove(x-1,y,x,y,color,type,s.start,false) ||
correctMove(x+1,y,x,y,color,type,s.start,false);
int h = y, total = 0;
boolean lok = false, rok = false;
while (h>0 && s.start.isEmpty(x, h-1)) h--;
while (h <= y && total==0) {
lok = MixGenerator.correctMove(x-1, h-1, x-1, h, -1, -1, s.start, lok);
rok = MixGenerator.correctMove(x+1, h-1, x+1, h, -1, -1, s.start, rok);
if (MixGenerator.correctMove(x-1, h, x, y, color, type, s.start, lok)) total++;
if (MixGenerator.correctMove(x+1, h, x, y, color, type, s.start, rok)) total++;
h++;
}
if (total > 0) return true;
return false;
}
/**
* Check if the move is correct.
*
* @param newX - coordinate of the new field
* @param newY - coordinate of the new field
* @param x - coordinate of the old field
* @param y - coordinate of the old field
* @param color - color of the block
* @param type - type of the block
* @param board - the board
* @param canRaise - true if the block can be raised
* @return true if the move is correct
*/
public static boolean correctMove(int newX, int newY, int x, int y, int color,
int type, Board board, boolean canRaise) {
if (type == -1) type = board.getType(x, y);
if (type != Board.BLOCK_BALLOON && type != Board.BLOCK_CONCRETE &&
type != Board.BLOCK_GLASS && type != Board.BLOCK_SPHERE) return false;
if (color == -1) color = board.getColor(x, y);
if (type != Board.BLOCK_BALLOON &&
(board.getType(newX, newY+1) == Board.EMPTY ||
board.getType(newX, newY+1) == Board.ICE ||
board.getType(newX, newY+1) == Board.ELEVATOR ||
board.getType(newX, newY+1) == Board.ELEVATOR_ROOT ||
board.getType(newX, newY+1) == Board.PAINTER ||
board.getType(newX, newY+1) == Board.LAVA ||
board.isBlock(newX, newY+1) && board.getColor(newX, newY+1) == color
&& !(y==newY+1 && x==newX))) return false;
if (type == Board.BLOCK_BALLOON &&
(board.getType(newX, newY-1) == Board.EMPTY ||
board.getType(newX, newY-1) == Board.ICE ||
board.getType(newX, newY-1) == Board.ELEVATOR ||
board.getType(newX, newY-1) == Board.ELEVATOR_ROOT ||
board.getType(newX, newY-1) == Board.PAINTER ||
board.getType(newX, newY-1) == Board.LAVA ||
board.isBlock(newX, newY-1) && board.getColor(newX, newY-1) == color
&& !(y==newY-1 && x==newX))) return false;
if (!(board.isEmpty(newX, newY) || board.isBlock(newX, newY) && canRaise) ||
board.isBlock(newX, newY) && board.getColor(newX, newY) == color ||
board.getType(newX, newY) == Board.BLOCK_GLASS)
return false;
if (board.isEmpty(newX, newY) && (board.isBlock(newX, newY-1) &&
board.getColor(newX, newY-1) == color && !(y==newY-1 && x==newX) ||
board.isBlock(newX, newY+1) && board.getColor(newX, newY+1) == color
&& !(y==newY+1 && x==newX))) return false;
if (board.isBlock(newX+1, newY) && board.getColor(newX+1, newY) == color
&& !(y==newY && x==newX+1) ||
board.isBlock(newX-1, newY) && board.getColor(newX-1, newY) == color
&& !(y==newY && x==newX-1)) return false;
return true;
}
/**
* Generates a random permutation of integers from [0..n).
*
* @param n - size of the permutation
* @return the new permutation
*/
public final static int[] randomPermutation(int n) {
int[] t = new int[n];
for (int i=0;i<n;i++) t[i]=i;
for (;n>0;n--) {
int i = rand.nextInt(n);
int v = t[n-1];
t[n-1] = t[i];
t[i] = v;
}
return t;
}
}