/*
* Datei: TetrisEnv.java
* Autor(en): Lukas König
* Java-Version: 6.0
* Erstellt: ??.06.2010
*
* (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;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
import java.util.TreeMap;
import eas.math.geometry.Polygon2D;
import eas.math.geometry.Vector2D;
import eas.miscellaneous.StaticMethods;
import eas.plugins.standard.visualization.chartPlugin.ChartEvent;
import eas.simulation.ConstantsSimulation;
import eas.simulation.Wink;
import eas.simulation.agent.AbstractAgent;
import eas.simulation.event.EASEvent;
import eas.simulation.spatial.sim2D.standardEnvironments.AbstractEnvironment2D;
import eas.startSetup.ParCollection;
/**
* @author Lukas König
*/
public class TetrisEnvironment extends AbstractEnvironment2D<AbstractTetrisAgent> {
/**
*
*/
private static final long serialVersionUID = -8699971734941326206L;
private boolean[][] feld = new boolean[10][25];
private long points = 0;
final protected int fac = 25;
private boolean gameOver = false;
private ParCollection pars;
private Random rand;
/**
* Miliseconds between tile drops.
*/
private int pause;
/**
* Character pressed last (null if none).
*/
private String pressed = null;
@Override
public synchronized BufferedImage getOutsideView(Graphics2D g2) {
BufferedImage img = this.getOutsideView();
g2.drawImage(img, 0, 0, null);
return img;
}
@Override
public BufferedImage getOutsideView() {
BufferedImage img = new BufferedImage(feld.length * fac, feld[0].length
* fac, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
g.setColor(Color.ORANGE);
g.drawLine(0, fac * 4, fac * feld.length, fac * 4);
g.drawLine(0, fac * 4 - 1, fac * feld.length, fac * 4 - 1);
for (int i = 0; i < feld.length; i++) {
for (int j = 0; j < feld[0].length; j++) {
if (this.getPix(i, j)) {
g.setColor(Color.DARK_GRAY);
g.drawRect(i * fac + 1, j * fac + 1, fac - 2, fac - 2);
g.setColor(Color.gray);
g.drawRect(i * fac + 0, j * fac + 0, fac - 2, fac - 2);
if (j == feld[0].length - 1) {
g.setColor(Color.black);
g.fillRect(i * fac + 1, j * fac + 1, fac - 2, fac - 2);
} else {
g.setColor(new Color(Math.min(255, j * 5 + 150), 0, 0));
g.fillRect(i * fac + 1, j * fac + 1, fac - 2, fac - 2);
}
}
}
}
g.setColor(Color.yellow);
for (AbstractAgent<?> a : this.getAgents()) {
g.drawString("" + a.id(), (int) this.getPositionInVisualization(a.id()).x - 6, (int) this.getPositionInVisualization(a.id()).y + 5);
}
return img;
}
@Override
public Vector2D getPositionInVisualization(int agentID) {
try {
Vector2D pos = new Vector2D(this.getAgentPosition(agentID));
pos.mult(this.fac);
pos.translate(new Vector2D(this.fac / 2, this.fac / 2));
return pos;
} catch (Exception e) {
return null;
}
}
private synchronized boolean getPix(int x, int y) {
return this.feld[StaticMethods.modZwischen(x, 0, this.feld.length - 1)]
[StaticMethods.modZwischen(y, 0, this.feld[0].length - 1)];
}
private synchronized void setPix(int x, int y, boolean color) {
this.feld[StaticMethods.modZwischen(x, 0, this.feld.length - 1)]
[StaticMethods.modZwischen(y, 0, this.feld[0].length - 1)] = color;
}
/**
* @param ident
*/
public TetrisEnvironment(final int ident, final ParCollection params) {
super(ident, params);
this.pars = params;
this.rand = new Random();
this.reset();
if (params != null) {
this.readScorez();
}
}
private TreeMap<Long, String> scorez;
/**
* @return Returns the scorez.
*/
public TreeMap<Long, String> getScorez() {
return this.scorez;
}
private LinkedList<String> pointHistory = new LinkedList<String>();
private ArrayList<String> pointHistoryBest = new ArrayList<String>();
private ArrayList<String> pointHistoryLast = new ArrayList<String>();
private void readScorez() {
LinkedList<String[]> scrz = new LinkedList<String[]>();
try {
scrz = StaticMethods.readCSVFile(
this.getParCollection().getStdDirectory(),
"scorez.dat",
";",
this.getParCollection());
pointHistoryBest = new ArrayList<String>(StaticMethods.liesTextArray(
this.getParCollection().getStdDirectory(),
"history.dat",
this.getParCollection()));
pointHistoryLast = new ArrayList<String>(StaticMethods.liesTextArray(
this.getParCollection().getStdDirectory(),
"Last.dat",
this.getParCollection()));
} catch (Exception e) {
StaticMethods.speichereTextDatei(this.getParCollection().getStdDirectory(),
"scorez.dat", "", this.getParCollection());
}
scorez = new TreeMap<Long, String>();
for (String[] line : scrz) {
scorez.put(Long.parseLong(line[1]), line[0]);
}
}
public void storeScorez() {
ArrayList<String> scrz = new ArrayList<String>(this.scorez.size());
for (long point : this.scorez.keySet()) {
scrz.add(this.scorez.get(point) + ";" + point);
}
StaticMethods.speichereTextAusArray(
this.getParCollection().getStdDirectory(),
"scorez.dat",
scrz,
this.getParCollection());
if (this.getPlace() == 1) {
StaticMethods.speichereTextAusArray(
this.getParCollection().getStdDirectory(),
"history.dat",
pointHistory,
this.getParCollection());
}
StaticMethods.speichereTextAusArray(
this.getParCollection().getStdDirectory(),
"Last.dat",
pointHistory,
this.getParCollection());
}
/**
* Note that the same score may only be stored once. Otherwise the older
* entry is deleted.
*/
private void gameOver() {
this.gameOver = true;
this.getSimTime().timeTerminate();
// int i = this.scorez.size();
}
public String scorezString() {
String s = "";
int i = this.scorez.size();
for (Long p : this.scorez.keySet()) {
s += "(" + i-- + ") " + this.scorez.get(p) + " - " + p + "\n";
}
return s;
}
public int getPlace() {
int place = 1;
for (long p : this.scorez.keySet()) {
if (this.points >= p) {
place++;
}
}
return this.scorez.size() - place + 2;
}
public void reset() {
this.gameOver = false;
this.lineCounter = 0;
this.nextAgent = null;
this.points = 0;
this.pressed = null;
if (this.getAgent(0) != null) {
this.removeAgent(0);
}
for (int i = 0; i < this.feld.length; i++) {
for (int j = 0; j < this.feld[0].length; j++) {
this.setPix(i, j, false);
}
}
for (int i = 0; i < this.getFeld().length; i++) {
this.setPix(i, this.getFeld()[0].length - 1, true);
}
this.addRandomTile();
}
private AbstractTetrisAgent nextAgent;
public AbstractTetrisAgent getNextAgent() {
return this.nextAgent;
}
public void setNextAgent(int nextAgent) {
switch (nextAgent) {
case 0:
this.nextAgent = new TetrisAgentI(0, this, this.pars);
break;
case 1:
this.nextAgent = new TetrisAgentZ(0, this, this.pars);
break;
case 2:
this.nextAgent = new TetrisAgentS(0, this, this.pars);
break;
case 3:
this.nextAgent = new TetrisAgentL(0, this, this.pars);
break;
case 4:
this.nextAgent = new TetrisAgentJ(0, this, this.pars);
break;
case 5:
this.nextAgent = new TetrisAgentO(0, this, this.pars);
break;
case 6:
this.nextAgent = new TetrisAgentT(0, this, this.pars);
break;
default:
break;
}
}
private synchronized void addRandomTile() {
Vector2D pos = new Vector2D(this.feld.length / 2, 1);
if (nextAgent == null) {
switch (rand.nextInt(7)) {
case 0:
this.nextAgent = new TetrisAgentI(0, this, this.pars);
break;
case 1:
this.nextAgent = new TetrisAgentZ(0, this, this.pars);
break;
case 2:
this.nextAgent = new TetrisAgentS(0, this, this.pars);
break;
case 3:
this.nextAgent = new TetrisAgentL(0, this, this.pars);
break;
case 4:
this.nextAgent = new TetrisAgentJ(0, this, this.pars);
break;
case 5:
this.nextAgent = new TetrisAgentO(0, this, this.pars);
break;
case 6:
this.nextAgent = new TetrisAgentT(0, this, this.pars);
break;
default:
break;
}
}
this.addAgent(this.nextAgent, pos, Math.PI * 2);
boolean full = false;
for (int j = 0; j < this.feld.length && !full; j++) {
if (this.feld[j][3]) {
full = true;
}
}
if (full) {
this.gameOver();
return;
}
switch (rand.nextInt(7)) {
case 0:
this.nextAgent = new TetrisAgentI(0, this, this.pars);
break;
case 1:
this.nextAgent = new TetrisAgentZ(0, this, this.pars);
break;
case 2:
this.nextAgent = new TetrisAgentS(0, this, this.pars);
break;
case 3:
this.nextAgent = new TetrisAgentL(0, this, this.pars);
break;
case 4:
this.nextAgent = new TetrisAgentJ(0, this, this.pars);
break;
case 5:
this.nextAgent = new TetrisAgentO(0, this, this.pars);
break;
case 6:
this.nextAgent = new TetrisAgentT(0, this, this.pars);
break;
default:
break;
}
this.points += 50;
}
protected synchronized void drawTile(
Polygon2D tile,
Vector2D pos,
double angle,
boolean color) {
Polygon2D tileTurned = new Polygon2D();
for (Vector2D v : tile) {
tileTurned.add(new Vector2D(v));
}
tileTurned.rotate(Vector2D.NULL_VECTOR, angle);
for (Vector2D p : tileTurned) {
Vector2D gesPos = new Vector2D(pos);
gesPos.translate(p);
this.setPix((int) Math.round(gesPos.x), (int) Math.round(gesPos.y), color);
}
}
@Override
public synchronized boolean setAgentPosition(int agentID, Vector2D newPosition) {
Polygon2D tile = this.getAgent(agentID).getAgentShape();
// Delete old position.
this.drawTile(
this.getAgent(agentID).getAgentShape(),
this.getAgentPosition(agentID),
this.getAgentAngle(agentID),
false);
// Try new position.
Polygon2D newShape = new Polygon2D();
for (Vector2D v : tile) {
newShape.add(new Vector2D(v));
}
newShape.rotate(Vector2D.NULL_VECTOR, this.getAgentAngle(agentID));
for (Vector2D p : newShape) {
Vector2D reqPos = new Vector2D(newPosition);
reqPos.translate(p);
if (this.getPix((int) Math.round(reqPos.x), (int) Math.round(reqPos.y))) {
// Redraw old position.
this.drawTile(
this.getAgent(agentID).getAgentShape(),
this.getAgentPosition(agentID),
this.getAgentAngle(agentID),
true);
return false;
}
}
// Draw new position.
this.drawTile(
this.getAgent(agentID).getAgentShape(),
newPosition,
this.getAgentAngle(agentID),
true);
return super.setAgentPosition(agentID, newPosition);
}
@Override
public synchronized void handleEvent(EASEvent e, Wink lastTick) {
this.pressed = e.getEventDescription().replace(ConstantsSimulation.KEY_EVENT_IDENTIFIER, "");
// for (Plugin<AbstractEnvironmentSpatial> visualizer : this.getVisualizers()) {
// visualizer.runDuringSim(this, lastTick, this.pars);
// }
}
private void deleteLine(final int num) {
this.lineCounter++;
for (int i = num; i > 0; i--) {
for (int j = 0; j < this.feld.length; j++) {
this.feld[j][i] = this.getPix(j, i - 1);
}
}
for (int j = 0; j < this.feld.length; j++) {
this.feld[j][0] = false;
}
}
private int lineCounter;
public int deleteLines() {
boolean full;
for (int i = this.feld[0].length - 2; i >= 0; i--) {
full = true;
for (int j = 0; j < this.feld.length; j++) {
if (!this.feld[j][i]) {
full = false;
}
}
if (full) {
this.deleteLine(i);
return this.deleteLines() + 1;
}
}
return 0;
}
@Override
public void step(Wink cyc) {
if (gameOver) {
return;
}
// if (stroke1 == null || stroke2 == null) {
// stroke1 = new BasicStroke(4f);
// stroke2 = new CompositeStroke(new BasicStroke(5f), new BasicStroke(0.5f));
// }
Vector2D aktPos = this.getAgentPosition(0);
Vector2D newpos = new Vector2D(aktPos);
Polygon2D pol = this.getAgentShape(0);
double angle = this.getAgentAngle(0);
newpos.translate(new Vector2D(0, 1));
if (!this.setAgentPosition(0, newpos)) {
this.removeAgent(0);
this.drawTile(
pol,
aktPos,
angle,
true);
this.lineCounter = 0;
this.deleteLines();
this.points += 250 * this.lineCounter * this.lineCounter;
this.addRandomTile();
}
this.points--;
ChartEvent event1 = new ChartEvent(
"Tetris chart",
"My points",
this.points);
if (cyc != null && cyc.getLastTick() == 1) {
event1.setChartFramePos(new Vector2D(500, 0));
event1.setChartFrameDim(new Vector2D(500, 600));
}
event1.setyAxisLabel("Points");
// event1.setLineStroke(stroke1);
event1.setLineColor(color1);
this.getSimTime().broadcastEvent(event1);
// Beste Punkte
if (cyc != null && this.pointHistoryBest.size() > cyc.getLastTick()) {
ChartEvent event2 = new ChartEvent(
"Tetris chart",
"Highscore",
Integer.parseInt(this.pointHistoryBest.get((int) cyc.getLastTick())));
// event2.setLineStroke(stroke2);
event2.setLineColor(color2);
this.getSimTime().broadcastEvent(event2);
}
// Letzte Punkte
if (cyc != null && this.pointHistoryLast.size() > cyc.getLastTick()) {
ChartEvent event3 = new ChartEvent(
"Tetris chart",
"Last run",
Integer.parseInt(this.pointHistoryLast.get((int) cyc.getLastTick())));
// event2.setLineStroke(stroke2);
// event2.setLineColor(color2);
this.getSimTime().broadcastEvent(event3);
}
if (cyc != null) {
pointHistory.add("" + this.points);
}
}
public void broadcastRemainingChartEvents(Wink terminationTime) {
for (long i = terminationTime.getLastTick(); i < pointHistoryBest.size(); i++) {
ChartEvent event2 = new ChartEvent(
"Tetris chart",
"Highscore",
Integer.parseInt(this.pointHistoryBest.get((int) i)));
event2.setXValue(i);
// event2.setLineStroke(stroke2);
event2.setLineColor(color2);
this.getSimTime().broadcastEvent(event2);
}
for (long i = terminationTime.getLastTick(); i < pointHistoryLast.size(); i++) {
ChartEvent event3 = new ChartEvent(
"Tetris chart",
"Last run",
Integer.parseInt(this.pointHistoryLast.get((int) i)));
event3.setXValue(i);
this.getSimTime().broadcastEvent(event3);
}
}
// private transient Stroke stroke1 = new BasicStroke(4f);
// private transient Stroke stroke2 = new CompositeStroke(new BasicStroke(5f), new BasicStroke(0.5f));
private Color color1 = Color.blue;
private Color color2 = Color.black;
@Override
public synchronized boolean removeAgent(int agentID) {
try {
this.drawTile(
this.getAgentShape(agentID),
this.getAgentPosition(agentID),
this.getAgentAngle(agentID),
false);
} catch (Exception e) {}
return super.removeAgent(agentID);
}
public String getPressed() {
return this.pressed;
}
public boolean[][] getFeld() {
return this.feld;
}
@Override
public Vector2D getAgentPosition(int agentID) {
Vector2D pos = super.getAgentPosition(agentID);
try {
pos.x = StaticMethods.modZwischen((int) Math.round(pos.x), 0, this.feld.length - 1);
pos.y = StaticMethods.modZwischen((int) Math.round(pos.y), 0, this.feld[0].length - 1);
} catch (Exception e) {
return null;
}
return pos;
}
@Override
public synchronized boolean setAgentAngle(int agentID, double angleNew) {
Vector2D posOld = this.getAgentPosition(agentID);
double angleOld = this.getAgentAngle(agentID);
Polygon2D shapeOld = this.getAgentShape(agentID);
// Delete old position.
this.drawTile(
shapeOld,
posOld,
angleOld,
false);
// Try new position.
Polygon2D shapeNew = new Polygon2D();
for (Vector2D v : shapeOld) {
shapeNew.add(new Vector2D(v));
}
shapeNew.rotate(Vector2D.NULL_VECTOR, angleNew);
for (Vector2D p : shapeNew) {
if (this.getPix((int) Math.round(p.x + posOld.x), (int) Math.round(p.y + posOld.y))) {
// Redraw old position.
this.drawTile(
shapeOld,
posOld,
angleOld,
true);
return false;
}
}
// Draw new position.
this.drawTile(
shapeOld,
posOld,
angleNew,
true);
return super.setAgentAngle(agentID, angleNew);
}
@Override
public String toString() {
String s = "";
for (int i = 0; i < this.feld[0].length; i++) {
for (int j = 0; j < this.feld.length; j++) {
if (this.feld[j][i]) {
s += "#";
} else {
s += " ";
}
}
s += "\n";
}
return s;
}
public synchronized void senkeAb() {
Vector2D aktPos = this.getAgentPosition(0);
Vector2D newpos = new Vector2D(aktPos);
newpos.translate(new Vector2D(0, 1));
while (this.setAgentPosition(0, newpos)) {
aktPos = this.getAgentPosition(0);
newpos = new Vector2D(aktPos);
newpos.translate(new Vector2D(0, 1));
}
this.step(null);
}
/**
* @return Returns the points.
*/
public long getPoints() {
return this.points;
}
/**
* @return Returns the gameOver.
*/
public boolean isGameOver() {
return this.gameOver;
}
/**
* @return Returns the pause.
*/
public int getPause() {
return this.pause;
}
/**
* @param pause The pause to set.
*/
public synchronized void setPause(int pause) {
this.pause = pause;
}
@Override
public synchronized Polygon2D getAgentShapeInVisualization(int agentID) {
return null;
}
}