package logic; import java.io.Serializable; import java.util.List; import models.Board; import models.Piece; import models.Square; import timer.ChessTimer; import utility.GuiUtility; /** * Move.java * * Class modeling a Move in a chess game. * * @author Drew Hannay, Daniel Opdyke & Alisa Maas * * CSCI 335, Wheaton College, Spring 2011 Phase 2 April 7, 2011 */ public class Move implements Serializable { /** * Generated Serial Version ID */ private static final long serialVersionUID = -7581503689590078817L; /** * Constant to indicate castling queen side */ public static final int CASTLE_QUEEN_SIDE = 89751354; /** * Constant to indicate castling king side */ public static final int CASTLE_KING_SIDE = 678945613; /** * The Board on which this Move is to be performed */ public Board board; /** * The Square the Move originates on */ public Square origin; /** * The Square the Move is going to */ private Square dest; /** * The Class to which this Move is promoting a Piece, if any */ protected String promo; /** * Boolean indicating whether this Move has been executed */ // TODO Remove? protected boolean executed; /** * The piece being moved */ private Piece piece; /** * The piece to which the moving piece was promoted */ protected Piece promotion; /** * The piece which was captured this Move */ private Piece captured; /** * The piece that was completely removed from the Board on this turn. Used * by the AfterMove class */ private Piece removed; /** * The enpassant column from the previous turn */ private int prevEnpassantCol; /** * Whether this Move is check */ protected boolean check; /** * Whether this Move is double check */ protected boolean doublecheck; /** * Whether this move is checkmate */ protected boolean checkmate; /** * Whether this move is stalemate */ protected boolean stalemate; /** * Whether this move is a castleQueenside */ protected boolean castleQueenside; /** * Whether this move is a castleKingside */ protected boolean castleKingside; /** * The Move played before this move */ protected Move prev; /** * Whether this Move has been verified as legal. */ private boolean verified; /** * The Result of this move, if this move was the end of the game */ protected Result result; /** * The old time from the white timer */ private long oldWhiteTime = -1; /** * The old time from the black timer */ private long oldBlackTime = -1; /** * The old direction of the white timer */ private int oldWhiteDirection = 1; /** * The old direction of the black timer */ private int oldBlackDirection = 1; /** * The uniqueness of the row and column of this move */ protected boolean[] unique = { false, false };// Row,Column /** * The old position of the square, if placed by opponent. */ private Square oldPos; /** * The pieces caught up in a potential atomic explosion. */ private Piece[] exploded; /** * Constructor * * @param board The Board on which this Move is to be performed * @param castle The type of castling to be performed * @throws Exception If this move was illegal */ public Move(Board board, int castle) throws Exception { this.board = board; switch (castle) { case CASTLE_QUEEN_SIDE: castleQueenside = true; break; case CASTLE_KING_SIDE: castleKingside = true; break; } if (!board.isLegalMove(this)) throw new Exception(Messages.getString("illegalMove")); //$NON-NLS-1$ } /** * Constructor * * @param board The Board on which this Move is to be performed * @param origin The origin Square for this Move * @param dest The destination Square for this Move * @throws Exception If this move was illegal */ public Move(Board board, Square origin, Square dest) throws Exception { this(board, origin, dest, null); } /** * Constructor This constructor should only be explicitly called when * reading in from Algebraic Chess Notation * * @param board The Board on which this Move is to be performed * @param origin The origin Square for this Move * @param dest The destination Square for this Move * @param promo The Class to which the moving Piece will be promoted * @throws Exception If this move was illegal */ public Move(Board board, Square origin, Square dest, String promo) throws Exception { this.board = board; this.origin = origin; setDest(dest); this.promo = promo; if (!board.isLegalMove(this)) throw new Exception(Messages.getString("illegalMove")); //$NON-NLS-1$ } /** * Execute the constructed Move. * */ public boolean execute() { prevEnpassantCol = board.getEnpassantCol(); if (board.getGame().isClassicChess()) { if (castleKingside) { if (board.isBlackTurn()) { origin = board.getSquare(8, 5); setDest(board.getSquare(8, 7)); } else { origin = board.getSquare(1, 5); setDest(board.getSquare(1, 7)); } } else if (castleQueenside) { if (board.isBlackTurn()) { origin = board.getSquare(8, 5); setDest(board.getSquare(8, 3)); } else { origin = board.getSquare(1, 5); setDest(board.getSquare(1, 3)); } } } setPiece(origin.getPiece()); if (!origin.isOccupied()) return false; // If there is no Piece to move, return unsuccessful if (getPiece().isBlack() != board.isBlackTurn()) return false; setCaptured(getDest().getPiece()); if (board.getGame().isClassicChess()) { if (origin.getPiece().getName().equals(Messages.getString("pawn")) && getCaptured() == null && origin.getCol() != getDest().getCol()) //$NON-NLS-1$ { setCaptured(board.getSquare(origin.getRow(), getDest().getCol()).getPiece()); } else { board.setEnpassantCol(Board.NO_ENPASSANT); } } // Take the captured Piece off the board if (getCaptured() != null) { getCaptured().setIsCaptured(true); Square capSquare = getCaptured().getSquare(); capSquare.setPiece(null); } // Only do enpassant and castling for Classic chess. if (board.getGame().isClassicChess()) { // Mark enpassant on the board if (origin.getPiece().getName().equals(Messages.getString("pawn")) && Math.abs(origin.getRow() - getDest().getRow()) == 2) //$NON-NLS-1$ { board.setEnpassantCol(origin.getCol()); } // Castling if (origin.getPiece().getName().equals(Messages.getString("king")) && origin.getPiece().getMoveCount() == 0) //$NON-NLS-1$ { Square rookOrigin; Square rookDest; // Long if (getDest().getCol() == 3) { // c rookOrigin = board.getSquare(origin.getRow(), 1); // a rookDest = board.getSquare(origin.getRow(), 4); // d rookDest.setPiece(rookOrigin.setPiece(null)); rookDest.getPiece().setMoveCount(rookDest.getPiece().getMoveCount() + 1); castleQueenside = true; } // Short else if (getDest().getCol() == 7) { // g rookOrigin = board.getSquare(origin.getRow(), 8); // h rookDest = board.getSquare(origin.getRow(), 6); // f rookDest.setPiece(rookOrigin.setPiece(null)); rookDest.getPiece().setMoveCount(rookDest.getPiece().getMoveCount() + 1); castleKingside = true; } } } unique = board.isDestUniqueForClass(getDest(), getPiece()); getDest().setPiece(origin.setPiece(null));// Otherwise, move the Piece. // Clear the list of legal destinations for the piece. Piece p = getDest().getPiece(); p.getLegalDests().clear(); p.getGuardSquares().clear(); p.setPinnedBy(null); p.setMoveCount(p.getMoveCount() + 1); List<Square> promoSquares = (board.isBlackTurn() ? board.getGame().getBlackRules() : board.getGame().getWhiteRules()) .getPromotionSquares(p); if (promoSquares != null && promoSquares.contains(getDest())) { promotion = (board.isBlackTurn() ? board.getGame().getBlackRules() : board.getGame().getWhiteRules()).promote(p, isVerified(), promo); } board.getGame().afterMove(this); if (isVerified() && prev == null) { oldWhiteTime = oldBlackTime = -1; } else { oldWhiteTime = board.getGame().getWhiteTimer().getRawTime(); oldBlackTime = board.getGame().getBlackTimer().getRawTime(); } if (ChessTimer.isWordTimer(board.getGame().getWhiteTimer())) { oldWhiteDirection = board.getGame().getWhiteTimer().getClockDirection(); oldBlackDirection = board.getGame().getBlackTimer().getClockDirection(); } prev = board.getGame().getLastMove(); board.getGame().setLastMove(this); board.getGame().setStaleLegalDests(true); if (!isVerified()) { board.getGame().genLegalDests(); } setVerified(true); if (!board.getGame().isPlayback()) { GuiUtility.getChessCrafter().getPlayGameScreen(board.getGame()).boardRefresh(board.getGame().getBoards()); board.getGame().nextTurn(); executed = true; } return true; } /** * @return the captured */ public Piece getCaptured() { return captured; } /** * @return the dest */ public Square getDest() { return dest; } /** * @return the piece */ public Piece getPiece() { return piece; } /** * @return the removed */ public Piece getRemoved() { return removed; } /** * Getter method for the Result of this Move * * @return The result of this Move */ public Result getResult() { return result; } /** * Check if this move is check * * @return If this move is check */ public boolean isCheck() { return check; } /** * Check if this move is checkmate * * @return If this move is checkmate */ public boolean isCheckmate() { return checkmate; } /** * Check if this move is double check * * @return If this move is double check */ public boolean isDoubleCheck() { return doublecheck; } /** * Check if this move is the end of the Game * * @return If this move is the end of the Game */ public boolean isEndOfGame() { return (checkmate || stalemate || result != null || result != Result.UNDECIDED); } /** * Check if this move is stalemate * * @return If this move is stalemate */ public boolean isStalemate() { return stalemate; } /** * @return the verified */ public boolean isVerified() { return verified; } /** * Convert a chess piece to it's corresponding character * * @param p The piece to convert * @return The character representing this piece */ public char pieceToChar(Piece p) { if (p.getName().equals(Messages.getString("knight"))) //$NON-NLS-1$ return AlgebraicConverter.KNIGHT; else return p.getName().charAt(0); } /** * @param captured the captured to set */ public void setCaptured(Piece captured) { this.captured = captured; } /** * Setter method for check * * @param b Whether this move is check */ public void setCheck(boolean b) { check = b; } /** * Setter method for checkmate * * @param b Whether this move is checkmate */ public void setCheckmate(boolean b) { checkmate = b; if (result == null && checkmate) { result = board.isBlackTurn() ? Result.BLACK_WIN : Result.WHITE_WIN; } else { result = null; } } /** * @param dest the dest to set */ public void setDest(Square dest) { this.dest = dest; } /** * Setter method for double check * * @param b Whether this move is double check */ public void setDoubleCheck(boolean b) { doublecheck = b; if (b) { check = b; } } /** * @param piece the piece to set */ public void setPiece(Piece piece) { this.piece = piece; } /** * @param removed the removed to set */ public void setRemoved(Piece removed) { this.removed = removed; } /** * Setter method for the Result of this Move * * @param r The result of this Move */ public void setResult(Result r) { result = r; } /** * Setter method for stalemate * * @param b Whether this move is stalemate */ public void setStalemate(boolean b) { stalemate = b; if (result == null && stalemate) { result = Result.DRAW; } else { result = null; } } /** * @param verified the verified to set */ public void setVerified(boolean verified) { this.verified = verified; } /** * Create a String representing this move in Algebraic Chess Notation * * @return The String representation of this Move */ @Override public String toString() { String s = ""; //$NON-NLS-1$ if (castleQueenside) { s = "O-O-O"; //$NON-NLS-1$ } else if (castleKingside) { s = "O-O"; //$NON-NLS-1$ } else { s = ((getPiece() != null && !(getPiece().getName().equals(Messages.getString("pawn")))) ? (pieceToChar(getPiece())) + "" : " ") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + origin.toString(unique) + ((getCaptured() != null) ? "x" : "") //$NON-NLS-1$ //$NON-NLS-2$ + getDest().toString(new boolean[] { false, false }); if (promotion != null) { s += "=" + pieceToChar(promotion); //$NON-NLS-1$ } if (checkmate) { s += "#"; //$NON-NLS-1$ } else { if (check) { s += "+"; //$NON-NLS-1$ } if (doublecheck) { s += "+"; //$NON-NLS-1$ } } } return s; } /** * Undo the execution of this move * * @throws Exception If the undo doesn't work properly */ public boolean undo() { board.setEnpassantCol(prevEnpassantCol); if (board.getGame().isClassicChess()) { // Castling if (getPiece().getName().equals(Messages.getString("king")) && getPiece().getMoveCount() == 1) //$NON-NLS-1$ { Square rookOrigin; Square rookDest; // Long if (getDest().getCol() == 3) {// c rookOrigin = board.getSquare(origin.getRow(), 1);// a rookDest = board.getSquare(origin.getRow(), 4);// d rookOrigin.setPiece(rookDest.setPiece(null)); rookOrigin.getPiece().setSquare(rookOrigin); rookOrigin.getPiece().setMoveCount(rookOrigin.getPiece().getMoveCount() - 1); } // Short else if (getDest().getCol() == 7) {// g rookOrigin = board.getSquare(origin.getRow(), 8);// h rookDest = board.getSquare(origin.getRow(), 6);// f rookOrigin.setPiece(rookDest.setPiece(null)); rookOrigin.getPiece().setSquare(rookOrigin); rookOrigin.getPiece().setMoveCount(rookOrigin.getPiece().getMoveCount() - 1); } } } List<Square> promoSquares = (board.isBlackTurn() ? board.getGame().getBlackRules() : board.getGame().getWhiteRules()) .getPromotionSquares(getPiece()); if (promoSquares != null && promoSquares.contains(getDest())) { (board.isBlackTurn() ? board.getGame().getBlackRules() : board.getGame().getWhiteRules()).undoPromote(promotion); } if (getDest().getPiece() != null) getDest().getPiece().setMoveCount(getDest().getPiece().getMoveCount() - 1); origin.setPiece(getDest().setPiece(null)); getPiece().setSquare(origin); // Put any captured Pieces back on the board. if (getCaptured() != null) { getCaptured().setIsCaptured(false); getCaptured().getSquare().setPiece(null); getDest().setPiece(getCaptured()); getCaptured().getLegalDests().clear(); } board.getGame().undoAfterMove(this); board.getGame().getWhiteTimer().setClockTime(oldWhiteTime); board.getGame().getBlackTimer().setClockTime(oldBlackTime); if (ChessTimer.isWordTimer(board.getGame().getWhiteTimer())) { board.getGame().getWhiteTimer().setClockDirection(oldWhiteDirection); board.getGame().getBlackTimer().setClockDirection(oldBlackDirection); } if (!board.getGame().isPlayback()) board.getGame().prevTurn(); board.getGame().setLastMove(prev); executed = false; board.getGame().setStaleLegalDests(true); if (!board.getGame().isPlayback()) GuiUtility.getChessCrafter().getPlayGameScreen(board.getGame()).boardRefresh(board.getGame().getBoards()); return true; } /** * Setter for oldPos. * * @param square The square to set oldPos to. */ public void setOldPos(Square square) { oldPos = square; } /** * Getter for oldPos * * @return oldPos */ public Square getOldPos() { return oldPos; } /** * Setter for exploded * * @param pieces The new value for exploded. */ public void setExploded(Piece[] pieces) { exploded = pieces; } /** * Getter for exploded. * * @return exploded */ public Piece[] getExploded() { return exploded; } /** * @return returns the piece that will replace the piece being promoted. */ public Piece getPromoPiece() { return promotion; } }