/* * Determines if the last move caused the game to end * * First, check if the king is in check, and store it in a boolean variable. * * If the king is in check, check if the attack is preventable. This is done * by seeing it the king can either move away safely or can neutralize all * attacks (both the primary attack and discoveryCheck if it occured). If the * king is in check and the conditions checking if the attempted capture * is preventable, checkmate has occured, otherwise the game is not over and the * King is simply in check. * * If the king is not in check, determine if any of the various draw conditions are * met, including by repetition, redundancy (x moves without a capture or pawn movement), * stalemate (where a player can not make a legal move) and by material (where neither side * has enough material to checkmate). * * If none of those conditions are met, the game can still be played, and is * considered to have normal status. */ private void IsGameOver(ChessMove.MoveStatusCodes moveType, BoardSquare directAttacker) { // Check if king is in check (discovered, normal) // Be sure to account for rook when castled, removed pawn in en passant // If one or the other, check for blocks, capture, move out of way // If both, check move out of way // If neither, check other pieces can move (stalemate) // Check draw by repetition, inaction (no capture/movement), and material // Set gameStatus internal variable as appropriate BoardSquare currKingPos = this.kingPos[(int)(whiteTurn ? Sides.white : Sides.black)]; BitBoard futureMoves = this.board[directAttacker].getAllMoves(this.board, (short)directAttacker.X, (short)directAttacker.Y); bool isDirectCheck = futureMoves.Contains(currKingPos); bool isDiscoverCheck; this.attackingPieces[(int)AttackTypes.discovery] = ExtrapolateAttack((short)this.startCoord.X, (short)this.startCoord.Y, (short)currKingPos.X, (short)currKingPos.Y); this.attackingPieces[(int)AttackTypes.regular] = isDirectCheck ? endCoord : BoardSquare.Empty; isDiscoverCheck = this.attackingPieces[(int)AttackTypes.discovery] != BoardSquare.Empty; if (!isDiscoverCheck) { BoardSquare lastKingPos = this.kingPos[(int)(whiteTurn ? Sides.black : Sides.white)]; if (moveType == ChessMove.MoveStatusCodes.queenCastle) { isDiscoverCheck = this[lastKingPos.Y, lastKingPos.X + 1].getAllMoves(this.board, (short)(this.endCoord.X + 1), (short)this.endCoord.Y).Contains(currKingPos); if (isDiscoverCheck) this.attackingPieces[(int)AttackTypes.discovery] = new BoardSquare(lastKingPos.X + 1, lastKingPos.Y); } else if (moveType == ChessMove.MoveStatusCodes.queenCastle) { isDiscoverCheck = this[lastKingPos.Y, lastKingPos.X - 1].getAllMoves(this.board, (short)(this.endCoord.X - 1), (short)this.endCoord.Y).Contains(currKingPos); if (isDiscoverCheck) this.attackingPieces[(int)AttackTypes.discovery] = new BoardSquare(lastKingPos.X - 1, lastKingPos.Y); } else if (moveType == ChessMove.MoveStatusCodes.enPassant) { // Check captured square this.attackingPieces[(int)AttackTypes.discovery] = ExtrapolateAttack((short)endCoord.X, (short)startCoord.Y, (short)currKingPos.X, (short)currKingPos.Y); isDiscoverCheck = this.attackingPieces[(int)AttackTypes.discovery] != BoardSquare.Empty; } } if (isDirectCheck || isDiscoverCheck) { this.gameStatus = GameStatusCodes.check; // If king can't move safely, check alternatives if (this.board[currKingPos].getAllMoves(this.board, (short)currKingPos.X, (short)currKingPos.Y) == BitBoard.Empty) { // Double attack, can only get out of it by moving if (isDirectCheck && isDiscoverCheck) this.gameStatus = GameStatusCodes.checkMate; else { BoardSquare attackSquare = BoardSquare.Empty; bool canBlockCap = false; if (isDirectCheck) attackSquare = this.attackingPieces[(int)AttackTypes.regular]; else attackSquare = this.attackingPieces[(int)AttackTypes.discovery]; // Check for blocking, capture foreach (BoardSquare sq in this.board) { Piece p = this.board[sq]; if (p.Colour == this.board[currKingPos].Colour) { BitBoard bbMoves = p.getAllMoves(this.board, (short)sq.X, (short)sq.Y); foreach (BoardSquare sqMove in bbMoves) { // Can block or capture if (sqMove == attackSquare || (!(board[sq] is King) && sqMove.IsBetweenPoints(currKingPos, attackSquare)) || board[sq] is Pawn && ((Pawn)board[sq]).MayEnPassant(this.board, (short)sq.X, (short)sq.Y, (short)sqMove.X, (short)sqMove.Y)) { canBlockCap = true; break; } } if (canBlockCap) break; } } if (!canBlockCap) this.gameStatus = GameStatusCodes.checkMate; } } } else { gameStatus = GameStatusCodes.normal; //Check for draw conditions (stalemate, repetition, inactivity (no capture/pawn), material if (this.positions.isDrawByMoves()) this.gameStatus = GameStatusCodes.forcedDraw; else if (this.lastMove.IsCapture && IsDrawByMaterial()) this.gameStatus = GameStatusCodes.drawByMaterial; else { bool isStalemate = true; char nextMoveCol = whiteTurn ? Piece.NOTATION_W : Piece.NOTATION_B; foreach (BoardSquare sq in this.board) if (this.board[sq].Colour == nextMoveCol && this.board[sq].getAllMoves(this.board, (short)sq.X, (short)sq.Y) != BitBoard.Empty) { isStalemate = false; break; } if (isStalemate) this.gameStatus = GameStatusCodes.stalemate; } } }
/* * The actions that take place when a player is checkmated. * * First, the game status is upated, followed by the ratings of each player. */ public void CheckMate(bool blackWon, GameStatusCodes gameStatus) { this.gameStatus = gameStatus; int player1Rating = PlayerWhite.Rating; this.blackWon = blackWon; if (blackWon) { PlayerWhite.CalcNewELO(PlayerBlack.Rating, 0); PlayerBlack.CalcNewELO(player1Rating, 1); } else { PlayerWhite.CalcNewELO(PlayerBlack.Rating, 1); PlayerBlack.CalcNewELO(player1Rating, 0); } }
/* * The actions that take place when a draw occurs * * First, the game status is upated to the code passed through as a parameter. * Then, the player ratings are updated accordingly. */ public void Draw(GameStatusCodes reasonForDraw) { gameStatus = reasonForDraw; int player1Rating = PlayerWhite.Rating; PlayerWhite.CalcNewELO(PlayerBlack.Rating, 0.5); PlayerBlack.CalcNewELO(player1Rating, 0.5); }
/* * Initialize all game-driven variables who's values must be retained for the duration * of the game, and are not set with every move (startXCoord, for example). */ public Game(Player p1, Player p2) { whiteTurn = true; kingPos = new BoardSquare[2] { new BoardSquare(4, 0), new BoardSquare(4, 7) }; gameStatus = GameStatusCodes.normal; attackingPieces = new BoardSquare[2] { BoardSquare.Empty, BoardSquare.Empty, }; allMoves = new List<ChessMove>(); positions = new BoardPositions(Game.DRAW_LIMIT, BoardPositions.DEFAULT_START); PlayerWhite = p1; PlayerBlack = p2; board = new Board(this); board[0, 0] = new Rook(Piece.NOTATION_W); board[0, 1] = new Knight(Piece.NOTATION_W); board[0, 2] = new Bishop(Piece.NOTATION_W); board[0, 3] = new Queen(Piece.NOTATION_W); board[0, 4] = new King(Piece.NOTATION_W); board[0, 5] = new Bishop(Piece.NOTATION_W); board[0, 6] = new Knight(Piece.NOTATION_W); board[0, 7] = new Rook(Piece.NOTATION_W); board[1, 0] = new Pawn(Piece.NOTATION_W); board[1, 1] = new Pawn(Piece.NOTATION_W); board[1, 2] = new Pawn(Piece.NOTATION_W); board[1, 3] = new Pawn(Piece.NOTATION_W); board[1, 4] = new Pawn(Piece.NOTATION_W); board[1, 5] = new Pawn(Piece.NOTATION_W); board[1, 6] = new Pawn(Piece.NOTATION_W); board[1, 7] = new Pawn(Piece.NOTATION_W); board[7, 0] = new Rook(Piece.NOTATION_B); board[7, 1] = new Knight(Piece.NOTATION_B); board[7, 2] = new Bishop(Piece.NOTATION_B); board[7, 3] = new Queen(Piece.NOTATION_B); board[7, 4] = new King(Piece.NOTATION_B); board[7, 5] = new Bishop(Piece.NOTATION_B); board[7, 6] = new Knight(Piece.NOTATION_B); board[7, 7] = new Rook(Piece.NOTATION_B); board[6, 0] = new Pawn(Piece.NOTATION_B); board[6, 1] = new Pawn(Piece.NOTATION_B); board[6, 2] = new Pawn(Piece.NOTATION_B); board[6, 3] = new Pawn(Piece.NOTATION_B); board[6, 4] = new Pawn(Piece.NOTATION_B); board[6, 5] = new Pawn(Piece.NOTATION_B); board[6, 6] = new Pawn(Piece.NOTATION_B); board[6, 7] = new Pawn(Piece.NOTATION_B); }