package com.tacoid.pweek.logic;
import java.util.ArrayList;
import java.util.Iterator;
import com.tacoid.pweek.IGameService;
import com.tacoid.pweek.SoundPlayer;
import com.tacoid.pweek.IGameService.Achievement;
import com.tacoid.pweek.SoundPlayer.SoundType;
public class GameLogic {
public enum MoveType {
LEFT,
RIGHT,
UP,
DOWN,
ROT_RIGHT,
ROT_LEFT
}
public final static int LINES = 12;
public final static int COLUMNS = 6;
public final int GARBAGE = 6;
private State state = State.MOVE;
int grid[][] = new int[LINES * 2][COLUMNS];
private Piece piece = new Piece();
private Piece nextPiece = new Piece();
public boolean gridFF[][];
private float sum = 0f;
private int score = 0;
private ArrayList<Falling> fallings;
private float speed = 0.4f;
private boolean first = true;
private ArrayList<Explosion> removes;
private int combo = 0;
private boolean isDown = false;
private GameLogic opponent = null;
private int points = 0;
public int garbage = 0;
private float leftoverNuisance = 0f;
protected boolean paused = false;
private boolean isIA = false;
private int n_colors = 4;
private boolean cheatMode = false;
private float initialSpeed = 0.4f;
public ArrayList<Explosion> explosions = new ArrayList<Explosion>();
private IGameService gameService;
public GameLogic(IGameService gameService, boolean isIA) {
this.isIA = isIA;
this.gameService = gameService;
}
public void init() {
if (!isIA) {
gameService.unlockAchievement(Achievement.FANBOY);
}
fallings = null;
score = 0;
sum = 0;
speed = initialSpeed;
state = State.MOVE;
points = 0;
garbage = 0;
leftoverNuisance = 0f;
for (int l = 0; l < LINES * 2; l++) {
for (int c = 0; c < COLUMNS; c++) {
grid[l][c] = 0;
}
}
generate(); // generate current
generate(); // generate next
}
public GameLogic(GameLogic logic, boolean isIA) {
// copy grid.
for (int l = 0; l < LINES; l++) {
for (int c = 0; c < COLUMNS; c++) {
grid[l][c] = logic.grid[l][c];
}
}
this.isIA = isIA;
piece = new Piece(logic.piece, true);
nextPiece = new Piece(logic.nextPiece, true);
state = logic.state;
combo = 0;
}
private int cheatColor() {
int[] counts = new int[GARBAGE];
for (int c = 0; c < COLUMNS; c++) {
int l = LINES;
while (l >= 0 && grid[l][c] == 0) l--;
if (l >= 0 && grid[l][c] > 0 && grid[l][c] != GARBAGE) {
counts[grid[l][c]]++;
}
}
int max = 1;
for (int i = 1; i < GARBAGE; i++) {
if (counts[max] < counts[i]) {
max = i;
}
}
return max;
}
private boolean generate() {
// Sens de la piece :
// [x] [x][ ] [ ] [ ][x]
// [ ] [x]
piece = new Piece(nextPiece);
int coul1 = 1 + (int) (Math.random() * n_colors);
int coul2;
if (cheatMode) {
if (Math.random() < 0.5) {
coul2 = coul1;
} else {
coul2 = cheatColor();
}
} else {
coul2 = 1 + (int) (Math.random() * n_colors);
}
nextPiece.rot = (int) (Math.random() * 4);
switch (nextPiece.rot) {
case 0:
nextPiece.coords[0] = new Coord(LINES - 1, COLUMNS / 2, coul1);
nextPiece.coords[1] = new Coord(LINES, COLUMNS / 2, coul2);
break;
case 1:
nextPiece.coords[0] = new Coord(LINES, COLUMNS / 2, coul1);
nextPiece.coords[1] = new Coord(LINES, COLUMNS / 2 + 1, coul2);
break;
case 2:
nextPiece.coords[0] = new Coord(LINES, COLUMNS / 2, coul1);
nextPiece.coords[1] = new Coord(LINES - 1, COLUMNS / 2, coul2);
break;
case 3:
nextPiece.coords[0] = new Coord(LINES, COLUMNS / 2 + 1, coul1);
nextPiece.coords[1] = new Coord(LINES, COLUMNS / 2, coul2);
break;
}
return (piece.coords[0] == null) || (grid[(int)piece.coords[0].l][(int)piece.coords[0].c] == 0 && grid[(int)piece.coords[1].l][(int)piece.coords[1].c] == 0);
}
public void rotateRight() {
if (state == State.MOVE && !paused) {
if(!isIA) {
SoundPlayer.getInstance().playSound(SoundType.ROTATE, 0.5f, true);
}
piece.rotateRight(grid);
}
}
public void rotateLeft() {
if (state == State.MOVE && !paused) {
if(!isIA) {
SoundPlayer.getInstance().playSound(SoundType.ROTATE, 0.5f, true);
}
piece.rotateLeft(grid);
}
}
public void moveDown() {
if (state == State.MOVE && !paused) {
piece.descendre(grid);
if(!isIA) {
SoundPlayer.getInstance().playSound(SoundType.MOVE, 0.2f, true);
}
}
}
public boolean moveLeft() {
if (state == State.MOVE && !paused) {
if (piece.moveLeft(grid)) {
if(!isIA) {
SoundPlayer.getInstance().playSound(SoundType.MOVE, 0.2f, true);
}
return true;
}
}
return false;
}
public boolean moveRight() {
if (state == State.MOVE && !paused) {
if (piece.moveRight(grid)) {
if(!isIA) {
SoundPlayer.getInstance().playSound(SoundType.MOVE, 0.2f, true);
}
return true;
}
}
return false;
}
public void dropPiece() {
if (state == State.MOVE && !paused) {
state = State.POSE;
}
}
private ArrayList<Falling> gravity() {
ArrayList<Falling> boules = new ArrayList<Falling>();
int[] sums = new int[COLUMNS];
for (int l = 0; l < LINES * 2; l++) {
for (int c = 0; c < COLUMNS; c++) {
if (grid[l][c] == 0) {
sums[c]++;
} else if (sums[c] > 0) {
boules.add(new Falling(new Coord(l, c, grid[l][c]), sums[c]));
grid[l][c] = 0;
}
}
}
return boules;
}
public int floodfill(int l, int c, int coul, ArrayList<Coord> list) {
if (l < 0 || c < 0 || l >= LINES || c >= COLUMNS || grid[l][c] != coul
|| gridFF[l][c]) {
return 0;
}
gridFF[l][c] = true;
if (list != null) {
list.add(new Coord(l, c, coul));
}
return 1 + floodfill(l + 1, c, coul, list)
+ floodfill(l - 1, c, coul, list)
+ floodfill(l, c + 1, coul, list)
+ floodfill(l, c - 1, coul, list);
}
public ArrayList<Explosion> resolve() {
ArrayList<Explosion> remove = new ArrayList<Explosion>();
gridFF = new boolean[LINES][COLUMNS];
for (int l = 0; l < LINES; l++) {
for (int c = 0; c < COLUMNS; c++) {
if (grid[l][c] > 0 && grid[l][c] != GARBAGE) {
ArrayList<Coord> list = new ArrayList<Coord>();
if (floodfill(l, c, grid[l][c], list) >= 4) {
if (grid[l][c] == 5 && !isIA) {
gameService.unlockAchievement(Achievement.NINJA);
}
remove.add(new Explosion(this, list));
}
}
}
}
if(!isIA && !remove.isEmpty()) {
SoundPlayer.getInstance().playSound(SoundType.EXPLODE, 1.0f, 0.8f + combo / 30.0f);
}
return remove;
}
private void pose() {
for (Coord p : piece.coords) {
grid[(int)p.l][(int)p.c] = p.coul;
p.coul = 0;
}
}
public void poseEtGravity() {
for (Coord c : piece.coords) {
while (c.l > 0 && grid[(int)c.l - 1][(int)c.c] == 0) {
c.l--;
}
grid[(int)c.l][(int)c.c] = c.coul;
}
}
public int descendreEtPose() {
pose();
points = 0;
combo = 0;
do {
fallings = gravity();
for (Falling f : fallings) {
grid[(int)f.getEnd().l][(int)f.getEnd().c] = f.getEnd().coul;
}
removes = resolve();
for (Explosion r : removes) {
r.points = r.getNbPuyos() * 10 * (r.getNbPuyos() - 3 + combo);
points += r.points;
}
if (combo == 0)
combo = 8;
else
combo *= 2;
} while (removes.size() > 0);
return points;
}
public void update(float delta) {
if(!paused) {
sum += delta;
switch (state) {
case GARBAGE:
if (garbage > 0) {
generateGarbage();
state = State.GRAVITY;
} else {
state = State.MOVE;
}
first = true;
break;
case MOVE:
if (first) {
if (!generate()) {
state = State.LOST;
if (score == 0 && !isIA) {
gameService.unlockAchievement(Achievement.AFK);
}
} else if (!isIA && score > 0) {
boolean ocd = true;
for (int col = 0; col < COLUMNS && ocd; col++) {
ocd = ocd && grid[0][col] == 0;
}
if (ocd) {
gameService.unlockAchievement(Achievement.OCD);
}
}
first = false;
}
if (sum > speed || (isDown && sum > 0.1f)) {
if (!piece.descendre(grid)) {
state = State.POSE;
}
sum = 0f;
}
break;
case POSE:
if (sum > speed) {
pose();
state = State.GRAVITY;
sum = 0f;
first = true;
}
break;
case GRAVITY:
if (first) {
fallings = gravity();
first = false;
}
for (Falling f : fallings) {
f.update(delta);
}
if (sum > 0.5) {
boolean playGarbage = false;
for (Falling f : fallings) {
grid[(int)f.getEnd().l][(int)f.getEnd().c] = f.getEnd().coul;
if(f.getEnd().coul == GARBAGE && f.getInitial().l >= LINES) {
playGarbage = true;
}
}
if(playGarbage == true) {
SoundPlayer.getInstance().playSound(SoundType.NUISANCE, 0.5f, true);
}
fallings = null;
first = true;
state = State.RESOLVE;
sum = 0f;
}
break;
case RESOLVE:
if (first) {
removes = resolve();
for (Explosion r : removes) {
if (r.getNbPuyos() >= 8) {
gameService.unlockAchievement(Achievement.MEGA_EXPLODE);
if (r.getNbPuyos() >= 10) {
gameService.unlockAchievement(Achievement.MASTERSTROKE);
}
}
r.points = r.getNbPuyos() * 10 * (r.getNbPuyos() - 3 + combo);
if (opponent != null) {
float nuisance = r.points / 70.0f + leftoverNuisance;
leftoverNuisance = nuisance - (int) nuisance;
opponent.sendGarbage((int) nuisance);
}
points += r.points;
explosions.add(r);
}
first = false;
}
if (sum > 0.3) {
if (removes.size() > 0) {
state = State.GRAVITY;
first = true;
if (combo == 0) {
combo = 8;
} else {
combo *= 2;
if (!isIA) {
gameService.unlockAchievement(Achievement.FIRST_COMBO);
if (combo == 8 * 2 * 2 * 2 * 2) {
gameService.unlockAchievement(Achievement.CHAIN);
}
}
}
} else {
state = State.GARBAGE;
score += points;
points = 0;
combo = 0;
}
sum = 0f;
}
break;
default:
break;
}
}
}
private void generateGarbage() {
int l = LINES;
while (garbage >= COLUMNS) {
for (int c = 0; c < COLUMNS; c++) {
grid[l][c] = GARBAGE;
}
garbage -= COLUMNS;
l++;
}
ArrayList<Integer> cols = new ArrayList<Integer>();
for (int c = 0; c < COLUMNS; c++) {
cols.add(c);
}
for (int c = 0; c < garbage; c++) {
int i = (int)(Math.random() * cols.size());
grid[l][cols.get(i)] = GARBAGE;
cols.remove(i);
}
garbage = 0;
}
public int[][] getGrid() {
return grid;
}
public Piece getPiece() {
return piece;
}
public Piece getNextPiece() {
return nextPiece;
}
public ArrayList<Falling> getFallings() {
return fallings;
}
public State getState() {
return state;
}
public int getScore() {
return score;
}
public void down() {
isDown = true;
}
public void up() {
isDown = false;
}
public void setOpponent(GameLogic opponent) {
this.opponent = opponent;
}
public void sendGarbage(int garbage) {
this.garbage += garbage;
if (this.garbage > LINES * COLUMNS) {
garbage = LINES * COLUMNS;
}
}
public void pause() {
paused = true;
}
public void resume() {
paused = false;
}
public boolean isPaused() {
return paused;
}
public void setInitialSpeed(float speed) {
this.initialSpeed = speed;
this.speed = speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}
public void setNColors(int n) {
this.n_colors = n;
}
public void setCheatMode(boolean cheatMode) {
this.cheatMode = cheatMode;
}
public ArrayList<Explosion> getExplosions() {
long date = System.currentTimeMillis();
Iterator<Explosion> it = explosions.iterator();
while (it.hasNext()) {
Explosion e = it.next();
if (date - e.getExplosionDate() > 1000) {
it.remove();
}
}
return explosions;
}
}