/*
* File name: TetrisEnvAutomatic.java (package eas.users.lukas.demos.tetris.automaticTetris)
* Author(s): Lukas König
* Java version: 6.0
* Generation date: 17.02.2012 (11:37:57)
*
* (c) This file and the EAS (Easy Agent Simulation) framework containing it
* is protected by Creative Commons by-nc-sa license. Any altered or
* further developed versions of this file have to meet the agreements
* stated by the license conditions.
*
* In a nutshell
* -------------
* You are free:
* - to Share -- to copy, distribute and transmit the work
* - to Remix -- to adapt the work
*
* Under the following conditions:
* - Attribution -- You must attribute the work in the manner specified by the
* author or licensor (but not in any way that suggests that they endorse
* you or your use of the work).
* - Noncommercial -- You may not use this work for commercial purposes.
* - Share Alike -- If you alter, transform, or build upon this work, you may
* distribute the resulting work only under the same or a similar license to
* this one.
*
* + Detailed license conditions (Germany):
* http://creativecommons.org/licenses/by-nc-sa/3.0/de/
* + Detailed license conditions (unported):
* http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en
*
* This header must be placed in the beginning of any version of this file.
*/
package eas.users.demos.tetris.automaticTetris;
import java.io.Serializable;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Vector2D;
import eas.plugins.standard.visualization.chartPlugin.ChartEvent;
import eas.simulation.Wink;
import eas.startSetup.ParCollection;
import eas.users.demos.tetris.AbstractTetrisAgent;
import eas.users.demos.tetris.TetrisAgentI;
import eas.users.demos.tetris.TetrisAgentJ;
import eas.users.demos.tetris.TetrisAgentL;
import eas.users.demos.tetris.TetrisAgentO;
import eas.users.demos.tetris.TetrisAgentS;
import eas.users.demos.tetris.TetrisAgentT;
import eas.users.demos.tetris.TetrisAgentZ;
import eas.users.demos.tetris.TetrisEnvironment;
/**
* @author Lukas König
*/
public class TetrisEnvAutomatic extends TetrisEnvironment implements Runnable, Serializable {
/**
*
*/
private static final long serialVersionUID = 4571125804876053675L;
private Bewertung1 bewertung;
public TetrisEnvAutomatic(int ident, ParCollection params) {
super(ident, params);
bewertung = new Bewertung1();
}
public boolean turnLeft() {
double angle = this.getAgentAngle(0) - Math.PI / 2;
if (angle < 0.1) {
angle = Math.PI * 2;
}
return this.setAgentAngle(0, angle);
}
public boolean sink() {
Vector2D newpos = new Vector2D(this.getAgentPosition(0));
newpos.translate(new Vector2D(0, 1));
boolean worked = this.setAgentPosition(0, newpos);
if (worked) {
deepness++;
}
return worked;
}
public boolean shiftLeft() {
Vector2D newpos = new Vector2D(this.getAgentPosition(0));
newpos.translate(new Vector2D(-1, 0));
return this.setAgentPosition(0, newpos);
}
public boolean shiftRight() {
Vector2D newpos = new Vector2D(this.getAgentPosition(0));
newpos.translate(new Vector2D(1, 0));
return this.setAgentPosition(0, newpos);
}
public boolean applyMove(TetrisMove move) {
if (move.getMove() == TetrisMove.MOVE_LEFT) {return this.shiftLeft();}
if (move.getMove() == TetrisMove.MOVE_RIGHT) {return this.shiftRight();}
if (move.getMove() == TetrisMove.MOVE_SINK) {return this.sink();}
if (move.getMove() == TetrisMove.MOVE_TURN) {return this.turnLeft();}
return false;
}
private boolean[][] oldField = null;
private void copyFeld(boolean[][] quelle, boolean[][] ziel) {
for (int i = 0; i < quelle.length; i++) {
for (int j = 0; j < quelle[0].length; j++) {
ziel[i][j] = quelle[i][j];
}
}
}
/**
* Wendet eine Move-Sequenz auf das Feld an.
*
* @param seq Die anzuwendende Sequenz. Es werden die nicht möglichen
* Moves herausgelöscht.
* @return Die anzahl der gelöschten Zeilen.
*/
public int applySequence(TetrisMoveSequence seq) {
TetrisMoveSequence seqNeu = new TetrisMoveSequence();
for (TetrisMove tm : seq) {
if (applyMove(tm)) {
seqNeu.add(tm);
}
}
seq.clear();
seq.addAll(seqNeu);
int lines = this.fixiereAgenten();
return lines;
}
public boolean addTile(int tileType) {
AbstractTetrisAgent nextAgent;
Vector2D pos = new Vector2D(this.getFeld().length / 2, 2);
this.removeAgent(0);
switch (tileType) {
case 0: nextAgent = new TetrisAgentI(0, this, this.getParCollection()); break;
case 1: nextAgent = new TetrisAgentZ(0, this, this.getParCollection()); break;
case 2: nextAgent = new TetrisAgentS(0, this, this.getParCollection()); break;
case 3: nextAgent = new TetrisAgentL(0, this, this.getParCollection()); break;
case 4: nextAgent = new TetrisAgentJ(0, this, this.getParCollection()); break;
case 5: nextAgent = new TetrisAgentO(0, this, this.getParCollection()); break;
case 6: nextAgent = new TetrisAgentT(0, this, this.getParCollection()); break;
default: nextAgent = null; break;
}
this.addAgent(nextAgent, pos, Math.PI * 2);
boolean full = false;
for (int j = 0; j < this.getFeld().length && !full; j++) {
if (this.getFeld()[j][3]) {
return false;
}
}
return true;
}
private void senkeKomplettAb() {
while (this.sink()) {}
}
public int fixiereAgenten() {
this.senkeKomplettAb();
Vector2D aktPos = this.getAgentPosition(0);
Polygon2D pol = this.getAgentShape(0);
double angle = this.getAgentAngle(0);
this.removeAgent(0);
this.drawTile(pol, aktPos, angle, true);
return this.deleteLines();
}
private int deepness;
public double bewerteSequenz(int tileType, TetrisMoveSequence sequenz) {
double bewertung = 0;
this.hoehenPunkte = null;
// Altes Feld sichern.
if (oldField == null) {
oldField = new boolean[this.getFeld().length][this.getFeld()[0].length];
}
this.bewertung.setHoeheVorher(this.getFuellHoehe());
this.bewertung.setLueckenVorher(this.getLueckenSophisticated());
this.bewertung.setHoehenDiffVorher(this.getHoehenDifferenz());
copyFeld(this.getFeld(), oldField);
this.addTile(tileType);
deepness = 0;
int lines = this.applySequence(sequenz);
bewertung = this.bewertung.bewerte(this, lines, deepness, tileType);
copyFeld(oldField, this.getFeld());
return bewertung;
}
public int getFuellHoehe() {
for (int i = 3; i < this.getFeld()[0].length - 5; i++) {
for (int j = 0; j < this.getFeld().length; j++) {
if (this.getFeld()[j][i]) {
return this.getFeld()[0].length - i - 1;
}
}
}
return 0;
}
/**
* xx
* xxxxx
* x xxx
* x xxx
* x xxx
*
* ==> 3 + 2 = 5.
*
* @return
*/
public int getLueckenSophisticated() {
int anzahl = 0;
int zwischAnzahl = 0;
boolean toggle = true;
boolean start = false;
int anzahlLuecken;
for (int j = 0; j < this.getFeld().length; j++) {
zwischAnzahl = 0;
start = false;
anzahlLuecken = 1;
for (int i = this.getFeld()[0].length - 2; i >= 3; i--) {
if (toggle && !this.getFeld()[j][i]) {
anzahl += zwischAnzahl * anzahlLuecken; // Je weiter oben, desto schlimmer.
zwischAnzahl = 0;
toggle = false;
start = true;
// anzahlLuecken++;
}
if (!toggle && !this.getFeld()[j][i]) {
zwischAnzahl++;
start = true;
}
if (!toggle && this.getFeld()[j][i]) {
toggle = true;
}
if (start && toggle && this.getFeld()[j][i]) {
zwischAnzahl++;
}
}
}
return anzahl;
}
public int getLueckenSimple() {
int anzahl = 0;
for (int i = 3; i < this.getFeld()[0].length; i++) {
for (int j = 0; j < this.getFeld().length; j++) {
if (!this.getFeld()[j][i]) {
if (this.getFeld()[j][i - 1]) {
anzahl++;
}
}
}
}
return anzahl;
}
protected Integer[] hoehenPunkte = null;
public int getHoehenDifferenz() {
int min = Integer.MAX_VALUE;
int min2 = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
if (hoehenPunkte == null) {
hoehenPunkte = new Integer[this.getFeld().length];
for (int i = 0; i < hoehenPunkte.length; i++) {
for (int j = 0; j < this.getFeld()[0].length; j++) {
if (this.getFeld()[i][j]) {
hoehenPunkte[i] = this.getFeld()[0].length - j - 1;
break;
}
}
}
}
for (int i = 0; i < hoehenPunkte.length - 1; i++) {
if (max < hoehenPunkte[i]) {
max = hoehenPunkte[i];
}
if (min > hoehenPunkte[i]) {
min2 = min;
min = hoehenPunkte[i];
}
}
return max - min2;
}
/**
* @param zweitschlucht Ob die größte oder zweitgrößte Schlucht zurückgegeben wird.
*/
public int getSchluchtenPunkte(boolean zweitschlucht) {
int maxSchlucht1 = 0;
int maxSchlucht2 = 0;
int zwisch1, zwisch2;
if (hoehenPunkte == null) {
hoehenPunkte = new Integer[this.getFeld().length];
for (int i = 0; i < hoehenPunkte.length; i++) {
for (int j = 0; j < this.getFeld()[0].length; j++) {
if (this.getFeld()[i][j]) {
hoehenPunkte[i] = this.getFeld()[0].length - j - 1;
break;
}
}
}
}
for (int i = 0; i < hoehenPunkte.length - 1; i++) {
if (i == 0) {
zwisch1 = hoehenPunkte[hoehenPunkte.length - 1] - hoehenPunkte[i];
zwisch2 = hoehenPunkte[i + 1] - hoehenPunkte[i];
} else {
zwisch1 = hoehenPunkte[i - 1] - hoehenPunkte[i];
zwisch2 = hoehenPunkte[i + 1] - hoehenPunkte[i];
}
if (zwisch2 < zwisch1) {
zwisch1 = zwisch2;
}
if (zwisch1 > maxSchlucht1) {
maxSchlucht2 = maxSchlucht1;
maxSchlucht1 = zwisch1;
} else if (zwisch1 > maxSchlucht2) {
maxSchlucht2 = zwisch1;
}
}
// Zweitgrößte Schlucht.
if (zweitschlucht) {
return maxSchlucht2;
} else {
return maxSchlucht1;
}
}
public TetrisMoveSequence bestMove(int tileType) {
double best;
TetrisMoveSequence tms;
TetrisMoveSequence bestSequenz = new TetrisMoveSequence();
double zwischBew;
best = Double.NEGATIVE_INFINITY;
int drehungen = 4;
switch (tileType) {
case 0: drehungen = 2; break; // I
case 1: drehungen = 2; break; // Z
case 2: drehungen = 2; break; // S
case 3: drehungen = 4; break; // L
case 4: drehungen = 4; break; // J
case 5: drehungen = 1; break; // O
case 6: drehungen = 4; break; // T
default: break;
}
// Drehen.
for (int h = 0; h < drehungen; h++) {
// Horizontal 1.
for (int i = 1; i < this.getFeld().length; i++) {
// Vertikal.
for (int j = 1; j < this.getFeld()[0].length; j++) {
// Horizontal 2.
for (int k = -1; k <= 1; k++) {
if (k != 0) {
tms = erzeugeSequenzAusCode(h, i, j, k);
zwischBew = this.bewerteSequenz(tileType, tms);
if (zwischBew > best) {
best = zwischBew;
bestSequenz = tms;
}
}
}
}
}
}
if (this.getSimTime() != null) {
if (Double.POSITIVE_INFINITY == best) {
this.getSimTime().broadcastEvent(new ChartEvent("TetrisAutomaticChart", "Bewertung", 1000));
} else {
this.getSimTime().broadcastEvent(new ChartEvent("TetrisAutomaticChart", "Bewertung", best));
}
}
return bestSequenz;
}
private TetrisMoveSequence erzeugeSequenzAusCode(int d, int h1, int v, int h2) {
TetrisMoveSequence tms = new TetrisMoveSequence();
// Drehen.
for (int i = 0; i < d; i++) {
tms.add(new TetrisMove(TetrisMove.MOVE_TURN));
}
// Horizontal 1.
if (h1 < 0) {
for (int i = 0; i > h1; i--) {
tms.add(new TetrisMove(TetrisMove.MOVE_LEFT));
}
} else {
for (int i = 0; i < h1; i++) {
tms.add(new TetrisMove(TetrisMove.MOVE_RIGHT));
}
}
// Vertikal.
for (int i = 0; i < v; i++) {
tms.add(new TetrisMove(TetrisMove.MOVE_SINK));
}
// Horizontal 2.
if (h2 < 0) {
for (int i = 0; i > h2; i--) {
tms.add(new TetrisMove(TetrisMove.MOVE_LEFT));
}
} else {
for (int i = 0; i < h2; i++) {
tms.add(new TetrisMove(TetrisMove.MOVE_RIGHT));
}
}
return tms;
}
@Override
public void step(Wink cyc) {
}
private TetrisMoveSequence seq;
private int tileType;
public void setSeq(TetrisMoveSequence seq) {
this.seq = seq;
}
public void setTileType(int tileType) {
this.tileType = tileType;
}
public TetrisMoveSequence getSeq() {
return this.seq;
}
@Override
public void run() {
this.addTile(tileType);
seq = null;
seq = bestMove(tileType);
}
}