/* Takes the board, current player, and current players king position and looks for checks on the king position from the opponent rook and diagonal queen. */ private bool RookQueenCheck(ChessPosition cpmCopy, Player curPlayer, Tuple<int, int> curPlayersKingPos, ref List<Tuple<int, int>> kingCheckedBy) { bool kingInCheck = false; EGamePieces oppositeRook = (curPlayer.PlayerValue == EGamePlayers.White) ? EGamePieces.BlackRook : EGamePieces.WhiteRook; EGamePieces oppositeQueen = (curPlayer.PlayerValue == EGamePlayers.White) ? EGamePieces.BlackQueen : EGamePieces.WhiteQueen; var adjRays = GetPieceRay(oppositeRook, curPlayersKingPos); // look through the rays until blocked, if find a rook or queen not owned by curPlayer // then this represents a discovered attack on current Player's king. foreach (var ray in adjRays) { foreach (var position in ray) { Tile tileAtPosition = cpmCopy.Board[position.Item1, position.Item2]; // if position not empty if (!tileAtPosition.IsEmpty()) { // if the position is occupied by an attacking bishop/ queen // add this to the kingcheckedbyvalue if (!curPlayer.Owns(tileAtPosition.piece)) { if (tileAtPosition.piece.Val == oppositeRook || tileAtPosition.piece.Val == oppositeQueen) { kingInCheck = true; kingCheckedBy.Add(position); } } // no more threats in this ray, move onto next rays to see if any other threats break; } } } return kingInCheck; }
/* This function receives a move object and the chess game and checks if the move also includes a pawn promotion, if it does it prompts the player for the selection piece, and adds it to the FormedMove. If it does not it ends silently TODO: could change it back to return bool, ref the selection piece then add it to move in the calling code ~~style issue*/ private void MoveIncludesPawnPromotion(ref FormedMove move, ChessPosition cpm) { Piece mvPiece = cpm.Board[move.PosA.Item1, move.PosA.Item2].piece; Tuple<int, int> posB = move.PosB; // if moving piece is a pawn and posB isHighRank if ((mvPiece.Val == EGamePieces.WhitePawn || mvPiece.Val == EGamePieces.BlackPawn) && (IsHighRank(posB))) { // Safe invoke for popup onto main thread via viewRef PromotionSelectionPopup(ref move, mvPiece); } }
/* Takes the board, current player, and current players king position and looks for checks on the king position from the opponent Pawn. */ private bool PawnCheck(ChessPosition cpmCopy, Player curPlayer, Tuple<int, int> curPlayersKingPos, ref List<Tuple<int, int>> kingCheckedBy) { bool kingInCheck = false; // the code must look in the advance direction (own pawn / CURRENT) to see the tiles an OPPONENT pawn could be on EGamePieces currentPawn = (curPlayer.PlayerValue == EGamePlayers.White) ? EGamePieces.WhitePawn : EGamePieces.BlackPawn; EGamePieces opponentPawn = (curPlayer.PlayerValue == EGamePlayers.White) ? EGamePieces.BlackPawn : EGamePieces.WhitePawn; var pRays = GetPieceRayPawnCapture(currentPawn, curPlayersKingPos); // look through the rays until blocked, if find a Pawn not owned by curPlayer // then this represents a discovered attack on current Player's king. foreach (var ray in pRays) { foreach (var position in ray) { Tile tileAtPosition = cpmCopy.Board[position.Item1, position.Item2]; // if position not empty if (!tileAtPosition.IsEmpty()) { // if the position is occupied by an attacking pawn // add this to the kingcheckedbyvalue if (!curPlayer.Owns(tileAtPosition.piece)) { if (tileAtPosition.piece.Val == opponentPawn) { kingInCheck = true; kingCheckedBy.Add(position); } } // no more threats in this ray, move onto next rays to see if any other threats break; } } } return kingInCheck; }
/* Takes a move object and a chess board object and returns true if the piece on the B position of the move belongs to the other player. TODO: replace with alternate call to previous functions*/ private bool IsMoveBPieceOtherPlayer(FormedMove move, ChessPosition cpm, ref Tile tileB) { bool result = false; tileB = cpm.Board[move.PosB.Item1, move.PosB.Item2]; if (!tileB.IsEmpty()) if (!cpm.Player.Owns(tileB.piece)) result = true; return result; }
/* Takes a move which so far meets the requirements for a Movement type move on the chess board. This function checks that one of the moving piece's rays covers this move object (The piece itself can move in this direction/way) and that the way is not blocked by another piece. */ private bool IsPieceMovementLegal(FormedMove move, ChessPosition cpm) { bool isLegalMovement = true; Tuple<int, int> posA = move.PosA; Tuple<int, int> posB = move.PosB; Tile tileA = cpm.Board[posA.Item1, posA.Item2]; List<List<Tuple<int, int>>> movementRays; // get the rays for a piece of type=piece.Val and location=posA movementRays = GetPieceRay(tileA.piece.Val, posA); ; List<Tuple<int, int>> moveRayUsed = null; foreach (List<Tuple<int, int>> ray in movementRays) { // for pawns the rays are comprise TWO forward positions (from first move) // so if the pawn has already .MovedOnce don not consider the second position in the ray // as the pawn can no longer legally move to that position if ((tileA.piece.Val == EGamePieces.WhitePawn || tileA.piece.Val == EGamePieces.BlackPawn) && (tileA.piece.MovedOnce)) { ; if (ray[0].Equals(posB)) { ; moveRayUsed = ray; break; } } // for all other cases consider the entirety of the ray else { if (ray.Contains(posB)) { moveRayUsed = ray; break; } } } if (moveRayUsed != null) // check there are no intermediate Pieces blocking the movement foreach (Tuple<int, int> tilePos in moveRayUsed) { Tile tileAtPosition = cpm.Board[tilePos.Item1, tilePos.Item2]; if (!tileAtPosition.IsEmpty()) { isLegalMovement = false; break; } if (tilePos.Equals(posB)) break; } else isLegalMovement = false; return isLegalMovement; }
/* Takes a move object and a chess board object and returns true if the piece on the A position of the move belongs to the current player. */ private bool IsMoveAPieceCurPlayer(FormedMove move, ChessPosition cpm, ref Tile tileA) { bool result = false; tileA = cpm.Board[move.PosA.Item1, move.PosA.Item2]; if (!tileA.IsEmpty()) if (cpm.Player.Owns(tileA.piece)) result = true; return result; }
/* Takes a move object and a chess board object and returns true if the tile on the B position of the move is empty. */ private bool IsMoveBEmpty(FormedMove move, ChessPosition cpm, ref Tile tileB) { tileB = cpm.Board[move.PosB.Item1, move.PosB.Item2]; return tileB.IsEmpty(); }
/* Takes a move which so far meets the requirements for an EnPassant type move on the chess board. This function checks that one of the moving pawn's diagonal rays covers this move object (The piece itself can move in this direction/way) and that the corresponding enpassant capture tile for this movement contains an enemy pawn piece to capture.*/ private bool IsEnPassantCaptureLegal(FormedMove move, ChessPosition cpm) { bool isLegalEnPassantCapture = true; Tuple<int, int> posA = move.PosA; Tuple<int, int> posB = move.PosB; Tile tileA = cpm.Board[posA.Item1, posA.Item2]; List<List<Tuple<int, int>>> epMovementRays; // get attack ray for the pawn on tileA // these are the movement rays in the case of EP epMovementRays = GetPieceRayPawnCapture(tileA.piece.Val, posA); List<Tuple<int, int>> moveRayUsed = null; foreach(List<Tuple<int, int>> ray in epMovementRays) { if (ray.Contains(posB)) { moveRayUsed = ray; break; } } // so at this point: // have posA and posB and it is a valid enpassant-type // movement (diagonal to empty square) if (moveRayUsed != null) { // continue checking ep position int epX = posB.Item2; int epY = posA.Item1; Tuple<int, int> posEP = Tuple.Create(epY, epX); // if this computed ep square between posA and posB does not // match the currently valid EnPassantSq, then this is not // a valid EP capture if (!posEP.Equals(cpm.EnPassantSq)) isLegalEnPassantCapture = false; } else isLegalEnPassantCapture = false; return isLegalEnPassantCapture; }
/* Takes a move which so far meets the requirements for a Capture type move on the chess board. This function checks that the one of the capturer piece's rays covers this move object (The piece itself can move in this way) and that the way is not blocked by another piece en route. Note: This is very similar to the IsPieceMovementLegal function, only difference is that the check for pieces on a tile is moved after the check for the final tile (since the final tile will have a piece on it: the piece being captured, and dont want the code to consider that as an intermediate piece which would block the capture. Other difference is that if the capturer piece is of type pawn, a unique set of pawn capture rays are gotten (Pawn only piece to use different rays for capture and movement). */ private bool IsCaptureLegal(FormedMove move, ChessPosition cpm) { bool isLegalCapture = true; Tuple<int, int> posA = move.PosA; Tuple<int, int> posB = move.PosB; Tile tileA = cpm.Board[posA.Item1, posA.Item2]; List<List<Tuple<int, int>>> captureRays; if (tileA.piece.Val == EGamePieces.BlackPawn || tileA.piece.Val == EGamePieces.WhitePawn) captureRays = GetPieceRayPawnCapture(tileA.piece.Val, posA); else captureRays = GetPieceRay(tileA.piece.Val, posA); List<Tuple<int, int>> captureRayUsed = null; foreach (List<Tuple<int, int>> ray in captureRays) { if (ray.Contains(posB)) { captureRayUsed = ray; break; } } if (captureRayUsed != null) // check there are no intermediate Pieces blocking the capture movement foreach (Tuple<int, int> position in captureRayUsed) { if (position.Equals(posB)) break; Tile tileAtPosition = cpm.Board[position.Item1, position.Item2]; if (!tileAtPosition.IsEmpty()) { isLegalCapture = false; break; } } else isLegalCapture = false; return isLegalCapture; }
/* Takes the board and the current player and returns the position of the current player's king. */ private Tuple<int, int> GetCurPlayersKingPos(ChessPosition cpmCopy, Player curPlayer) { Tuple<int, int> curPlayersKingPos = null; for (int row = 0; row<cpmCopy.Size; row ++) { for (int col = 0; col<cpmCopy.Size; col ++) { Tile tile = cpmCopy.Board[row, col]; if (!tile.IsEmpty()) { if ((curPlayer.Owns(tile.piece)) && (tile.piece.Val == EGamePieces.WhiteKing || tile.piece.Val == EGamePieces.BlackKing)) { curPlayersKingPos = Tuple.Create(row, col); break; } } } } return curPlayersKingPos; }
/* This function is passed a copy of the chess game. It uses the information contained in this object to determine whether or not the Current player's king is being threatened by check from the other player returning true if so. If it is being checked by the opposing player a list of the one / two coords containing the attacking pieces is stored in 'kingCheckedBy */ public bool IsKingInCheck(ChessPosition cpmCopy, ref List<Tuple<int, int>> kingCheckedBy) { bool kingInCheck = false; Player curPlayer = cpmCopy.Player; Tuple<int, int> curPlayersKingPos = GetCurPlayersKingPos(cpmCopy, curPlayer); // using current player's king position, generate all attack vectors if (curPlayersKingPos != null) { // check for threats from the pieces of the opponent if (BishopQueenCheck(cpmCopy, curPlayer, curPlayersKingPos, ref kingCheckedBy) || RookQueenCheck(cpmCopy, curPlayer, curPlayersKingPos, ref kingCheckedBy) || KnightCheck(cpmCopy, curPlayer, curPlayersKingPos, ref kingCheckedBy) || KingCheck(cpmCopy, curPlayer, curPlayersKingPos, ref kingCheckedBy) || PawnCheck(cpmCopy, curPlayer, curPlayersKingPos, ref kingCheckedBy)) kingInCheck = true; } return kingInCheck; }
/* Returns a ChessPosition object which is a deep copy of this one. Since this method is also inherited by the ChessPositionModel, if the CPM produces this object it will be a deep copy of the ChessPositionModel object as if it were a ChessPosition. (CPM minus handlers etc). This function will be used during evaluation in the king check phase, and will also be used during the move search of the ai function. */ public ChessPosition getChessPositionCopy() { int dimCopy = Size; // deep copy the board manually // Tile[,] boardCopy = new Tile[dimCopy, dimCopy]; for (int j = 0; j < dimCopy; j ++) { for (int k = 0; k < dimCopy; k ++) { Tile tileCopy = new Tile(); if (!Board[j,k].IsEmpty()) { Piece piece = Board[j, k].piece; EGamePieces val = piece.Val; bool mv = piece.MovedOnce; Piece pieceCopy = new Piece(val); pieceCopy.MovedOnce = mv; tileCopy.piece = pieceCopy; } boardCopy[j, k] = tileCopy; } } // copy rest of ChessPosition properties Player playerCopy = new Player(this.Player.PlayerValue); Dictionary<char, bool> castleCopy = new Dictionary<char, bool>(Castle); Tuple<int, int> enPassantSqCopy = (EnPassantSq == null) ? null : Tuple.Create(EnPassantSq.Item1, EnPassantSq.Item2); int halfmoveClockCopy = HalfMoveClock; List<EGamePieces> piecesCapdCopy = new List<EGamePieces>(PiecesCapd); // create and return the copy ChessPosition cpCopy = new ChessPosition(dimCopy, boardCopy, playerCopy, castleCopy, enPassantSqCopy, halfmoveClockCopy, piecesCapdCopy); return cpCopy; }