package models; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Map; import logic.AlgebraicConverter; import logic.Messages; import logic.Move; import timer.ChessTimer; import timer.TimerTypes; import utility.FileUtility; import ai.FakeMove; import com.google.common.collect.Lists; public class Game implements Serializable { public Game(String gameType, Board[] boards, List<Piece> whiteTeam, List<Piece> blackTeam, Rules whiteRules, Rules blackRules, Map<String, List<String>> whitePromotionMap, Map<String, List<String>> blackPromotionMap) { mGameType = gameType; mBoards = boards; mWhiteTimer = ChessTimer.createTimer(TimerTypes.NO_TIMER, null, 0, 0, false); mBlackTimer = ChessTimer.createTimer(TimerTypes.NO_TIMER, null, 0, 0, true); mWhiteTeam = whiteTeam; mBlackTeam = blackTeam; mWhiteRules = whiteRules; mBlackRules = blackRules; mWhitePromotionMap = whitePromotionMap; mBlackPromotionMap = blackPromotionMap; whiteRules.setGame(this); blackRules.setGame(this); for (Board board : mBoards) board.setGame(this); mStaleLegalDests = true; mHistory = Lists.newArrayList(); } /** * Call the afterMove method in the correct rules object * * @param move The Move that has just been performed */ public void afterMove(Move move) { if (isBlackMove()) getBlackRules().afterMove(move); else getWhiteRules().afterMove(move); } /** * Generate the legal destinations for every piece on the board. */ public void genLegalDests() { Piece[] threats = null; Piece movingObjectivePiece = null; Piece otherObjectivePiece = null; List<Piece> movingTeam = null; List<Piece> otherTeam = null; setStaleLegalDests(false); for (int i = 0; i < mBoards.length; i++) { for (int r = 0, c = 0; r < mBoards[i].getMaxRow(); r++) { for (c = 0; c < mBoards[i].getMaxCol(); c++) { if (mBoards[i].mSquares[r][c].getPiece() != null) { mBoards[i].mSquares[r][c].getPiece().genLegalDests( (isBlackMove() ? getBlackRules() : getWhiteRules()).getBoard(mBoards[i])); } } } } movingObjectivePiece = (isBlackMove()) ? mBlackRules.objectivePiece(true) : mWhiteRules.objectivePiece(false); movingTeam = (isBlackMove()) ? getBlackTeam() : getWhiteTeam(); otherObjectivePiece = (isBlackMove()) ? mBlackRules.objectivePiece(true) : mWhiteRules.objectivePiece(false); otherTeam = (isBlackMove()) ? getWhiteTeam() : getBlackTeam(); // Make sure the objective piece doesn't put himself in check if (movingObjectivePiece != null) { (movingObjectivePiece.isBlack() ? getBlackRules() : getWhiteRules()).cropLegalDests(movingObjectivePiece, movingObjectivePiece, movingTeam); } if (otherObjectivePiece != null) { (otherObjectivePiece.isBlack() ? getBlackRules() : getWhiteRules()).cropLegalDests(otherObjectivePiece, otherObjectivePiece, otherTeam); } if (movingObjectivePiece != null) { // Now see if any of the moves puts the objective piece in check and // are therefore illegal for (int i = 0; i < otherTeam.size(); i++) { if (otherTeam.equals(getWhiteTeam())) getWhiteRules().cropLegalDests(movingObjectivePiece, otherTeam.get(i), movingTeam); else getBlackRules().cropLegalDests(movingObjectivePiece, otherTeam.get(i), movingTeam); } } (isBlackMove() ? getBlackRules() : getWhiteRules()).adjustTeamLegalDestinations(movingTeam); // if the objective piece is in check, the legal moves list must be // modified accordingly if (movingObjectivePiece != null && movingObjectivePiece.isInCheck()) { if (getLastMove() != null) getLastMove().setCheck(true); threats = getThreats(movingObjectivePiece); switch (threats.length) { case 1: // there is only one threat, so another Piece could block, or // the King could move for (int i = 0; i < movingTeam.size(); i++) movingTeam.get(i).genLegalDestsSaveKing(movingObjectivePiece, threats[0]); break; case 2: // since there is two threats, the objective piece is the only // one who can get himself out of check. for (int i = 0; i < movingTeam.size(); i++) { Piece p = (movingTeam.get(i)); if (p != movingObjectivePiece) { p.getLegalDests().clear(); p.getGuardSquares().clear(); } } if (getLastMove() != null) getLastMove().setDoubleCheck(true); break; } } } public Rules getBlackRules() { return mBlackRules; } public List<Piece> getBlackTeam() { return mBlackTeam; } public ChessTimer getBlackTimer() { return mBlackTimer; } public Board[] getBoards() { return mBoards; } /** * Get an array of the captured Pieces for the specified team * * @param isBlack The team of which to find captured pieces * @return The Piece[] of captured Pieces */ public Piece[] getCapturedPieces(boolean isBlack) { Piece[] pows = null; List<Piece> team = (isBlack) ? getBlackTeam() : getWhiteTeam(); int count = 0; Piece piece = null; for (int i = 0; i < team.size(); i++) { if (team.get(i).isCaptured()) { count++; } } if (count > 0) { pows = new Piece[count]; count = 0; for (int i = 0; i < team.size(); i++) { piece = team.get(i); if (piece.isCaptured()) pows[count++] = piece; } } return pows; } /** * Get the Pieces of the specified team guarding the specified Square * * @param square The Square being guarded * @param isBlack The team guarding the Square * @return The Pieces guarding the Square */ public Piece[] getGuards(Square square, boolean isBlack) { Iterator<Piece> team = (isBlack) ? getBlackTeam().iterator() : getWhiteTeam().iterator(); List<Piece> attackers = Lists.newArrayList(); if (isStaleLegalDests()) genLegalDests(); Piece piece = null; while (team.hasNext()) { piece = team.next(); if (piece.isGuarding(square)) attackers.add(piece); } Piece[] guards = null; if (attackers.size() > 0) { guards = new Piece[attackers.size()]; guards = attackers.toArray(guards); } return guards; } public List<Move> getHistory() { return mHistory; } public Move getLastMove() { return mLastMove; } /** * Get a count of all legal moves for this turn. * * @return The number of legal moves this turn. */ public int getLegalMoveCount() { int count = 0; List<Piece> movingTeam = (isBlackMove()) ? getBlackTeam() : getWhiteTeam(); if (isStaleLegalDests()) genLegalDests(); for (int i = 0; i < movingTeam.size(); i++) count += (movingTeam.get(i)).getLegalDests().size(); return count; } /** * Get the specified objective piece * * @param isBlack The team from which to get the objective piece * @return The objective piece, if found */ public Piece getOtherObjectivePiece(boolean isBlack) { return isBlack ? mWhiteRules.objectivePiece(false) : mBlackRules.objectivePiece(true); } /** * Get the threats on the given piece * * @param piece The piece on which to assess threats * @return The pieces threatening the specified piece */ public Piece[] getThreats(Piece piece) { return getThreats(piece.getSquare(), !piece.isBlack()); } /** * Get the threats on the given piece * * @param threatened The piece on which to assess threats * @param attackerIsBlack The team of the attacker * @return The pieces threatening the specified piece */ private Piece[] getThreats(Square threatened, boolean attackerIsBlack) { Iterator<Piece> team = (attackerIsBlack) ? getBlackTeam().iterator() : getWhiteTeam().iterator(); List<Piece> attackers = Lists.newArrayList(); if (isStaleLegalDests()) genLegalDests(); Piece piece = null; while (team.hasNext()) { piece = team.next(); if (piece.isLegalAttack(threatened)) attackers.add(piece); } Piece[] threats = null; if (attackers.size() > 0) { threats = new Piece[attackers.size()]; threats = attackers.toArray(threats); return threats; } return threats; } public Rules getWhiteRules() { return mWhiteRules; } public List<Piece> getWhiteTeam() { return mWhiteTeam; } public ChessTimer getWhiteTimer() { return mWhiteTimer; } public boolean isBlackMove() { return mIsBlackMove; } /** * Determines if a unit threatens the King-type unit. * * @return If king-type unit is threatened. */ private boolean isCheck() { boolean check = false; if (getLastMove() != null) return getLastMove().isCheck(); if (isStaleLegalDests()) genLegalDests(); check = (isBlackMove()) ? getBlackRules().objectivePiece(true).isInCheck() : getWhiteRules().objectivePiece(false).isInCheck(); return check; } /** * Determines if the King-type unit cannot escape check. * * @return If the King-type unit is in Check mate. */ public boolean isCheckmate() { if (getLastMove() != null) return getLastMove().isCheckmate(); if (isStaleLegalDests()) genLegalDests(); if (getLegalMoveCount() == 0 && isCheck()) return true; else return false; } /** * Check if this game is classic chess * * @return If this game is classic chess */ public boolean isClassicChess() { return mGameType.equals(Messages.getString("classic")); //$NON-NLS-1$ } /** * Check if the given square is guarded by the given team * * @param square The square to assess * @param isBlack The team to assess * @return If the square is guarded by the given team */ public boolean isGuarded(Square square, boolean isBlack) { return (getGuards(square, isBlack) != null); } public boolean isStaleLegalDests() { return mStaleLegalDests; } /** * Determines if a player is unable to move. * * @return If game is over by Stale mate. */ public boolean isStalemate() { if (getLastMove() != null) return getLastMove().isStalemate(); if (isStaleLegalDests()) genLegalDests(); if (getLegalMoveCount() == 0 && !isCheck()) return true; else return false; } /** * Check threats for the given piece * * @param piece The piece to assess threats on * @return If the piece is threatened */ public boolean isThreatened(Piece piece) { return isThreatened(piece.getSquare(), !piece.isBlack()); } /** * Check threats for the given square * * @param sq The square to assess threats for * @param isBlack The team to assess threats for * @return If the square is threatened by the given team */ public boolean isThreatened(Square sq, boolean isBlack) { return (getThreats(sq, isBlack) != null); } /** * Move to the next turn */ public void nextTurn() { // we only need to track turns once, so always use the whiteRules setBlackMove(getWhiteRules().nextTurn()); } /** * Play a move in the Game * * @param move The Move to play * @throws Exception If the Move was illegal */ public void playMove(Move move) throws Exception { move.execute(); if (getHistory().contains(move)) return; getHistory().add(move); getBlackRules().checkEndOfGame(mBlackRules.objectivePiece(true)); getWhiteRules().checkEndOfGame(mWhiteRules.objectivePiece(false)); } /** * Revert to the previous turn */ public void prevTurn() { setBlackMove(getWhiteRules().prevTurn(mIsPlayback)); } /** * Saves game's state to file. * * @param dirName The directory in which to save the game * @param fileName The name for the game file * @param ACN Whether the game should be saved in ACN * @param inProgress */ public void saveGame(String fileName, boolean ACN, boolean inProgress) { try { if (!inProgress) { FileOutputStream f_out = new FileOutputStream(FileUtility.getCompletedGamesFile(fileName)); ObjectOutputStream out = new ObjectOutputStream(f_out); out.writeObject(this); out.close(); f_out.close(); if (ACN) AlgebraicConverter.convert(getHistory(), fileName); } else { FileOutputStream f_out = new FileOutputStream(FileUtility.getGamesInProgressFile(fileName)); ObjectOutputStream out = new ObjectOutputStream(f_out); out.writeObject(this); out.close(); f_out.close(); } } catch (Exception e) { e.printStackTrace(); } } public void setBlackMove(boolean isBlackMove) { mIsBlackMove = isBlackMove; } public void setLastMove(Move lastMove) { mLastMove = lastMove; } /** * @param staleLegalDests the staleLegalDests to set */ public void setStaleLegalDests(boolean staleLegalDests) { mStaleLegalDests = staleLegalDests; } /** * Set the timers for this game * * @param whiteTimer The timer for the white team * @param blackTimer The timer for the black team */ public void setTimers(ChessTimer whiteTimer, ChessTimer blackTimer) { mWhiteTimer = whiteTimer; mBlackTimer = blackTimer; } /** * Undo the afterMove events * * @param move The Move to undo */ public void undoAfterMove(Move move) { if (isBlackMove()) getBlackRules().undoAfterMove(move); else getWhiteRules().undoAfterMove(move); } /** * @param fakeMove A fake move * @return An actual move * @throws Exception If the move is not legal. */ public Move fakeToRealMove(FakeMove fakeMove) throws Exception { Board to = mBoards[fakeMove.mBoardIndex]; Board from = ((mIsBlackMove) ? mBlackRules : mWhiteRules).getBoard(to); return new Move(from, from.getSquare(fakeMove.mOriginRow, fakeMove.mOriginColumn), to.getSquare(fakeMove.mDestinationRow, fakeMove.mDestinationColumn), fakeMove.mPromotionPieceName); } /** * @param move An actual move * @return A fake move. */ public FakeMove moveToFakeMove(Move move) { int boardNum = (move.board == mBoards[0]) ? 0 : 1; String promoName = null; if (mHistory.get(mHistory.size() - 1).getPromoPiece() != null) promoName = mHistory.get(mHistory.size() - 1).getPromoPiece().getName(); return new FakeMove(boardNum, move.origin.getRow(), move.origin.getCol(), move.getDest().getRow(), move.getDest().getCol(), promoName); } public Map<String, List<String>> getWhitePromotionMap() { return mWhitePromotionMap; } public Map<String, List<String>> getBlackPromotionMap() { return mBlackPromotionMap; } public void setIsPlayback(boolean isPlayback) { mIsPlayback = isPlayback; } public boolean isPlayback() { return mIsPlayback; } private static final long serialVersionUID = 7291801823624891384L; private final Board[] mBoards; private final Rules mWhiteRules; private final Rules mBlackRules; private final String mGameType; private final List<Piece> mWhiteTeam; private final List<Piece> mBlackTeam; private final Map<String, List<String>> mWhitePromotionMap; private final Map<String, List<String>> mBlackPromotionMap; private ChessTimer mWhiteTimer; private ChessTimer mBlackTimer; private boolean mIsBlackMove; private boolean mIsPlayback; private boolean mStaleLegalDests; private List<Move> mHistory; private Move mLastMove; }