/// <summary> /// Checks if it's the en passant move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool IsEnPassantCaptureMove(Board board, int from, int to) { return (board.Status.EnPassantTarget != null && // there is a en passant target to == board.Status.EnPassantTarget.Value && Board.Rank(from) == 3 && Board.Rank(board.Status.EnPassantTarget.Value) == 2 && Math.Abs(Board.File(from) - Board.File(to)) == 1); // it's a diagonal move }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return (base.MightMove(board, from, to) && Math.Abs(Board.File(from) - Board.File(to)) == Math.Abs(Board.Rank(from) - Board.Rank(to)) && // it's a diagonal move board.IsPathClear(from, to)); // the path is clear }
/// <summary> /// Checks if it's the two-squares move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public bool IsTwoSquaresMove(Board board, int from, int to) { return (Board.Rank(from) == Board.SideSquareNo - 2 && Board.Rank(to) == Board.SideSquareNo - 4 && Board.File(from) == Board.File(to) && // the same file board[from - Board.SideSquareNo] == null && board[from - (Board.SideSquareNo << 1)] == null); // the first and second squares are empty }
/// <summary> /// Verifies if the piece attacks the "to" square, on this board, from the "from" square. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool Attacks(Board board, int from, int to) { // castling is not considered an attack // so the king attacks the squares around him return (base.MightMove(board, from, to) && Math.Abs(Board.Rank(from) - Board.Rank(to)) <= 1 && Math.Abs(Board.File(from) - Board.File(to)) <= 1); }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return (base.MightMove(board, from, to) && Board.File(from) != Board.File(to) && // the files are different Board.Rank(from) != Board.Rank(to) && // the ranks are different (Math.Abs(Board.File(from) - Board.File(to)) + Math.Abs(Board.Rank(from) - Board.Rank(to))) == 3); // the rank difference plus file difference must be 3 }
/// <summary> /// Verifies if the piece attacks the "to" square, on this board, from the "from" square. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool Attacks(Board board, int from, int to) { // a pawn attacks one square in front diagonaly // and only if it's an empty square or an opposite side piece return (Board.Rank(to) == Board.Rank(from) - 1 && // it's on the front row Math.Abs(Board.File(to) - Board.File(from)) == 1 && // it's a diagonal move !(board[to] is WhitePiece)); // it's not occupied by a same side piece }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return (base.MightMove(board, from, to) && ( Board.File(from) == Board.File(to) || // the same file Board.Rank(from) == Board.Rank(to) // the same rank ) && board.IsPathClear(from, to)); // the path is clear }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return (base.MightMove(board, from, to) && ( (Math.Abs(Board.Rank(from) - Board.Rank(to)) <= 1 && Math.Abs(Board.File(from) - Board.File(to)) <= 1) || // the king can move one square CanCastleLong(board, from, to) || // the king can castle long CanCastleShort(board, from, to) // the king can castle short )); }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return (base.MightMove(board, from, to) && ( Board.File(from) == Board.File(to) || // the same file Board.Rank(from) == Board.Rank(to) || // the same rank Math.Abs(Board.File(from) - Board.File(to)) == Math.Abs(Board.Rank(from) - Board.Rank(to)) // it's a diagonal move ) && board.IsPathClear(from, to)); }
/// <summary> /// Gets the square notation. /// </summary> /// <param name="position">The position</param> /// <returns></returns> public static string GetNotation(int position) { if (position < 0 || position > 63) { return(null); } StringBuilder sb = new StringBuilder(2); sb.Append((char)((Board.File(position)) + 'a')); sb.Append((char)('8' - Board.Rank(position))); return(sb.ToString()); }
/// <summary> /// Checks if the piece might move on this "board", /// from the "from" square to the "to" square according to the chess rules. /// It doesn't verify if its own king is in check after the move. /// </summary> /// <param name="board">The board</param> /// <param name="from">The starting square</param> /// <param name="to">The ending square</param> /// <returns></returns> public override bool MightMove(Board board, int from, int to) { return // a pawn can move one square in front to an empty square (( to == from - Board.SideSquareNo && // it's the front square board[to] == null // it's an empty square ) || // a pawn can move one square in front diagonally if there is an opposite side piece ( Board.Rank(to) == Board.Rank(from) - 1 && // it's on the front row Math.Abs(Board.File(to) - Board.File(from)) == 1 && // it's a diagonal move board[to] is BlackPiece // it's an opposite side piece ) || IsTwoSquaresMove(board, from, to) || // it's the two-squares move IsEnPassantCaptureMove(board, from, to)); // it's the en passant move }
/// <summary> /// Gets a move from its SAN (standard algebraic notation). /// Throws ArgumentException if it's not a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="san">The SAN string</param> /// <returns></returns> public static Move GetSANMove(ValilGame game, string san) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", Resources.NullGameMsg); } // the SAN string must not be null if (san == null) { throw new ArgumentNullException("san", Resources.NullSANMsg); } Move move = null; // if it's a short castling move if (san == "O-O") { foreach (Move m in game.PossibleMoves) { if (m is CastlingMove && (m.To == Board.G1 || m.To == Board.G8)) { move = m; break; } } } // if it's a long castling move else if (san == "O-O-O") { foreach (Move m in game.PossibleMoves) { if (m is CastlingMove && (m.To == Board.C1 || m.To == Board.C8)) { move = m; break; } } } else { int index = san.Length - 1; // remove chess and checkmate representation (if any) if (index > -1 && (san[index] == '+' || san[index] == '#')) { index--; } if (index < 1) { throw new ArgumentException(Resources.IllegalSANFormatMsg, "san"); } // get the promotion (if any) char prom = '\0'; if (san[index - 1] == '=') { prom = san[index]; index -= 2; } if (index < 1 || san[index - 1] < 'a' || san[index - 1] > 'h' || san[index] < '1' || san[index] > '8') { throw new ArgumentException(Resources.IllegalSANFormatMsg, "san"); } // get the ending square int to = Utils.GetPosition(san[index - 1], san[index]); index -= 2; // remove capture char (if any) if (index > -1 && san[index] == 'x') { index--; } // get the rank of the starting square (if any) int?rank = null; if (index > -1 && san[index] >= '1' && san[index] <= '8') { rank = GetRank(san[index]); index--; } // get the file of the starting square (if any) int?file = null; if (index > -1 && san[index] >= 'a' && san[index] <= 'h') { file = GetFile(san[index]); index--; } // get piece type char (if any) char pieceChar = 'P'; if (index > -1) { pieceChar = san[index]; index--; } // look into possible moves foreach (Move m in game.PossibleMoves) { if ( m.To == to && // the ending squares match (file == null || (file != null && Board.File(m.From) == file.Value)) && // the starting squares files match (if any) (rank == null || (rank != null && Board.Rank(m.From) == rank.Value)) && // the starting squares ranks match (if any) pieceTypeToUpperCharConversion[game.CurrentBoard[m.From].GetType()] == pieceChar // the piece type chars match ) { move = m; break; } } // if it's a promotion move, set the promotion if (move is PromotionMove) { (move as PromotionMove).PromotionType = GetPromotionType(game.CurrentBoard.Status.WhiteTurn, prom); } } if (move != null) { return(move); } else { throw new ArgumentException(Resources.IllegalSANMoveMsg, "san"); } }
/// <summary> /// Gets the begin of the SAN (standard algebraic notation) for a move - without promotion or check/checkmate representation - before the move is made /// without checking the status of the game or if it's a valid move. /// </summary> /// <param name="game">The game</param> /// <param name="move">The move</param> /// <returns></returns> public static string GetSANBegin(ValilGame game, Move move) { // the game must not be null if (game == null) { throw new ArgumentNullException("game", Resources.NullGameMsg); } // the move must not be null if (move == null) { throw new ArgumentNullException("move", Resources.NullMoveMsg); } StringBuilder sb = new StringBuilder(6); //if it's a castling move if (move is CastlingMove) { if (move.To == Board.G1 || move.To == Board.G8) { // short castling sb.Append("O-O"); } else if (move.To == Board.C1 || move.To == Board.C8) { // long castling sb.Append("O-O-O"); } } else { // add piece type and disambiguation char p = pieceTypeToUpperCharConversion[game.CurrentBoard[move.From].GetType()]; if (p == 'P') { // if the pawn captures, add the starting square file if (move.HasCapture) { sb.Append(GetFileNotation(move.From)); } } else { // add piece char sb.Append(p); // add disambiguation // disambigutationList will contain starting squares that contain the same // type of piece and that can move to the same ending square as "move" List <int> disambiguationList = new List <int>(5); foreach (Move m in game.PossibleMoves) { if (m.To == move.To && m.From != move.From && game.CurrentBoard[m.From].ToString() == game.CurrentBoard[move.From].ToString()) { disambiguationList.Add(m.From); } } if (disambiguationList.Count > 0) { // see if the file is unique bool isFileUnique = true; foreach (int from in disambiguationList) { if (Board.File(move.From) == Board.File(from)) { isFileUnique = false; break; } } if (isFileUnique) { // insert file sb.Append(Utils.GetFileNotation(move.From)); } else { // see if the rank is unique bool isRankUnique = true; foreach (int from in disambiguationList) { if (Board.Rank(move.From) == Board.Rank(from)) { isRankUnique = false; break; } } if (isRankUnique) { // insert rank sb.Append(Utils.GetRankNotation(move.From)); } else { // both file and rank are not unique, insert both of them sb.Append(Utils.GetNotation(move.From)); } } } } // if there is a capture, add capture notation if (move.HasCapture) { sb.Append('x'); } // add destination square sb.Append(GetNotation(move.To)); } return(sb.ToString()); }
/// <summary> /// Computes the board hash using Zobrist method. /// </summary> /// <param name="board">The board</param> /// <returns></returns> public static int GetHash(Board board) { // the board must not be null if (board == null) { throw new ArgumentNullException("board", Resources.NullBoardMsg); } long hash = 0; // loop through the squares for (int sqIndex = 0; sqIndex < Board.SquareNo; sqIndex++) { // if the square is not empty if (board[sqIndex] != null) { // XOR the Zobrist key which coresponds to type of piece and for this square // to find the key index, offset the key index of the type by the square number hash ^= zobristKeys[pieceZobristIndexTable[board[sqIndex].GetType()] + sqIndex]; } } // if White is to move, XOR the corresponding Zobrist key if (board.Status.WhiteTurn) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo]; } // if the Kings could castle, the corresponding Zobrist keys if (board.Status.WhiteCouldCastleLong) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 1]; } if (board.Status.WhiteCouldCastleShort) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 2]; } if (board.Status.BlackCouldCastleLong) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 3]; } if (board.Status.BlackCouldCastleShort) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 4]; } // if there is an en passant target, XOR the corresponding Zobrist key if (board.Status.EnPassantTarget != null) { // the en passant targets have rank 2 or 5 if (Board.Rank(board.Status.EnPassantTarget.Value) == 2) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 5 + Board.File(board.Status.EnPassantTarget.Value)]; } else if (Board.Rank(board.Status.EnPassantTarget.Value) == 5) { hash ^= zobristKeys[Piece.TypesNo * Board.SquareNo + 13 + Board.File(board.Status.EnPassantTarget.Value)]; } } // XOR the first 4 bytes with the last 4 bytes to return an 32-bit integer return((int)((hash & 0xFFFFFFFF) ^ (hash >> 32))); }
/// <summary> /// Gets the file char for a square. /// </summary> /// <param name="position">The position</param> /// <returns></returns> private static char GetFileNotation(int position) { return((char)((Board.File(position)) + 'a')); }