/* * Create an ASCII representation of a position */ public static String asciiBoard(Position position) { StringBuilder boardRepresentation = new StringBuilder(400); String newLine = Environment.NewLine; boardRepresentation.Append(" +----+----+----+----+----+----+----+----+"); boardRepresentation.Append(newLine); for (int rank = 7; rank >= 0; rank--) { boardRepresentation.Append(" |"); for (int file = 0; file < 8; file++) { boardRepresentation.Append(" "); PieceType piece = position.getPiece(Position.getSquare(file, rank)); if (piece == PieceType.Empty) { Boolean isDark = Position.darkSquare(file, rank); boardRepresentation.Append(isDark ? ".. |" : " |"); } else { //A black piece has prefaced with a * boardRepresentation.Append(((int)piece < 6) ? ' ' : '*'); String pieceName = "" + piece; boardRepresentation.Append(pieceName); boardRepresentation.Append(" |"); } } boardRepresentation.Append(newLine); boardRepresentation.Append(" +----+----+----+----+----+----+----+----+"); boardRepresentation.Append(newLine); } return boardRepresentation.ToString(); }
/* * Returns true if a move results in a caputre for a given * position */ public static Boolean isMoveCapture(Move move, Position position) { if (position.getPiece(move.destination) == PieceType.Empty) { //Deal with En Passant capture PieceType piece = position.getPiece(move.origin); if ((piece == (position.whiteMove ? PieceType.P : PieceType.p)) && (move.destination == position.getEpSquare())) { return true; } else { return false; } } else { return true; } }
/* * Converts a move object into a UCI move string (long algebraic * notation), a move list can be supplied to search through. */ public static String moveObjectToString(Move move, Position position, ArrayList moves) { if ((move == null) || move.Equals(new Move(0, 0, 0))) { return "--"; } StringBuilder moveString = new StringBuilder(); int whiteKingOrigin = Position.getSquare(4, 0); int blackKingOrigin = Position.getSquare(4, 7); if ((move.origin == whiteKingOrigin) && (position.getPiece(whiteKingOrigin) == PieceType.K)) { //Check white castle if (move.destination == Position.getSquare(6, 0)) { moveString.Append("O-O"); } else if (move.destination == Position.getSquare(2, 0)) { moveString.Append("O-O-O"); } } else if ((move.origin == blackKingOrigin) && (position.getPiece(blackKingOrigin) == PieceType.k)) { //Check black castle if (move.destination == Position.getSquare(6, 7)) { moveString.Append("O-O"); } else if (move.destination == Position.getSquare(2, 7)) { moveString.Append("O-O-O"); } } if (moveString.Length == 0) { PieceType piece = position.getPiece(move.origin); moveString.Append("" + piece); int originFile = Position.getRank(move.origin); int originRank = Position.getFile(move.origin); int destinationFile = Position.getRank(move.destination); int destinationRank = Position.getFile(move.destination); //Long Algebraic Notation----- moveString.Append((char)(originFile + 'a')); moveString.Append((char)(originRank + '1')); moveString.Append(isMoveCapture(move, position) ? 'x' : '-'); //---------------------------- moveString.Append((char)(destinationFile + 'a')); moveString.Append((char)(destinationRank + '1')); if (move.promoteTo != PieceType.Empty) { moveString.Append("" + piece); } } UnMakeInfo unMake = new UnMakeInfo(); position.makeMove(move, unMake); Boolean inCheck = MoveGenerator.inCheck(position); if (inCheck) { ArrayList nextMoves = MoveGenerator.mgInstance.legalMoves(position); if (nextMoves.Count == 0) { moveString.Append('#'); } else { moveString.Append('+'); } } position.unMakeMove(move, unMake); return moveString.ToString(); }
/* * Converts a UCI move string (long algebraic notation into * a move object, a list of moves can be supplied to search * through. */ public static Move moveStringToObject(String moveString, Position position, ArrayList moves) { if (moveString.Equals("--")) { return new Move(0, 0, 0); } moveString = moveString.Replace("=", ""); moveString = moveString.Replace("\\+", ""); moveString = moveString.Replace("#", ""); Boolean whiteToMove = position.whiteMove; MoveComposition moveComp = new MoveComposition(); Boolean capture = false; if (moveString.Equals("O-O") || moveString.Equals("0-0") || moveString.Equals("o-o")) { moveComp.piece = whiteToMove ? PieceType.K : PieceType.k; moveComp.originFile = 4; moveComp.destinationFile = 6; moveComp.originRank = moveComp.destinationRank = whiteToMove ? 0 : 7; moveComp.promotionPiece = PieceType.Empty; } else if (moveString.Equals("O-O-O") || moveString.Equals("0-0-0") || moveString.Equals("o-o-o")) { moveComp.piece = whiteToMove ? PieceType.K : PieceType.k; moveComp.originFile = 4; moveComp.destinationFile = 2; moveComp.originRank = moveComp.destinationRank = whiteToMove ? 0 : 7; moveComp.promotionPiece = PieceType.Empty; } else { Boolean attackToSquare = false; for (int i = 0; i < moveString.Length; i++) { char c = moveString[i]; if (i == 0) { PieceType piece = charToPieceType(c); if (piece != PieceType.Empty) { moveComp.piece = piece; continue; } } int tempFile = c - 'a'; if ((tempFile >= 0) && (tempFile < 8)) { if (attackToSquare || (moveComp.originFile >= 0)) { moveComp.destinationFile = tempFile; } else { moveComp.originFile = tempFile; } } int tempRank = c - '1'; if ((tempRank >= 0) && (tempFile < 8)) { if (attackToSquare || (moveComp.originRank >= 0)) { moveComp.destinationRank = tempRank; } else { moveComp.originRank = tempRank; } } if ((c == 'x') || (c == '-')) { attackToSquare = true; if (c == 'x') { capture = true; } } if (i == moveString.Length - 1) { PieceType promotionPiece = charToPieceType(c); if (promotionPiece != PieceType.Empty) { moveComp.promotionPiece = promotionPiece; } } } if ((moveComp.originFile >= 0) && (moveComp.destinationFile < 0)) { moveComp.destinationFile = moveComp.originFile; moveComp.originFile = -1; } if ((moveComp.originRank >= 0) && (moveComp.destinationRank < 0)) { moveComp.destinationRank = moveComp.originRank; moveComp.originFile = -1; } if (moveComp.piece == PieceType.Empty) { Boolean haveAll = (moveComp.originFile >= 0) && (moveComp.destinationFile >= 0) && (moveComp.originRank >= 0) && (moveComp.destinationRank >= 0); if (!haveAll) { moveComp.piece = whiteToMove ? PieceType.P : PieceType.p; } } } if (moves == null) { moves = MoveGenerator.mgInstance.legalMoves(position); } ArrayList matches = new ArrayList(2); foreach (Move listMove in moves) { PieceType piece = position.getPiece(listMove.origin); Boolean match = true; if ((moveComp.piece >= 0) && (moveComp.piece != piece)) { match = false; } if ((moveComp.originFile >= 0) && (moveComp.originFile != Position.getFile(listMove.origin))) { match = false; } if ((moveComp.originRank >= 0) && (moveComp.originRank != Position.getRank(listMove.origin))) { match = false; } if ((moveComp.destinationFile >= 0) && (moveComp.destinationFile != Position.getFile(listMove.destination))) { match = false; } if ((moveComp.destinationRank >= 0) && (moveComp.destinationRank != Position.getRank(listMove.destination))) { match = false; } if ((moveComp.promotionPiece >= 0) && (moveComp.promotionPiece != listMove.promoteTo)) { match = false; } if (match) { matches.Add(listMove); } } int numMatches = matches.Count; if (numMatches == 0) { return null; } else if (numMatches == 1) { return (Move)matches[0]; } if (!capture) { return null; } Move move = null; foreach (Move listMove in matches) { PieceType capturedPiece = position.getPiece(listMove.destination); if (capturedPiece != PieceType.Empty) { if (move == null) { move = listMove; } else { return null; } } } return move; }
/* * Parse a FEN string and return a position object */ public static Position convertFENToPosition(String fen) { Position position = new Position(); String[] terms = fen.Split(' '); if (terms.Length < 2) { System.Console.WriteLine("Too few terms"); } for (int i = 0; i < terms.Length; i++) { terms[i] = terms[i].Trim(); } //Piece placement int rank = 7; int file = 0; for (int i = 0; i < terms[0].Length; i++) { char c = (char)terms[0].ToCharArray().GetValue(i); switch (c) { case '1': file += 1; break; case '2': file += 2; break; case '3': file += 3; break; case '4': file += 4; break; case '5': file += 5; break; case '6': file += 6; break; case '7': file += 7; break; case '8': file += 8; break; case '/': rank--; file = 0; break; case 'P': setPieceSafely(position, file, rank, PieceType.P); file++; break; case 'N': setPieceSafely(position, file, rank, PieceType.N); file++; break; case 'B': setPieceSafely(position, file, rank, PieceType.B); file++; break; case 'R': setPieceSafely(position, file, rank, PieceType.R); file++; break; case 'Q': setPieceSafely(position, file, rank, PieceType.Q); file++; break; case 'K': setPieceSafely(position, file, rank, PieceType.K); file++; break; case 'p': setPieceSafely(position, file, rank, PieceType.p); file++; break; case 'n': setPieceSafely(position, file, rank, PieceType.n); file++; break; case 'b': setPieceSafely(position, file, rank, PieceType.b); file++; break; case 'r': setPieceSafely(position, file, rank, PieceType.r); file++; break; case 'q': setPieceSafely(position, file, rank, PieceType.q); file++; break; case 'k': setPieceSafely(position, file, rank, PieceType.k); file++; break; default: throw new ParserError("Invalid Piece", position); } } //Active Colour if (terms[1].Length > 0) { Boolean whiteToMove; char c = (char)terms[1].ToCharArray().GetValue(0); switch (c) { case 'w': whiteToMove = true; break; case 'b': whiteToMove = false; break; default: throw new ParserError("Invalid Active Colour", position); } position.setWhiteMove(whiteToMove); } else { throw new ParserError("Invalid Active Colour", position); } //Castling Rights int castleMask = 0; if (terms.Length > 2) { for (int i = 0; i < terms[2].Length; i++) { char c = (char)terms[2].ToCharArray().GetValue(i); switch (c) { case 'K': castleMask |= (1 << Position.H1_CASTLE); break; case 'Q': castleMask |= (1 << Position.A1_CASTLE); break; case 'k': castleMask |= (1 << Position.H8_CASTLE); break; case 'q': castleMask |= (1 << Position.A8_CASTLE); break; case '-': break; default: throw new ParserError("Invalid castling flags", position); } } } position.setCastleMask(castleMask); removeInvalidCastleFlags(position); //En Passant Target Square if (terms.Length > 3) { String epString = terms[3]; if (!epString.Equals("-")) { if (epString.Length < 2) { throw new ParserError("Invalid En Passent Square", position); } position.setEpSquare(getSquare(epString)); } } try { //Halfmove Clock if (terms.Length > 4) { position.halfMoveClock = Convert.ToInt32(terms[4]); } //Full Move Counter if (terms.Length > 5) { position.fullMoveCounter = Convert.ToInt32(terms[5]); } } catch (ArgumentException ae) { //Ignore errors here since fields are optional } //Each side must have exactly one king int maxNumber = (int)PieceType.Empty + 1; int[] numPieces = new int[maxNumber]; for (int i = 0; i < maxNumber; i++) { numPieces[i] = 0; } for (file = 0; file < 8; file++) { for (rank = 0; rank < 8; rank++) { numPieces[(int)position.getPiece(Position.getSquare(file, rank))]++; } } if (numPieces[(int)PieceType.K] != 1) { throw new ParserError("Too many white kings", position); } if (numPieces[(int)PieceType.k] != 1) { throw new ParserError("Too many black kings", position); } //White must not have too many pieces int maxWPawns = 8; maxWPawns -= Math.Max(0, numPieces[(int)PieceType.N] - 2); maxWPawns -= Math.Max(0, numPieces[(int)PieceType.B] - 2); maxWPawns -= Math.Max(0, numPieces[(int)PieceType.R] - 2); maxWPawns -= Math.Max(0, numPieces[(int)PieceType.Q] - 1); if (numPieces[(int)PieceType.P] > maxWPawns) { throw new ParserError("Too many white pieces", position); } //Black must not have too many pieces int maxBPawns = 8; maxBPawns -= Math.Max(0, numPieces[(int)PieceType.n] - 2); maxBPawns -= Math.Max(0, numPieces[(int)PieceType.b] - 2); maxBPawns -= Math.Max(0, numPieces[(int)PieceType.r] - 2); maxBPawns -= Math.Max(0, numPieces[(int)PieceType.q] - 1); //Make sure king can not be captured Position pos2 = new Position(position); pos2.setWhiteMove(!position.whiteMove); if (MoveGenerator.inCheck(pos2)) { throw new ParserError("King capture possible", position); } fixupEPSquare(position); return position; }
/* * Remove castling flags that arent valid */ private static void removeInvalidCastleFlags(Position position) { int castleMask = position.getCastleMask(); int validCastle = 0; if (position.getPiece(4) == PieceType.K) { if (position.getPiece(0) == PieceType.R) validCastle |= (1 << Position.A1_CASTLE); if (position.getPiece(7) == PieceType.R) validCastle |= (1 << Position.H1_CASTLE); } if (position.getPiece(60) == PieceType.k) { if (position.getPiece(56) == PieceType.r) validCastle |= (1 << Position.A8_CASTLE); if (position.getPiece(63) == PieceType.r) validCastle |= (1 << Position.H8_CASTLE); } castleMask &= validCastle; position.setCastleMask(castleMask); }
/* * Remove EPSquare from position if it is not legal */ private static void fixupEPSquare(Position position) { int epSquare = position.getEpSquare(); if (epSquare >= 0) { ArrayList moves = MoveGenerator.mgInstance.legalMoves(position); Boolean epValid = false; foreach (Move move in moves) { if (move.destination == epSquare) { if (position.getPiece(move.origin) == (position.whiteMove ? PieceType.P : PieceType.p)) { epValid = true; break; } } } if (!epValid) { position.setEpSquare(-1); } } }
/* * Return a Forsyth-Edwards Notation string corresponding to a Position object */ public static String convertPositionToFEN(Position position) { StringBuilder feNotation = new StringBuilder(); //Piece placement for (int rank = 7; rank >= 0; rank--) { int emptySquares = 0; for (int file = 0; file < 8; file++) { PieceType piece = position.getPiece(Position.getSquare(file, rank)); if (piece == PieceType.Empty) { emptySquares++; } else { if (emptySquares > 0) { feNotation.Append(emptySquares); emptySquares = 0; } switch (piece) { case PieceType.K: feNotation.Append('K'); break; case PieceType.Q: feNotation.Append('Q'); break; case PieceType.R: feNotation.Append('R'); break; case PieceType.B: feNotation.Append('B'); break; case PieceType.N: feNotation.Append('N'); break; case PieceType.P: feNotation.Append('P'); break; case PieceType.k: feNotation.Append('k'); break; case PieceType.q: feNotation.Append('q'); break; case PieceType.r: feNotation.Append('r'); break; case PieceType.b: feNotation.Append('b'); break; case PieceType.n: feNotation.Append('n'); break; case PieceType.p: feNotation.Append('p'); break; default: throw new ParserError("Error creating FEN String"); } } } if (emptySquares > 0) { feNotation.Append(emptySquares); } if (rank > 0) { feNotation.Append("/"); } } //Active Colour feNotation.Append(position.whiteMove ? " w " : " b "); //Castling Rights Boolean anyCastle = false; if (position.h1Castle()) { feNotation.Append('K'); anyCastle = true; } if (position.a1Castle()) { feNotation.Append('Q'); anyCastle = true; } if (position.h8Castle()) { feNotation.Append('k'); anyCastle = true; } if (position.a8Castle()) { feNotation.Append('q'); anyCastle = true; } if (!anyCastle) { feNotation.Append('-'); } //En Passant Target Square { feNotation.Append(" "); if (position.getEpSquare() >= 0) { int file = Position.getFile(position.getEpSquare()); int rank = Position.getRank(position.getEpSquare()); feNotation.Append((char)(file + 'a')); feNotation.Append((char)(rank + '1')); } else { feNotation.Append('-'); } } //Move Counters feNotation.Append(' '); feNotation.Append(position.halfMoveClock); feNotation.Append(' '); feNotation.Append(position.fullMoveCounter); return feNotation.ToString(); }
/** * Gets a list of all the Square numbers of Sqaures controlled by the enemy player */ private List<int> getEnemyControlledSquares(Position position) { List<int> controlledSquares = new List<int>(); for (int i = 0; i < 64; i++) { if (position.getPiece(i).Equals(PieceType.Empty)) continue; if ((Char.IsLower(position.getPiece(i).ToString()[0]) && position.whiteMove) | (Char.IsUpper(position.getPiece(i).ToString()[0]) && !position.whiteMove)) { controlledSquares.Add(i); } } return controlledSquares; }
/* * Add all moves from a given square in direction delta * @param maxSteps Max steps until reaching a border, Set to 1 for non-sliding pieces * Returns True if the enemy king could be captured, false otherwise */ private Boolean addDirection(ArrayList moveList, Position position, int square, int maxSteps, int delta) { int destination = square; Boolean whiteToMove = position.whiteMove; PieceType opppositeKing = (whiteToMove ? PieceType.k : PieceType.K); while (maxSteps > 0) { destination += delta; PieceType piece = position.getPiece(destination); if (piece == PieceType.Empty) { moveList.Add(getMoveObject(square, destination, PieceType.Empty)); } else { //check if piece is white if (((int)piece < 6) != whiteToMove) { if (piece == opppositeKing) { returnMoveList(moveList); moveList = getMoveListObject(); moveList.Add(getMoveObject(square, destination, PieceType.Empty)); return true; } else { moveList.Add(getMoveObject(square, destination, PieceType.Empty)); } } break; } maxSteps--; } return false; }
/* * Check if there is an attacking piece in a given direction starting from * a given square. The direction is given by delta * @param maxSteps Max steps until reaching a border, Set to 1 for non-sliding pieces * Returns the first piece in the given direction, or Empty if there is no piece * in that direction */ private static PieceType checkDirection(Position position, int square, int maxSteps, int delta) { while (maxSteps > 0) { square += delta; PieceType piece = position.getPiece(square); if (piece != PieceType.Empty) { return piece; } maxSteps--; } return PieceType.Empty; }
/* * Generate and return a list of psuedo-legal moves. * Pseudo-legal moves are those that don't necessarily defend * from check threats. */ public ArrayList psuedoLegalMoves(Position position) { ArrayList moveList = getMoveListObject(); Boolean whiteToMove = position.whiteMove; for (int file = 0; file < 8; file++) { for (int rank = 0; rank < 8; rank++) { int square = Position.getSquare(file, rank); PieceType piece = position.getPiece(square); if ((piece == PieceType.Empty) || (((int)piece < 6) != whiteToMove)) { continue; } if ((piece == PieceType.R) || (piece == PieceType.r) || (piece == PieceType.Q) || (piece == PieceType.q)) { if (addDirection(moveList, position, square, 7 - file, 1)) return moveList; if (addDirection(moveList, position, square, 7 - rank, 8)) return moveList; if (addDirection(moveList, position, square, file, -1)) return moveList; if (addDirection(moveList, position, square, rank, -8)) return moveList; } if ((piece == PieceType.B) || (piece == PieceType.b) || (piece == PieceType.Q) || (piece == PieceType.q)) { if (addDirection(moveList, position, square, Math.Min(7 - file, 7 - rank), 9)) return moveList; if (addDirection(moveList, position, square, Math.Min(file, 7 - rank), 7)) return moveList; if (addDirection(moveList, position, square, Math.Min(file, rank), -9)) return moveList; if (addDirection(moveList, position, square, Math.Min(7 - file, rank), -7)) return moveList; } if ((piece == PieceType.N) || (piece == PieceType.n)) { if (file < 6 && rank < 7 && addDirection(moveList, position, square, 1, 10)) return moveList; if (file < 7 && rank < 6 && addDirection(moveList, position, square, 1, 17)) return moveList; if (file > 0 && rank < 6 && addDirection(moveList, position, square, 1, 15)) return moveList; if (file > 1 && rank < 7 && addDirection(moveList, position, square, 1, 6)) return moveList; if (file > 1 && rank > 0 && addDirection(moveList, position, square, 1, -10)) return moveList; if (file > 0 && rank > 1 && addDirection(moveList, position, square, 1, -17)) return moveList; if (file < 7 && rank > 1 && addDirection(moveList, position, square, 1, -15)) return moveList; if (file < 6 && rank > 0 && addDirection(moveList, position, square, 1, -6)) return moveList; } if ((piece == PieceType.K) || (piece == PieceType.k)) { if (file < 7 && addDirection(moveList, position, square, 1, 1)) return moveList; if (file < 7 && rank < 7 && addDirection(moveList, position, square, 1, 9)) return moveList; if (rank < 7 && addDirection(moveList, position, square, 1, 8)) return moveList; if (file > 0 && rank < 7 && addDirection(moveList, position, square, 1, 7)) return moveList; if (file > 0 && addDirection(moveList, position, square, 1, -1)) return moveList; if (file > 0 && rank > 0 && addDirection(moveList, position, square, 1, -9)) return moveList; if (rank > 0 && addDirection(moveList, position, square, 1, -8)) return moveList; if (file < 7 && rank > 0 && addDirection(moveList, position, square, 1, -7)) return moveList; int kingOrigin = whiteToMove ? Position.getSquare(4, 0) : Position.getSquare(4, 7); if (Position.getSquare(file, rank) == kingOrigin) { int aCastle = whiteToMove ? Position.A1_CASTLE : Position.A8_CASTLE; int hCastle = whiteToMove ? Position.H1_CASTLE : Position.H8_CASTLE; PieceType rook = whiteToMove ? PieceType.R : PieceType.r; if (((position.getCastleMask() & (1 << hCastle)) != 0) && (position.getPiece(kingOrigin + 1) == PieceType.Empty) && (position.getPiece(kingOrigin + 2) == PieceType.Empty) && (position.getPiece(kingOrigin + 3) == rook) && !squareAttacked(position, kingOrigin) && !squareAttacked(position, kingOrigin + 1) && !squareAttacked(position, kingOrigin + 2)) { moveList.Add(getMoveObject(kingOrigin, kingOrigin + 2, PieceType.Empty)); } if (((position.getCastleMask() & (1 << aCastle)) != 0) && (position.getPiece(kingOrigin - 1) == PieceType.Empty) && (position.getPiece(kingOrigin - 2) == PieceType.Empty) && (position.getPiece(kingOrigin - 3) == PieceType.Empty) && (position.getPiece(kingOrigin - 4) == rook) && !squareAttacked(position, kingOrigin) && !squareAttacked(position, kingOrigin - 1) && !squareAttacked(position, kingOrigin - 2)) { moveList.Add(getMoveObject(kingOrigin, kingOrigin - 2, PieceType.Empty)); } } } if ((piece == PieceType.P) || (piece == PieceType.p)) { int rankDir = whiteToMove ? 8 : -8; if (position.getPiece(square + rankDir) == PieceType.Empty) { //non capture moves addPawnMoves(moveList, square, square + rankDir); if ((rank == (whiteToMove ? 1 : 6)) && (position.getPiece(square + 2 * rankDir) == PieceType.Empty)) { //double step moves addPawnMoves(moveList, square, square + rankDir * 2); } } //Capture to the left if (file > 0) { int toSquare = square + rankDir - 1; PieceType captured = position.getPiece(toSquare); if (captured != PieceType.Empty) { if (((int)captured < 6) != whiteToMove) { if (captured == (whiteToMove ? PieceType.k : PieceType.K)) { returnMoveList(moveList); moveList = getMoveListObject(); moveList.Add(getMoveObject(square, toSquare, PieceType.Empty)); return moveList; } else { addPawnMoves(moveList, square, toSquare); } } } else if (toSquare == position.getEpSquare()) { addPawnMoves(moveList, square, toSquare); } } //Capture to the right if (file < 7) { int toSquare = square + rankDir + 1; PieceType captured = position.getPiece(toSquare); if (captured != PieceType.Empty) { if (((int)captured < 6) != whiteToMove) { if (captured == (whiteToMove ? PieceType.k : PieceType.K)) { returnMoveList(moveList); moveList = getMoveListObject(); moveList.Add(getMoveObject(square, toSquare, PieceType.Empty)); return moveList; } else { addPawnMoves(moveList, square, toSquare); } } } else if (toSquare == position.getEpSquare()) { addPawnMoves(moveList, square, toSquare); } } } } } return moveList; }