package components;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import util.ChessfigureConstants;
import util.ExtractInformationFromBinary;
/**
* Repräsentiert das Spielfeld und enthaelt eine HashMap mit allen Figuren und
* ihrer aktuellen Position.
*
* @author Florian Franke
*
*/
public class Field {
/**
* HashMap, die alle im Spiel befindlichen Figuren enthaelt.
*/
private HashMap<Integer, Figure> figures = new HashMap<Integer, Figure>();
/**
* Gibt an, ob noch eine weisse Rochade moeglich ist
*/
private boolean kingSideCastlingWhitePossible = true;
private boolean queenSideCastlingWhitePossible = true;
/**
* Gibt an, ob noch eine schwarze Rochade moeglich ist
*/
private boolean kingSideCastlingBlackPossible = true;
private boolean queenSideCastlingBlackPossible = true;
/**
* Private Variable, in der die Instanz dieser Klasse gespeichert ist.
*/
private static Field instance = null;
/**
* Privater Konstruktor, damit nur eine Field-Instanz moeglich ist. Erstellt
* alle nötigen Figuren und positioniert sie auf dem Spielfeld.
*/
private Field() {
// Figuren aufs (virtuelle) Feld stellen
this.equipField();
}
/**
* Gibt die Field-Instanz zurueck
*
* @return Field-Instanz
*/
public static Field getInstance()
{
if (instance == null)
instance = new Field();
return instance;
}
public void equipArbitraryField(HashMap<Integer, Byte> arbitraryField)
{
// bisheriges Feld leeren
this.removeAllFigures();
Iterator<Entry<Integer, Byte>> it = arbitraryField.entrySet().iterator();
while (it.hasNext()) {
// getting key/value
Map.Entry<Integer, Byte> entry = (Map.Entry<Integer, Byte>) it.next();
Integer field = entry.getKey();
Byte figureByte = entry.getValue();
// creating figure
Figure f = ChessfigureConstants.makeFigureFromByte(figureByte);
this.putFigureAt(field, f);
}
}
/**
* Stattet das Schachfeld mit den Figuren aus.
*/
private void equipField()
{
// Feld sicherheitshalber leern
this.figures.clear();
// Reihe 1 und 8 setzen (Koenig, Dame, Springer, Laeufer, Turm)
for (int i = 1; i <= 2; i++) {
int rowCount = 0;
byte color = ChessfigureConstants.WHITE;
if (i == 2) {
rowCount = 56;
color = ChessfigureConstants.BLACK;
}
int position = rowCount;
// Turm
FigureRook fr1 = new FigureRook(color);
this.putFigureAt(++position, fr1);
// Springer
FigureKnight fk1 = new FigureKnight(color);
this.putFigureAt(++position, fk1);
// Laeufer
FigureBishop fb1 = new FigureBishop(color);
this.putFigureAt(++position, fb1);
// Dame
FigureQueen fq = new FigureQueen(color);
this.putFigureAt(++position, fq);
// Koenig
FigureKing fk = new FigureKing(color);
this.putFigureAt(++position, fk);
// Laeufer
FigureBishop fb2 = new FigureBishop(color);
this.putFigureAt(++position, fb2);
// Springer
FigureKnight fk2 = new FigureKnight(color);
this.putFigureAt(++position, fk2);
// Turm
FigureRook fr2 = new FigureRook(color);
this.putFigureAt(++position, fr2);
}
// Bauern setzen
for (int j = 1; j <= 16; j++) {
if (j <= 8) {
// Position in Reihe 2 bestimmen
Integer position = 8 + j;
// weissen Bauern aufs Spielfeld setzen
this.putFigureAt(position, new FigurePawn(ChessfigureConstants.WHITE));
} else {
// Erstelle schwarze Bauern
FigurePawn pawn = new FigurePawn(ChessfigureConstants.BLACK);
// Position in Reihe 7 bestimmen
int position = 40 + j;
// schwarzen Bauern aufs Spielfeld setzen
this.figures.put(position, pawn);
}
}
}
/**
* Holt die Figur an der gegebenen Position, sofern dort eine Figur
* vorhanden ist.
*
* @param position
* Die Position der Figur
* @return Figur oder null wenn keine Figur vorhanden ist.
*/
public Figure getFigureAt(Integer position)
{
if (this.isFigureOnField(position))
return figures.get(position);
else
return null;
}
/**
* Ueberprueft ob auf der gegebenen Position eine Figur steht.
*
* @param position
* Zur pruefende Position
* @return True: Figur vorhanden; False: Leeres Feld
*/
public boolean isFigureOnField(Integer fieldNumber)
{
return this.figures.containsKey(fieldNumber);
}
/**
* Positioniert eine Figur auf dem Feld
*
* @param fieldNumber
* Nummer des Felds, auf dem die Figur positioniert werden soll.
* @param figure
* Die zu positionierende Figur
* @return True: Figur konnte positioniert werden. False: Feld schon besetzt
* oder nicht vorhanden.
*/
public boolean putFigureAt(Integer fieldNumber, Figure figure)
{
if (!this.isFigureOnField(fieldNumber) && fieldNumber > 0 && fieldNumber <= 64) {
this.figures.put(fieldNumber, figure);
return true;
} else {
System.out.println("Es ist eine Figur auf dem Feld (" + fieldNumber + ") schon vorhanden.\n");
return false;
}
}
/**
* Bewegt eine Figur vom Start- aufs Zielfeld. Es wird davon ausgegangen,
* dass das Zielfeld leer ist. Sollte also eine Figur mit diesem Zug
* geschmissen werden, muss sie vorher entfernt worden sein.
* Sicherheitshalber wird es jedoch ueberprueft und notfalls gewarnt.
*
* @param fromFieldNumber
* Startfeld
* @param toFieldNumber
* Zielfeld
* @return True: Zug wurde ausgefuehrt; False: Zug konnte nicht ausgefuehrt
* werden
*/
public boolean moveFigure(Integer fromFieldNumber, Integer toFieldNumber)
{
// Rueckgabewert
boolean ret = false;
try {
// Befindet sich eine Figur auf dem Feld?
if (!this.isFigureOnField(fromFieldNumber))
throw new FieldException("Es befindet sich keine Figur auf dem Feld angegebenen Feld (" + getFieldName(fromFieldNumber) + ")!");
// Ist das Zielfeld frei?
if (this.isFigureOnField(toFieldNumber))
throw new FieldException("+++++\nEs befindet sich eine Figur auf Feld " + getFieldName(toFieldNumber)
+ ".\nWenn die Figur geschlagen werden soll, muss sie zuerst entfernt werden!\n+++++");
// Rochade pruefen
switch (fromFieldNumber) {
case 4: // Field.getFieldNumber("e1") geht nicht!
// Weissen Koenig bewegt
this.kingSideCastlingWhitePossible = false;
this.queenSideCastlingWhitePossible = false;
break;
case 61: // e8
// Schwarzen Koenig bewegt
this.kingSideCastlingBlackPossible = false;
this.queenSideCastlingBlackPossible = false;
break;
case 8: // h1
// Weisser Turm von h1 wegbewegt
this.kingSideCastlingWhitePossible = false;
break;
case 1: // a1
// Weisser Turm von a1 wegbewegt
this.queenSideCastlingWhitePossible = false;
break;
case 64: // h8
// Schwarzer Turm von h8 wegbewegt
this.kingSideCastlingBlackPossible = false;
break;
case 57: // a8
// Schwarzer Turm von a8 wegbewegt
this.queenSideCastlingBlackPossible = false;
break;
default:
break;
}
// Figur temporaer speichern
Figure figure = this.figures.get(fromFieldNumber);
// Figur vom alten Feld entfernen
this.removeFigureAt(fromFieldNumber);
// Figur auf neues Feld setzen
this.putFigureAt(toFieldNumber, figure);
// Figur wurde bewegt
figure.setMoved(true);
// Zug erfolgreich
ret = true;
} catch (FieldException e) {
System.out.println(e.getMessage());
}
return ret;
}
/**
* Entfernt die Figur auf der gegebenen Feldnummer.
*
* @param fieldNumber
* Nummer des Feldes, von dem die Figur entfernt werden soll
* @return True: Figur entfernt; False: Keine Figur vorhanden oder
* fehlerhafte Feldnummer
*/
public boolean removeFigureAt(Integer fieldNumber)
{
if (this.isFigureOnField(fieldNumber) && isValidFieldnumber(fieldNumber)) {
this.figures.remove(fieldNumber);
return true;
} else {
System.out.println("Keine Figur auf dem Feld (" + getFieldName(fieldNumber) + ").");
return false;
}
}
/**
* Gibt das aktuelle Feld als Hashmap aus. Integer: Feldposition Figure:
* Figur auf dem Feld
*
* @return HashMap<Integer, Figure>
*/
public HashMap<Integer, Figure> getCurrentFieldAsHashMap()
{
return this.figures;
}
/**
* Gibt das aktuelle Feld als Hashmap aus, jedoch mit dem Byte-Wert der
* Figur.
*
* @return HashMap<Integer, Byte>
*/
public HashMap<Integer, Byte> getCurrentFieldAsHashMapWithBytes()
{
HashMap<Integer, Byte> map = new HashMap<Integer, Byte>();
Iterator<Entry<Integer, Figure>> it = this.figures.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Figure> entry = (Map.Entry<Integer, Figure>) it.next();
Integer field = entry.getKey();
int moved = (entry.getValue().isMoved()) ? 1 : 0;
Byte figureByte = (byte) (entry.getValue().getFigureType() + (8 * entry.getValue().getColor()) + (16 * moved));
map.put(field, figureByte);
}
return map;
}
/**
* Gibt das aktuelle Feld aus.
*
* @return String mit dem aktuellen Feld
*/
public String getCurrentField()
{
// Figuren auf dem Feld werden im Strin gespeichert
String str = "";
// Nach Feldnummer (Key) sortieren
List<Integer> sortedList = new ArrayList<Integer>();
sortedList.addAll(this.figures.keySet());
Collections.sort(sortedList);
for (int i = 0; i < sortedList.size(); i++) {
// Feldnummer aus der sortierten Liste holen
Integer fieldNumber = sortedList.get(i);
// Figur holen
Figure f = this.getFigureAt(fieldNumber);
// In Ausgabe-String speichern
str += "Feld " + getFieldName(fieldNumber) + "(" + fieldNumber + "): " + f.toString() + "\n";
}
return str;
}
/**
* Exportiert alle Figuren, die sich aktuell auf dem Spielfeld befinden, in
* ein Short-Array
*
* @return Das Short-Array mit allen Short-Werten der Figuren
*/
public byte[] exportToShortArray()
{
// short-array anlegen. die groesse entspricht der anzahl der figuren
byte[] byteArray = new byte[this.figures.size()];
// Iterator erstellen, der ueber alle Figuren iteriert
Iterator<Entry<Integer, Figure>> it = this.figures.entrySet().iterator();
// Zaehler, der bei jeder Figur inkrementiert,
// um die aktuelle Position im ShortArray zu bestimmen.
int arrayPosition = 0;
// iteriere ueber alle Figuren
while (it.hasNext()) {
// Key/Value-Paar speichern
Map.Entry<Integer, Figure> pair = it.next();
// value-Objekt
Figure f = pair.getValue();
// Farbe, Figurtyp, X/Y-Position der aktuellen Figur ermitteln
byte color = f.getColor();
byte figureType = f.getFigureType();
boolean figureMoved = f.isMoved();
// Short-Wert mit ermittelten Werten berechnen
byte by = ChessfigureConstants.makeFigureByte(color, figureType, figureMoved);
// neuen Short-Wert dem ShortArray hinzufuegen
byteArray[arrayPosition] = by;
// zaehler erhoehen
arrayPosition++;
}
return byteArray;
}
/**
* Ermittelt die Position
*
* @return
*/
public int getKingPosition(byte color)
{
int position = -1;
if (ChessfigureConstants.isValidColor(color)) {
// Iterator erstellen, der ueber alle Figuren iteriert
Iterator<Entry<Integer, Figure>> it = this.figures.entrySet().iterator();
// iteriere ueber alle Figuren
while (it.hasNext()) {
// Key/Value-Paar speichern
Map.Entry<Integer, Figure> pair = it.next();
Figure f = pair.getValue();
// Koenig mit benoetigter Farbe?
if (f.getFigureType() == ChessfigureConstants.KING && f.getColor() == color) {
position = pair.getKey();
break;
}
}
} else {
System.out.println("Ungueltige Farbe des Koenigs!");
}
return position;
}
/**
* Ist eine weisse kurze Rochade moeglich?
*
* @return True: Ja; False: Nein
*/
public boolean isKingSideCastlingWhitePossible()
{
return (this.kingSideCastlingWhitePossible &&
!this.isFigureOnField(getFieldNumber("f1")) &&
!this.isFigureOnField(getFieldNumber("g1")));
}
/**
* Ist eine weisse lange Rochade moeglich?
*
* @return True: Ja; False: Nein
*/
public boolean isQueenSideCastlingWhitePossible()
{
return (this.queenSideCastlingWhitePossible &&
!this.isFigureOnField(getFieldNumber("b1")) &&
!this.isFigureOnField(getFieldNumber("c1")) &&
!this.isFigureOnField(getFieldNumber("d1")));
}
/**
* Ist eine schwarze kurze Rochade moeglich?
*
* @return True: Ja; False: Nein
*/
public boolean isKingSideCastlingBlackPossible()
{
return (this.kingSideCastlingBlackPossible &&
!this.isFigureOnField(getFieldNumber("f8")) &&
!this.isFigureOnField(getFieldNumber("g8")));
}
/**
* Ist eine schwarze lange Rochade moeglich?
*
* @return True: Ja; False: Nein
*/
public boolean isQueenSideCastlingBlackPossible()
{
return (this.queenSideCastlingBlackPossible &&
!this.isFigureOnField(getFieldNumber("b8")) &&
!this.isFigureOnField(getFieldNumber("c8")) &&
!this.isFigureOnField(getFieldNumber("d8")));
}
/**
* Ist eine weisse Rochade moeglich?
*
* @return
*/
public boolean isCastlingWhitePossible()
{
return (this.isQueenSideCastlingWhitePossible() || this.isKingSideCastlingWhitePossible());
}
/**
* Ist eine schwarze Rochade moeglich?
*
* @return
*/
public boolean isCastlingBlackPossible()
{
return (this.isQueenSideCastlingBlackPossible() || this.isKingSideCastlingBlackPossible());
}
/**
* Setzt das Feld in den Startzustand zurueck
*/
public void resetField()
{
this.equipField();
}
/**
* Leert das gesamte Feld
*/
public void removeAllFigures()
{
this.figures.clear();
}
/**
* Berechnet aus der gegebenen Feldnummer die X-Position
*
* @param fieldNumber
* Nummer des Feldes (1-64)
* @return Nummer der X-Position (1-8)
*/
public static byte getXPositionFromFieldnumber(int fieldNumber)
{
byte by = -1;
try {
if (fieldNumber < 1 || fieldNumber > 64)
throw new FieldException("Gegebene Feldwert ausserhalb des Feldes (1-64)!");
else {
// y-Wert ermitteln
byte y = getYPositionFromFieldnumber(fieldNumber);
// x-Wert berechnen
by = (byte) (fieldNumber - (8 * (y - 1)));
}
} catch (FieldException e) {
System.out.println(e.getMessage());
}
return by;
}
/**
* Berechnet aus der gegebenen Feldnummer (1-64) die Y-Position (1-8)
*
* @param fieldNumber
* Nummer des Feldes (1-64)
* @return Nummer der Y-Position (1-8)
*/
public static byte getYPositionFromFieldnumber(int fieldNumber)
{
byte by = -1;
try {
if (fieldNumber < 1 || fieldNumber > 64)
throw new FieldException("Gegebene Feldwert ausserhalb des Feldes (1-64)!");
else {
// Von (1-64) auf (0-63) aendern
fieldNumber--;
// Feldnummer von 1-8 berechnen
by = (byte) (Math.floor(fieldNumber / 8.0) + 1);
}
} catch (FieldException e) {
System.out.println(e.getMessage());
}
return by;
}
/**
* Ermittelt anhand der Zeile (a-h) die entsprechende Nummer (1-8)
*
* @param c
* Char-Wert der Zeile
* @return Zeilennummer
*/
public static int getColumnnumberByChar(char c)
{
int i = -1;
if (isValidXPosition(c)) {
if (c == 'a')
i = 1;
else if (c == 'b')
i = 2;
else if (c == 'c')
i = 3;
else if (c == 'd')
i = 4;
else if (c == 'e')
i = 5;
else if (c == 'f')
i = 6;
else if (c == 'g')
i = 7;
else if (c == 'h')
i = 8;
}
return i;
}
/**
* Ermittelt die Position anhand der Feldnummer in der Form A7/C3 etc.
*
* @param fieldNumber
* Nummer des Feldes (von 1 bis 64)
* @return Name des Feldes (z.B. A8)
*/
public static String getFieldName(int fieldNumber)
{
byte y = getYPositionFromFieldnumber(fieldNumber);
char x = getXPosition(getXPositionFromFieldnumber(fieldNumber));
String str = x + "" + y;
return str;
}
/**
* Ermittelt aus dem uebergebenen Feldnamen (a-h1-8) die Feldnummer (1-64)
*
* @param fieldName
* @return
*/
public static int getFieldNumber(String fieldName)
{
int fieldNumber = -1;
String pattern = "[a-h]{1}[1-8]{1}";
try {
if (!Pattern.matches(pattern, fieldName))
throw new FieldException("Die Eingabe (" + fieldName + ") entspricht nicht einer gueltigen Feldbezeichnung!\n Buchstaben klein geschrieben?");
else {
// Gueltige Feldbezeichnung, Zeile und Spalte "ausschneiden"
int column = getColumnnumberByChar(fieldName.substring(0, 1).charAt(0));
int row = Integer.parseInt(fieldName.substring(1, 2));
// Feldnummer berechnen
fieldNumber = ((row - 1) * 8) + column;
}
} catch (FieldException e) {
System.out.println(e.getMessage());
}
return fieldNumber;
}
/**
* Berechnet aus der Zeile und Spalte die Feldnummer.
*
* @param column
* Spalte
* @param row
* Zeile
* @return Feldnummer
*/
public static int getFieldNumber(int column, int row)
{
int fieldNumber = -1;
try {
if (!(row > 0 && row <= 8 && column > 0 && column <= 8)) {
throw new FieldException("Ungueltige Zeilen/Spaltengroessen");
} else {
fieldNumber = ((row - 1) * 8) + column;
}
} catch (FieldException e) {
// System.out.println(e.getMessage());
}
return fieldNumber;
}
/**
* Ueberprueft ob der gegebenen byte-Wert eine gueltige X-Position ist
*
* @param xPosition
* X-Position
* @return True: Gueltige Position; False: Ungueltige Position
*/
public static boolean isValidXPosition(byte xPosition)
{
return (xPosition > 0 && xPosition <= 8);
}
/**
* Ueberprueft ob der gegebenen char-Wert eine gueltige X-Position ist
*
* @param xPosition
* X-Position als Char (a-h)
* @return True: Gueltige Position; False: Ungueltige Position
*/
public static boolean isValidXPosition(char xPosition)
{
return (xPosition == 'a' || xPosition == 'b' || xPosition == 'c' || xPosition == 'd' || xPosition == 'e' || xPosition == 'f' || xPosition == 'g' || xPosition == 'h');
}
/**
* Ueberprueft ob der gegebenen byte-Wert eine gueltige Y-Position ist
*
* @param xPosition
* X-Position
* @return True: Gueltige Position; False: Ungueltige Position
*/
public static boolean isValidYPosition(byte yPosition)
{
return (yPosition > 0 && yPosition <= 8);
}
/**
* Prueft ob die uebergebene Feldnummer korrekt ist.
*
* @param fieldNumber
* Feldnummer
* @return True: Gehoert zum Schachfeld; False: Gehoert nicht zum Schachfeld
*/
public static boolean isValidFieldnumber(int fieldNumber)
{
return (fieldNumber > 0 && fieldNumber <= 64);
}
/**
* Gibt die X-Position als Buchstabe aus
*
* @param xPos
* @return
*/
public static char getXPosition(byte xPos)
{
char xPosChar = 'x';
switch (xPos) {
case 1:
xPosChar = 'a';
break;
case 2:
xPosChar = 'b';
break;
case 3:
xPosChar = 'c';
break;
case 4:
xPosChar = 'd';
break;
case 5:
xPosChar = 'e';
break;
case 6:
xPosChar = 'f';
break;
case 7:
xPosChar = 'g';
break;
case 8:
xPosChar = 'h';
break;
default:
break;
}
return xPosChar;
}
@Override
public String toString()
{
String str = "Field [figures=[";
Iterator<Entry<Integer, Figure>> it = this.figures.entrySet().iterator();
int i = 0;
while (it.hasNext()) {
Figure f = (Figure) it.next().getValue();
if (i > 0)
str += "\t";
str += f.toString();
str += "\n";
it.remove();
i++;
}
str += "]]";
return str;
}
}