package hexenschach.gameplay;
import hexenschach.board.Board;
import hexenschach.board.TurnPossibility;
import hexenschach.gui.UserInterfaceAPI;
import hexenschach.gui.FieldHighlightStatus;
import hexenschach.player.Player;
import hexenschach.player.AiAlgorithm;
import hexenschach.gui.graphic.HexenschachPanel;
import hexenschach.gui.Dictionary;
/**
* Die Klasse leitet Spiele indem sie die im Spiel befindlichen Spieler verwaltet,
* Eingaben vom GUI verarbeitet, mit dem Spielbrett kommuniziert und Methoden zum
* Rückgängigmachen und Wiederherstellen eines Zuges bietet.
*
* @author Andreas Lorig
* @date 11.05.2010
*/
public class Gameplay
{
// true wenn das Spiel vorbei ist
private boolean isOver = false;
// Der aktuelle GameContext
private GameContext currentContext;
// Die 3 beteiligten Spieler
private Player player1;
private Player player2;
private Player player3;
// Der Spieler der gerade am Zug ist
private int currentPlayer;
// Die Interface API
private UserInterfaceAPI interfaceAPI;
// Aktuelles Spielbrett
private Board board;
// Array von TurnPossibilities
private TurnPossibility []turn;
private TurnPossibility tp;
// Der Status wird als int dargestellt
private int status;
// Liste in der GameContext Objekte verwaltet werden
private UndoRedo undoRedo;
private AiAlgorithm aiAlg;
private boolean eroeffnungsZug;
/**
* Konstruktor der Klasse Gameplay, welchem 3 Spielerobjekte und das Interface
* "UserInterfaceAPI" übergeben werden. Dieser Konstruktor wird für die Funktion
* "neues Spiel" benutzt.
*
* @param newPlayer1 Spieler 1
* @param newPlayer2 Spieler 2
* @param newPlayer3 Spieler 3
* @param newInterfaceAPI die zu benutzende InterfaceAPI
*/
public Gameplay (Player newPlayer1, Player newPlayer2, Player newPlayer3, UserInterfaceAPI newInterfaceAPI)
{
player1 = newPlayer1;
player2 = newPlayer2;
player3 = newPlayer3;
interfaceAPI = newInterfaceAPI;
// Default: first currentPlayer = player1
currentPlayer = 1;
// Am Anfang eines Spieles wird ein neues Board erzeugt
board = new Board();
// setzen des ersten GameContextes
currentContext = new GameContext (board, player1, player2, player3, currentPlayer);
// Neue UndoRedo Liste wird erzeugt
undoRedo = new UndoRedo();
// der Kontext wird in die UndoRedo liste eingefügt
undoRedo.appendToEnd(currentContext);
// Von Anfang an ist keine Figur ausgewählt
status = 1;
// Zeichnen der Figuren
this.drawFigures();
// Anzeigen das eine neue Runde gestartet wurde
interfaceAPI.setDirectHelp(Dictionary.NewGameMessage);
// Anzeigen das der erste Spieler am Zug ist
this.informCurrentPlayer(status);
// Situation -> für schachcheck
aiAlg = new AiAlgorithm ();
eroeffnungsZug = true;
}
/**
* Konstruktor, dem 3 Spielerobjekte, die InterfaceAPI, der Aktuelle Spieler
* und ein Brett übergeben werden. Benutzt wird dieser Konstruktor für das
* Laden von Spielständen.
*
* @param newPlayer1 der erste Spieler
* @param newPlayer2 der zweite Spieler
* @param newPlayer3 der dritte Spieler
* @param newInterfaceAPI die InterfaceAPI
* @param newCurrentPlayer der aktuelle Spieler
* @param newBoard das neue Brett
*/
public Gameplay (Player newPlayer1, Player newPlayer2, Player newPlayer3, UserInterfaceAPI newInterfaceAPI,
int newCurrentPlayer, Board newBoard)
{
player1 = newPlayer1;
player2 = newPlayer2;
player3 = newPlayer3;
interfaceAPI = newInterfaceAPI;
// Das Board wird übergeben
board = newBoard;
// Der aktuelle Spieler wird übergeben
currentPlayer = newCurrentPlayer;
// setzen des ersten GameContextes
currentContext = new GameContext (board, player1, player2, player3, currentPlayer);
// Neue UndoRedo Liste wird erzeugt
undoRedo = new UndoRedo();
// der Kontext wird in die UndoRedo liste eingefügt
undoRedo.appendToEnd(currentContext);
// Von Anfang an ist keine Figur ausgewählt
status = 1;
// Zeichnen der Figuren
this.drawFigures();
// Anzeigen das eine neue Runde gestartet wurde
interfaceAPI.setDirectHelp(Dictionary.LoadedGameMessage);
// Anzeigen welcher Spieler am Zug ist
this.informCurrentPlayer(currentPlayer);
// Situation -> für schachcheck
aiAlg = new AiAlgorithm ();
eroeffnungsZug = true;
}
/**
* Verarbeitet die Eingabe eines Feldes, welche durch einen Klick, aber auch
* durch Tastatureingabe erfolgt sein kann. Die Methode wird nach besagten
* Ereignissen vom GUI aufgerufen.
*
* @param field String der ein Feld auf dem Spielfeld beschreibt.
*/
public void processFieldInput (String selectedField)
{
if (!isOver()) // Wenn das Spiel noch nicht vorbei ist
{
// 1. Menschlicher oder Computerspieler?
if (getPlayer (currentPlayer).isComputer) // boolean
{
this.computersTurn ();
} else this.playersTurn (selectedField);
}else
{
interfaceAPI.setDirectHelp(Dictionary.GameIsOver);
if (this.getPlayerColor(currentPlayer).equals("white"))
{
interfaceAPI.setDirectHelp(Dictionary.WhiteHasWon);
} else if (this.getPlayerColor(currentPlayer).equals("brown"))
interfaceAPI.setDirectHelp(Dictionary.BrownHasWon);
else interfaceAPI.setDirectHelp(Dictionary.BlackHasWon);
interfaceAPI.gameFinished();
}
}
/**
* Verarbeitet Klicke auf das Spielfeld, wenn zu dem Zeitpunkt noch keine Felder hervorgehoben sind.
*
* @param field das Feld der Figur deren mögliche Züge hervorgehoben werden sollen.
* @param die zuvor berechneten Spielzugsmöglichkeiten.
*/
private void noSelectedFigure (String field, TurnPossibility [] turnPo)
{
// Das Highlighting zurücksetzen
interfaceAPI.resetHighlight();
// Vergleich ob die Figur vom Spieler ist der dran ist
if (board.getPlayer(field) == currentPlayer)
{
// Highlighten des Feldes auf dem die ausgewählte Figur steht
interfaceAPI.highlightField (turnPo[0].from, FieldHighlightStatus.SELECT);
// Schleife die das Array durchläuft und die anderen Felder "highlighted"
for (int i = 0; i < turnPo.length; i++)
interfaceAPI.highlightField (turnPo[i].to, this.calculateStatus(turnPo[i].wasHit));
// Übergang in einen anderen Status
status = 2;
}
}
/**
* Verarbeitet Klicke auf das Spielfeld, wenn zu dem Zeitpunkt Felder hervorgehoben sind.
*
* @param field auf dieses Feld wurde geklickt.
* @param turnPo die zuvor berechneten Spielzugsmöglichkeiten.
*/
private void figureIsSelected (String field, TurnPossibility [] turnPo)
{
// Vorraussetzung: Auf dem Feld ist keine Figur des Spielers, der dort hin ziehen möchte.
if (board.getPlayer(field) != currentPlayer)
{
// In den Zugmöglichkeiten suchen ob der gewünschte Zug durchgeführt werden kann.
for (int i = 0; i < turnPo.length; i++)
{
if (turnPo[i].to.equals (field))
{
// Wird ein König geschlagen?
if (board.getFigureType(turnPo[i].to).equals ("king"))
{
String deadPlayer = getPlayerColor(board.getPlayer(turnPo[i].to));
int playerNum = board.getPlayer(turnPo[i].to);
// Ist es der König des weißen Spielers?
if (deadPlayer.equals("white"))
{
this.playerIsGameOver(playerNum);
// Wurde das Spiel dadurch entschieden?
if (isOver)
{
interfaceAPI.setDirectHelp(Dictionary.GameIsOver);
if (this.getPlayerColor(currentPlayer).equals("white"))
{
interfaceAPI.setDirectHelp(Dictionary.WhiteHasWon);
} else if (this.getPlayerColor(currentPlayer).equals("brown"))
{
interfaceAPI.setDirectHelp(Dictionary.BrownHasWon);
}
else {
interfaceAPI.setDirectHelp(Dictionary.BlackHasWon);
interfaceAPI.gameFinished();
}
} else {
interfaceAPI.setDirectHelp(Dictionary.WhiteKingDied);
interfaceAPI.setDirectHelp(Dictionary.RemainingPlayersContinue);
}
} else
// ist es der König des braunen Spielers?
if (deadPlayer.equals("brown"))
{
this.playerIsGameOver(playerNum);
if (isOver)
{
interfaceAPI.setDirectHelp(Dictionary.GameIsOver);
if (this.getPlayerColor(currentPlayer).equals("white"))
{
interfaceAPI.setDirectHelp(Dictionary.WhiteHasWon);
} else if (this.getPlayerColor(currentPlayer).equals("brown"))
interfaceAPI.setDirectHelp(Dictionary.BrownHasWon);
else {
interfaceAPI.setDirectHelp(Dictionary.BlackHasWon);
interfaceAPI.gameFinished();
}
} else {
interfaceAPI.setDirectHelp (Dictionary.BrownKingDied);
interfaceAPI.setDirectHelp(Dictionary.RemainingPlayersContinue);
}
} else
{
this.playerIsGameOver(playerNum);
if (isOver)
{
interfaceAPI.setDirectHelp(Dictionary.GameIsOver);
if (this.getPlayerColor(currentPlayer).equals("white"))
{
interfaceAPI.setDirectHelp(Dictionary.WhiteHasWon);
} else if (this.getPlayerColor(currentPlayer).equals("brown"))
interfaceAPI.setDirectHelp(Dictionary.BrownHasWon);
else interfaceAPI.setDirectHelp(Dictionary.BlackHasWon);
interfaceAPI.gameFinished();
} else {
interfaceAPI.setDirectHelp(Dictionary.BlackKingDied);
interfaceAPI.setDirectHelp(Dictionary.RemainingPlayersContinue);
}
}
}
// Figur auf das neue Feld setzen
interfaceAPI.moveFigure(turnPo[i].from, turnPo[i].to);
interfaceAPI.resetHighlight ();
// Neue Situation als aktuelles Board speichern.
board = turnPo[i].boardafter;
// Der Zug ist vorrüber und jetzt ist wieder keine Figur selektiert
status = 1;
if (checkMateCheck())
{
interfaceAPI.setDirectHelp ("Spieler " + currentPlayer + " ist Schachmatt gesetzt.");
// Die Referenz auf den aktuellen Spieler (der ja im schach steht) wird auf null gesetzt
playerIsGameOver (currentPlayer);
}
// Der nächste Spieler ist dran
this.nextPlayer();
// Anhängen des aktuellen GameContext in die UndoRedo Liste
undoRedo.appendToEnd(currentContext = new GameContext(board, player1, player2, player3, currentPlayer));
// Anzeigen wer als nächstes am Zug ist
this.informCurrentPlayer(currentPlayer);
// Steht er im schach?
if (checkCheck())
{
interfaceAPI.setDirectHelp (Dictionary.CheckWarning);
}
} else interfaceAPI.resetHighlight ();
}
} else
{
// Ist auf dem angeklickten Feld eine Figur des Spielers der am Zug ist, wird diese ausgewählt.
status = 1;
this.processFieldInput(field);
}
}
/**
* Gibt zurück ob ein Spiel vorbei ist.
*
* @return true wenn ein Spiel vorbei ist.
*/
public boolean isOver ()
{
if ((player1 == null && player2 == null) || (player2 == null && player3 == null) || (player1 == null && player3 == null))
isOver = true;
return isOver;
}
/**
* Der jeweilige Spieler ist game over
* @param num der Spieler der auf null gesetzt wird
*/
private void playerIsGameOver (int num)
{
if (num == 1)
player1 = null;
else if (num == 2)
player2 = null;
else player3 = null;
}
/**
* Setzt die aktuelle Spielsituation auf die vorherige zurück.
*/
public void undo ()
{
undoRedo.undo();
currentContext = undoRedo.getCurrentContext();
this.loadContext();
this.informCurrentPlayer(currentPlayer);
this.drawFigures();
status = 1;
interfaceAPI.resetHighlight();
}
/**
* Macht die Verwendung eines zuvor genutzten Undos wieder Rückgängig.
*/
public void redo ()
{
undoRedo.redo();
currentContext = undoRedo.getCurrentContext();
this.loadContext();
this.informCurrentPlayer(currentPlayer);
this.drawFigures();
status = 1;
interfaceAPI.resetHighlight();
}
/**
* Lässt die KI einen Zug des Spielers übernehmen.
*/
public void autoMove ()
{
TurnPossibilityList tpl = getAllPossibilities ();
boolean checked = this.checkCheck();
// Zwischenspeichern der TurnPossibility
tp = getPlayer (currentPlayer).evaluate(tpl, currentPlayer, getPlayer(currentPlayer), board, checked);
// Figur auf das neue Feld setzen
interfaceAPI.moveFigure(tp.from, tp.to);
interfaceAPI.resetHighlight ();
this.nextPlayer();
// Neue Situation als aktuelles Board speichern.
board = tp.boardafter;
// Anhängen des aktuellen GameContext in die UndoRedo Liste
undoRedo.appendToEnd(currentContext = new GameContext(board, player1, player2, player3, currentPlayer));
this.informCurrentPlayer(currentPlayer);
}
/**
* Der nächste Spieler ist am Zug
*/
private void nextPlayer ()
{
switch (currentPlayer)
{
case 1: if (getPlayer(2) != null)
{
currentPlayer = 2;
} else currentPlayer = 3; break;
case 2: if (getPlayer(3) != null)
{
currentPlayer = 3;
} else currentPlayer = 1; break;
case 3: if (getPlayer(1) != null)
{
currentPlayer = 1;
} else currentPlayer = 2; break;
}
}
/**
* Gibt für ein PlayerEnum den jeweiligen Spieler zurück.
*
* @param num Die Zahl die den Spieler repräsentiert
* @return der zugehörige Spieler
*/
private Player getPlayer (int num)
{
if (num == 1)
return player1;
else
if (num == 2)
return player2;
else
return player3;
}
/**
* Gibt für ein PlayerEnum die Farbe des jeweiligen Spielers zurück.
*
* @param num die Zahl die den Spieler repräsentiert.
* @return die Farbe des zugehörigen Spielers
*/
private String getPlayerColor (int num)
{
String color = getPlayer(num).playerColor;
return color;
}
/**
* Zeigt auf der Direkthilfe an, welcher Spieler am Zug ist.
*
* @param num die Zahl die den Spieler repräsentiert
*/
private void informCurrentPlayer (int num)
{
if (getPlayer (currentPlayer).isComputer == true)
{
interfaceAPI.setDirectHelp(Dictionary.NextPlayerIsPC);
interfaceAPI.setDirectHelp(Dictionary.PleaseClick);
} else {
String color = getPlayerColor(num);
if (color.equals ("white"))
interfaceAPI.setDirectHelp(Dictionary.WhitePlayerTurn);
else if (color.equals("black"))
interfaceAPI.setDirectHelp(Dictionary.BlackPlayerTurn);
else
interfaceAPI.setDirectHelp(Dictionary.BrownPlayerTurn);
}
}
/**
* Setzt das Spiel auf den Stand eines GameContextes
*/
private void loadContext ()
{
currentPlayer = currentContext.activePlayer;
board = currentContext.board;
player1 = currentContext.getPlayer(0);
player2 = currentContext.getPlayer(1);
player3 = currentContext.getPlayer(2);
}
/**
* Gibt den aktuellen GameContext zurück
* @return der aktuelle Kontext
*/
public GameContext getGameContext ()
{
return currentContext;
}
/**
* Der Zug eines Menschlichen Spielers
*
* @param field
*/
private void playersTurn (String field)
{
// Unterscheidung ob schon eine Figur ausgewählt ist oder nicht.
switch (status)
{
// Keine Figur ist "selektiert"
case 1: // zwischenspeichern der Zugmöglichkeiten der Figur für dieses Feld
turn = board.calculateTurnPossibility (field);
if (turn != null)
{
// Highlighten
noSelectedFigure (field, turn);
} else
if (board.getFigure(field) != null)
{
// Sollten keine Zugmöglichkeiten für eine Figur bestehen, so wird dieser darauf hingewiesen.
interfaceAPI.setDirectHelp (Dictionary.FigureCantMove);
// Das Highlighting wird zurück gesetzt
interfaceAPI.resetHighlight();
} else
{
// Ist keine Figur auf diesem Feld wird der Spieler darauf hingewiesen.
interfaceAPI.setDirectHelp(Dictionary.ChooseYourOwn);
// Das Highlighting wird zurück gesetzt
interfaceAPI.resetHighlight();
}
break;
// Es ist bereits eine Figur selektiert.
case 2:
// Zug der Figur
figureIsSelected (field, turn);
break;
}
}
/**
* Setzt die Figuren zu Spielbeginn aufs Spielfeld.
*/
void drawFigures ()
{
// In diesem String-Array sind alle Koordinaten des Spielfeldes enthalten.
String [] coords = HexenschachPanel.FIELD_NAMES;
// Die Schleife durchläuft alle Einträge der Hashmap
for(int i = 0; i < coords.length; i++)
{
if (board.boardHM.get(coords[i]) != null)
{
// Die jeweilige Figur wird gesetzt
interfaceAPI.setFigure(coords[i], board.getFigure(coords[i]));
}
}
}
/**
* Der Zug eines Computer gesteuerten Spielers.
*
*/
private void computersTurn ()
{
if (eroeffnungsZug == true)
{
tp = aiAlg.openGame(board, currentPlayer);
eroeffnungsZug = false;
} else {
TurnPossibilityList tpl = getAllPossibilities ();
boolean checked = this.checkCheck();
// Zwischenspeichern der TurnPossibility
tp = getPlayer (currentPlayer).evaluate(tpl, currentPlayer, getPlayer(currentPlayer), board, checked);
}
// Figur auf das neue Feld setzen
interfaceAPI.moveFigure(tp.from, tp.to);
interfaceAPI.resetHighlight ();
this.nextPlayer();
// Neue Situation als aktuelles Board speichern.
board = tp.boardafter;
// Anhängen des aktuellen GameContext in die UndoRedo Liste
undoRedo.appendToEnd(currentContext = new GameContext(board, player1, player2, player3, currentPlayer));
this.informCurrentPlayer(currentPlayer);
}
/**
* Testet ob Figuren im Schach stehen
*/
private boolean checkCheck ()
{
int [] intArray = aiAlg.checked(board);
if (intArray[currentPlayer] == 1)
return true;
else return false;
}
/**
* Schachmatterkennung
*/
private boolean checkMateCheck ()
{
int [] intArray = aiAlg.checkmate(currentPlayer, board);
if (intArray[currentPlayer] == 1)
return true;
else return false;
}
/**
* Berechnet den Status eines Feldes abhängig davon ob dort ein Gegner steht oder nicht.
* Ausgewählt/Frei/Belegt vom Gegner/
*
* @paran enemy true wenn dort eine gegnerische Figur steht
*/
private FieldHighlightStatus calculateStatus (boolean enemy)
{
if (enemy)
return FieldHighlightStatus.CAPTURE;
else
return FieldHighlightStatus.POSSIBLE;
}
/**
* Gibt alle Turnpossibilities für alle Figuren zurück
*/
private TurnPossibilityList getAllPossibilities ()
{
// Eine TurnPossibilityList wird erzeugt
TurnPossibilityList turnPoList = new TurnPossibilityList ();
// Die Schleife durchläuft alle Einträge der Hashmap
for(char x = 'A'; x <= 'U'; x++)
{
for(int y = 1; y <= 11; y++)
{
// Setzt die Koordinate für das Board zusammen
String coord = "" + x + y;
if ((board.boardHM.get(coord) != null) && (board.getPlayer (coord) == currentPlayer))
{
// Die Zugmöglichkeiten der Figur werden berechnet und zwischengespeichert
TurnPossibility [] turnPo = board.calculateTurnPossibility (coord);
// Das Array aus Turn Possibilities wird durchlaufen und jede Possibility wird in die Liste eingefügt
if (turnPo != null)
{
for (int i = 0; i < turnPo.length; i++)
{
turnPoList.addTP(turnPo[i]);
}
}
}
}
}
return turnPoList;
}
}