package Game;
import Pieces.BoardSquare;
import java.util.Stack;
/**
* @author Kate Fayra <kate@fayra.com>
*
* This class manages the game by keeping track of the current owner's turn, validating moves, and makes calls to the Game.Board to move pieces.
*/
public class GameManager {
/**
*The game board.
*/
public Board gameBoard;
/**
*True if it is player 1's turn.<br>
*False if it is player 2's turn.
*
*/
private Player playerTurn;
private boolean ignoreTurn;//flag to ignore who's turn it is for check testing
/**
*This enum is used as a return value from the move functions.<br>
* NONE: If the piece is moving to a blank square.<br>
* CAPTURE: If this move would capture the opponent's piece.<br>
* ERROR: The move can not be made.<br>
*/
public enum MoveStatus {
NONE, CAPTURE, ERROR
}
/**
* Stack containing the move history.
*/
public Stack MoveHist;
private int moveCount = 0;
/**
* GameManager Constructor, sets up the game board and starting turn state.
*/
public GameManager(GameType type){
MoveHist = new Stack();
gameBoard = new Board(8, 8, type);
System.out.println("Setting up new game.\n");
playerTurn = Player.WHITE;
ignoreTurn = false;
}
/**
* Undos the last move.
*
* @return true if the undo is successful, false otherwise.
*/
public boolean undo(){
if(!MoveHist.empty()){
Command command = (Command) MoveHist.pop();
BoardSquare startSquare = gameBoard.getPiece(command.destX, command.destY);
BoardSquare destSquare = gameBoard.getPiece(command.sourceX, command.sourceY);
//Player start_player = startSquare.owner;
//Player dest_player = destSquare.owner;
gameBoard.setPiece(destSquare.x, destSquare.y, startSquare, startSquare.owner);//Make the move
gameBoard.setBlank(startSquare.x, startSquare.y);
gameBoard.getPiece(command.sourceX, command.sourceY).hasMoved = command.hasMoved;
if (currentTurn() == Player.BLACK) {
moveCount--;
}
nextTurn();//Next owner's turn.
return true;
}
return false;
}
/**
* Adds a move to the history.
* @param startX starting x-coordinate
* @param startY starting y-coordinate.
* @param destX destination x-coordinate.
* @param destY destination y-coordinate.
* @param startSquare starting square.
*/
public void addToMoveHist(int startX, int startY, int destX, int destY, BoardSquare startSquare){
//char xs = ((char) (startX + 97));
char xEndChar = ((char) (destX + 97));
String commandStr = "";
if (currentTurn() == Player.WHITE) {
moveCount++;
commandStr += moveCount;
commandStr += ". ";
} else {
commandStr += " ";
}
char symbol = startSquare.symbol;
if (symbol != 'P' && symbol != ' ')
commandStr += symbol;
commandStr += xEndChar;
commandStr += (destY + 1);
Command command = new Command(startX, startY, destX, destY, startSquare.owner, startSquare.hasMoved, commandStr);
MoveHist.push(command);
}
/**
* Sets playerTurn to the next players turn.
*/
public void nextTurn(){
if(playerTurn == Player.WHITE)
playerTurn = Player.BLACK;
else
playerTurn = Player.WHITE;
}
/**
* Returns the player whose turn it is.
* @return Returns the player whose turn it is.
*/
public Player currentTurn() {
return playerTurn;
}
/**
* Attempt to move a Piece from (startX,startY) to (destX,destY).
*
* @param startX starting y-coordinate.
* @param startY starting y-coordinate.
* @param destX destination x-coordinate.
* @param destY destination y-coordinate.
*
* @return Returns a MoveStatus enum.
*/
public MoveStatus Move(int startX, int startY, int destX, int destY) {
return MoveWrapped(startX, startY, destX, destY, false);//call wrapped function not testing for checkmate
}
/**
* Helper function for move.
*
* @param startX starting y-coordinate.
* @param startY starting y-coordinate.
* @param destX destination x-coordinate.
* @param destY destination y-coordinate.
* @param testMate, true if testing for checkmate, false if testing for check.
*
* @return Returns a MoveStatus enum.
*/
private MoveStatus MoveWrapped(int startX, int startY, int destX, int destY, boolean testMate) {
if(startX<0 || startY<0 || startX>=gameBoard.width || startY>=gameBoard.height || destX<0 || destY<0 || destX>=gameBoard.width || destY>=gameBoard.height)
return MoveStatus.ERROR;
MoveStatus ret = validMove(startX, startY, destX, destY);
if(ret != MoveStatus.ERROR) {
//Check placement:
BoardSquare startSquare = gameBoard.getPiece(startX, startY);
BoardSquare destSquare = gameBoard.getPiece(destX, destY);
if (attemptMove_andTestCheck(startSquare, destSquare, testMate) == false)
return MoveStatus.ERROR;
//Move succeeded.
if(!testMate) {//if we were not checking for checkmate, it is the next players turn.
addToMoveHist(startX, startY, destX,destY, startSquare);
nextTurn();//Next player's turn.
}
}
return ret;
}
/**
* Tests the validity of a move.
*
* @param startX starting y-coordinate.
* @param startY starting y-coordinate.
* @param destX destination x-coordinate.
* @param destY destination y-coordinate.
*
* @return Returns a MoveStatus enum.
*/
public MoveStatus validMove(int startX, int startY, int destX, int destY) {
if(startX<0 || startY<0 || startX>=gameBoard.width || startY>=gameBoard.height || destX<0 || destY<0 || destX>=gameBoard.width || destY>=gameBoard.height)
return MoveStatus.ERROR;//return 0 if args are out of bounds.
BoardSquare startSquare = gameBoard.getPiece(startX, startY);
BoardSquare destSquare = gameBoard.getPiece(destX, destY);
if(!ignoreTurn) {
if (playerTurn == Player.WHITE && startSquare.owner != Player.WHITE)
return MoveStatus.ERROR;//return 0 if it is not our turn.
if (playerTurn == Player.BLACK && startSquare.owner != Player.BLACK)
return MoveStatus.ERROR;
}
//Test moving the Piece
if(!startSquare.MoveThis(gameBoard, startSquare, destSquare))
return MoveStatus.ERROR;//move invalid
return testPlacement(destSquare, startSquare);
}
/**
* Tests placement of the Piece at sourceSquare, placing the Piece at x,y
*
* @param destSquare destination Pieces.BoardSquare
* @param sourceSquare source Pieces.BoardSquare
*
* @return Returns a MoveStatus enum.
*/
private MoveStatus testPlacement(BoardSquare destSquare, BoardSquare sourceSquare) {
if(destSquare == null || sourceSquare == null)
return MoveStatus.ERROR;
if(sourceSquare.owner == destSquare.owner)
return MoveStatus.ERROR;
if(destSquare.owner == Player.NONE)
return MoveStatus.NONE;
return MoveStatus.CAPTURE;//Piece captured
}
/**
* Additional helper function, After a move was determined valid, attempt the move and test for the check condition
*
* @param destSquare destination Pieces.BoardSquare
* @param startSquare source Pieces.BoardSquare
* @param testMate true if testing for checkmate, false otherwise.
*
* @return Returns a boolean if the move does not place the player into check.
*/
private boolean attemptMove_andTestCheck(BoardSquare startSquare, BoardSquare destSquare, boolean testMate){
Player start_player = startSquare.owner;
Player dest_player = destSquare.owner;
boolean startHasMoved = startSquare.hasMoved;
gameBoard.setPiece(destSquare.x, destSquare.y, startSquare, startSquare.owner);//Make the move
gameBoard.setBlank(startSquare.x, startSquare.y);
//If this move places the owner in check, rollback.
if(((testMate || testCheck(false) == start_player) && testCheck(false) != Player.NONE)) {//You can not make this move, it would place you in check.
gameBoard.setPiece(destSquare.x, destSquare.y, destSquare, dest_player);
gameBoard.setPiece(startSquare.x, startSquare.y, startSquare, start_player);
gameBoard.getPiece(startSquare.x, startSquare.y).hasMoved = startHasMoved;//@todo refactor setting hasMoved
return false;
}
//if the move is allowed, and we are testing for check, rollback since this was just a simulation
if(testMate){
gameBoard.setPiece(destSquare.x, destSquare.y, destSquare, dest_player);
gameBoard.setPiece(startSquare.x, startSquare.y, startSquare, start_player);
gameBoard.getPiece(startSquare.x, startSquare.y).hasMoved = startHasMoved;
}
return true;
}
/**
* Test if the game situation is check or checkmate.
*
* @param testMate true if testing for checkmate, false if testing for check.
*
* @return Returns owner id of the owner in check or checkmate, else returns -1.
*/
public Player testCheck(boolean testMate){
if(!testMate)//If we are only checking for check, ignore who's turn it is to check ALL possible moves.
ignoreTurn = true;
for(int sx = 0; sx < gameBoard.width; sx++){
for(int sy = 0; sy < gameBoard.height; sy++) {
for(int x = 0; x < gameBoard.width; x++) {
for(int y = 0; y < gameBoard.height; y++) {
BoardSquare p = gameBoard.getPiece(x, y);
//If we are checking for checkmate, simulate a move for each Piece to see if the Pieces.King gets out of check.
if (testMate && MoveWrapped(sx, sy, x, y, testMate) != MoveStatus.ERROR) {
System.out.println("Not checkmate");
ignoreTurn = false;
return Player.NONE;
//If we are checking for check, call validMove for each Piece with the king as destination.
} else if (p.symbol == 'K' && validMove(sx, sy, x, y) != MoveStatus.ERROR) {
ignoreTurn = false;
return p.owner;
}
}
}
}
}
ignoreTurn = false;
if(testMate) {//If we were checking for checkmate, return the owner who is in checkmate.
return currentTurn();
}
return Player.NONE;
}
/**
* Check if the game is in stale mate.
*
* @return returns true if the game is stale mate, false otherwise
*/
public boolean testStalemate(){
for(int sx = 0; sx < gameBoard.width; sx++){
for(int sy = 0; sy < gameBoard.height; sy++) {
for(int x = 0; x < gameBoard.width; x++) {
for(int y = 0; y < gameBoard.height; y++) {
if (MoveWrapped(sx, sy, x, y, true) != MoveStatus.ERROR) {
return false;
}
}
}
}
}
return true;
}
}