public static String AsciiBoard(Board board, List <Move> moves = null, bool displayCount = false) { if (moves == null) { moves = new List <Move>(); } moves = moves.Where(move => board.IsLegalMove(move)).ToList(); StringBuilder ascii = new StringBuilder(); ascii.Append("+---------------+\n"); for (int row = 7; row >= 0; row--) { ascii.Append("|"); for (int column = 0; column < 8; column++) { var position = row * BoardStateOffset.ROW_OFFSET + column; var piece = board.GetPiece(position); var movesOnPosition = moves.Count(move => move.targetPosition == position); if (movesOnPosition > 0) { if (movesOnPosition < 10 && displayCount) { ascii.Append(movesOnPosition); } else { ascii.Append("x"); } } else { ascii.Append(PieceParser.ToChar(piece)); } if (column != 7) { ascii.Append(" "); } } ascii.Append($"| {(row + 1)}\n"); } ascii.Append("+---------------+\n"); ascii.Append(" A B C D E F G H"); return(ascii.ToString()); }
public static String BoardToFen(Board board, int move = 0) { StringBuilder fen = new StringBuilder(); for (int row = 7; row >= 0; row--) { int count = 0; for (int column = 0; column < 8; column++) { var position = row * BoardStateOffset.ROW_OFFSET + column; var piece = board.GetPiece(position); //var piece = Board.GetPiece(board, position); char c = PieceParser.ToChar(piece); if (c == '_') { count++; } else { if (count != 0) { fen.Append(count); count = 0; } fen.Append(c); } } if (count != 0) { fen.Append(count); } if (row != 0) { fen.Append("/"); } } fen.Append(" "); fen.Append(board.IsWhiteTurnBool ? "w" : "b"); fen.Append(" "); CastlingBits castlingBits = board.CastlingBits; if (castlingBits == CastlingBits.EMPTY) { fen.Append("-"); } else { if ((castlingBits & CastlingBits.WHITE_KING_SIDE_CASTLE) != CastlingBits.EMPTY) { fen.Append("K"); } if ((castlingBits & CastlingBits.WHITE_QUEEN_SIDE_CASTLE) != CastlingBits.EMPTY) { fen.Append("Q"); } if ((castlingBits & CastlingBits.BLACK_KING_SIDE_CASTLE) != CastlingBits.EMPTY) { fen.Append("k"); } if ((castlingBits & CastlingBits.BLACK_QUEEN_SIDE_CASTLE) != CastlingBits.EMPTY) { fen.Append("q"); } } fen.Append(" "); fen.Append(board.EnPassantTarget != EnPassant.NO_ENPASSANT ? BoardPosition.ReadablePosition(board.EnPassantTarget) : "-"); fen.Append(" "); fen.Append(board.HalfTurnCounter); fen.Append(" "); fen.Append(move); return(fen.ToString()); }
public static ChessState GameToState(ChessGame game) { var chessState = new ChessState(); //var moves = board.GetMoves().Where(move => board.IsLegalMove(move)); var moves = game.Moves(); for (int column = 0; column < 8; column++) { for (int row = 0; row < 8 * BoardStateOffset.ROW_OFFSET; row += BoardStateOffset.ROW_OFFSET) { var position = row + column; Piece piece = game.board.GetPiece(position); if (piece != Piece.EMPTY) { var from = BoardPosition.x88PositionToCoordinate(position); var chessPiece = new ChessPiece() { piece = PieceParser.ToChar(piece).ToString(), row = from.row, column = from.column, isWhite = (piece & Piece.IS_WHITE) == Piece.IS_WHITE }; chessState.pieces.Add(chessPiece); foreach (var move in moves) { if (move.move.fromPosition == position) { var to = BoardPosition.x88PositionToCoordinate(move.move.targetPosition); chessPiece.options.Add(new PieceOption() { row = to.row, column = to.column, isPromotion = (Piece)move.move.promotion != Piece.EMPTY, isCastle = ((MoveFlags)move.move.moveFlags & MoveFlags.CASTLING) == MoveFlags.CASTLING, isEnpassant = (((MoveFlags)move.move.moveFlags & MoveFlags.ENPASSANT) == MoveFlags.ENPASSANT), san = move.san }); } } } } } var winner = game.Winner(); if (winner == Winner.DRAW) { chessState.isDraw = true; } else if (winner == Winner.WINNER_BLACK) { chessState.blackWins = true; } else if (winner == Winner.WINNER_WHITE) { chessState.whiteWins = true; } chessState.fen = game.FEN; return(chessState); }
// based on rules from https://en.wikipedia.org/wiki/Algebraic_notation_(chess) // Notice the parameter legalMoves only contains legal moves public string StandardAlgebraicNotation(Move move, List <Move> legalMoves) { StringBuilder san = new StringBuilder(); var piece = GetPiece(move.fromPosition); if (((MoveFlags)move.moveFlags & MoveFlags.CASTLING) == MoveFlags.CASTLING) { if (move.targetPosition < move.fromPosition) { // castle queen side san.Append("O-O-O"); } else { // castle king side san.Append("O-O"); } } else { var isPawn = (piece & Piece.PIECE_MASK) == Piece.PAWN; if (!isPawn) { // all pieces other pawns display their piece name var pChar = PieceParser.ToChar(piece); san.Append(pChar.ToString().ToUpper()); } int fromRow = move.fromPosition / BoardStateOffset.ROW_OFFSET; int fromColumn = move.fromPosition - (fromRow * BoardStateOffset.ROW_OFFSET); bool sameColumns = false; bool sameRows = false; bool isAmbigious = false; foreach (var possibleMove in legalMoves) { // check all other moves of the same piece type if they can move to the same position if (possibleMove.targetPosition == move.targetPosition && // check if it can reach the same square possibleMove.fromPosition != move.fromPosition && // check if not the same piece piece == GetPiece(possibleMove.fromPosition)) // check it is the same type of piece { isAmbigious = true; int possibleFromRow = possibleMove.fromPosition / BoardStateOffset.ROW_OFFSET; int possibleFromColumn = possibleMove.fromPosition - (possibleFromRow * BoardStateOffset.ROW_OFFSET); if (possibleFromColumn == fromColumn) { sameColumns = true; } if (possibleFromRow == fromRow) { sameRows = true; } } } if (isPawn) { if ((Piece)move.capturedPiece != Piece.EMPTY) { // when pawn captures always specify the starting column san.Append(Convert.ToChar('a' + (fromColumn))); } } else { if (isAmbigious) { // disambiguating moves if (sameColumns && sameRows) { san.Append(Convert.ToChar('a' + (fromColumn))); san.Append((fromRow + 1).ToString()); } else if (sameColumns) { san.Append((fromRow + 1).ToString()); } else { san.Append(Convert.ToChar('a' + (fromColumn))); } } } if ((Piece)move.capturedPiece != Piece.EMPTY) { san.Append("x"); } int targetRow = move.targetPosition / BoardStateOffset.ROW_OFFSET; int targetColumn = move.targetPosition - (targetRow * BoardStateOffset.ROW_OFFSET); san.Append(Convert.ToChar('a' + (targetColumn))).Append((targetRow + 1).ToString()); Piece promotion = (Piece)move.promotion; if (promotion != Piece.EMPTY) { san.Append("=").Append(PieceParser.ToChar(promotion).ToString().ToUpper()); } } Move(move); // check for checkmate // after the move is played check if the current players king is attacked if (Attacked(GetKingPosition(IsWhiteTurn), IsWhiteTurn)) { var winner = detectWinner(GetMoves()); if (winner == Winner.WINNER_BLACK || winner == Winner.WINNER_WHITE) { //san += "#"; san.Append("#"); } else { //san += "+"; san.Append("+"); } } UndoMove(move); return(san.ToString()); }
// Loads a board from the FEN notation(Forsyth–Edwards Notation) // Useful for getting to certain chess positions quickly. // Use the website below to generate positions // https://lichess.org/editor/8/1QQ2QQ1/QqqQQqqQ/QqqqqqqQ/QqqqqqqQ/1QqqqqQ1/2QqqQ2/3QQ3_b_-_-_0_1 // FEN consists of 6 parts which are sepperated by space // 1. Position // - Describes what pieces are at which positions // - The pieces are described from left to right with a "/" to indicate row changes // - a single letter is used to represent a piece // - P = Pawn // - N = Knight // - B = Bishop // - R = Rook // - Q = Queen // - K = King // - Capital letters indicate White pieces, while lowercase letter indicate Black pieces // 2. active color // - Either "w" or "b", which indicates whose turn it is. // 3. castling options // - stores information if about who is allowed to castle. // - Capital letters indicate White side lowercase letters are blacks options // - the letters K and Q mean queen or kingside castle. // - If no castling options it is marked with a "-" // 4. En passant target // - marks which square is current possible to attack with en passant. // 5. Half move clock // - Stores how many half moves have been made since the last capture or pawn move // - This is used for declaring stalemate // 6. Fullmove number // - Counts how many full moves have been made // - full moves are not a part of Board since it is not required to play legal chess, it will be handled by the Chess class public static Board LoadBoardFromFen(out int move, String fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") { Board board = new Board(new byte[BoardStateOffset.BOARD_STATE_SIZE]); var sections = fen.Split(" "); if (sections.Length != 6) { throw new ArgumentException("FEN has to contanin 6 sections"); } string positions = sections[0]; string activeColor = sections[1]; string castlingOptions = sections[2]; string enPassantAttackedSquare = sections[3]; string halfMoveClock = sections[4]; string fullMoveClock = sections[5]; int square = BoardStateOffset.A8; for (var i = 0; i < positions.Length; i++) { char piece = positions[i]; if (piece == '/') { square -= 24; } else if (Char.IsDigit(piece)) { square += int.Parse(piece.ToString()); } else { Piece parsedPiece = PieceParser.FromChar(piece); //Board.PutPiece(board, square, parsedPiece); board.SetPiece(square, parsedPiece); if ((parsedPiece & Piece.PIECE_MASK) == Piece.KING) { //Board.SetKingPosition(board, (parsedPiece & Piece.IS_WHITE) == Piece.IS_WHITE, square); board.SetKingPosition((int)(parsedPiece & Piece.IS_WHITE), (byte)square); } square++; } } //Board.SetIsWhitesTurn(board, activeColor == "w"); board.IsWhiteTurnBool = activeColor == "w"; if (castlingOptions.Contains("K") && board.WhiteKingPosition == BoardStateOffset.E1 && board.GetPiece(BoardStateOffset.H1) == (Piece.ROOK | Piece.IS_WHITE)) { //Board.SetCastleBit(board, CastlingBits.WHITE_KING_SIDE_CASTLE, true); board.CastlingBits |= CastlingBits.WHITE_KING_SIDE_CASTLE; } if (castlingOptions.Contains("Q") && board.WhiteKingPosition == BoardStateOffset.E1 && board.GetPiece(BoardStateOffset.A1) == (Piece.ROOK | Piece.IS_WHITE)) { //Board.SetCastleBit(board, CastlingBits.WHITE_QUEEN_SIDE_CASTLE, true); board.CastlingBits |= CastlingBits.WHITE_QUEEN_SIDE_CASTLE; } if (castlingOptions.Contains("k") && board.BlackKingPosition == BoardStateOffset.E8 && board.GetPiece(BoardStateOffset.H8) == Piece.ROOK) { //Board.SetCastleBit(board, CastlingBits.BLACK_KING_SIDE_CASTLE, true); board.CastlingBits |= CastlingBits.BLACK_KING_SIDE_CASTLE; } if (castlingOptions.Contains("q") && board.BlackKingPosition == BoardStateOffset.E8 && board.GetPiece(BoardStateOffset.A8) == Piece.ROOK) { //Board.SetCastleBit(board, CastlingBits.BLACK_QUEEN_SIDE_CASTLE, true); board.CastlingBits |= CastlingBits.BLACK_QUEEN_SIDE_CASTLE; } if (enPassantAttackedSquare == "-") { //Board.SetEnPassantAttackedSquare(board, EnPassant.NO_ENPASSANT); board.EnPassantTarget = EnPassant.NO_ENPASSANT; } else { //Board.SetEnPassantAttackedSquare(board, Board.AlgebraicPosition(enPassantAttackedSquare)); board.EnPassantTarget = (byte)BoardPosition.ArrayPosition(enPassantAttackedSquare); } //Board.SetHalfTurnCounter(board, int.Parse(halfMoveClock)); board.HalfTurnCounter = (byte)int.Parse(halfMoveClock); //Board.SetFullMoveClock(board, int.Parse(fullMoveClock)); move = (short)int.Parse(fullMoveClock); return(board); }