/** Remove pseudo-legal EP square if it is not legal, ie would leave king in check. */ public static void fixupEPSquare(Position pos) { int epSquare = pos.getEpSquare(); if (epSquare >= 0) { MoveGen MG = new MoveGen(); MoveGen.MoveList moves = MG.pseudoLegalMoves(pos); MoveGen.RemoveIllegal(pos, moves); bool epValid = false; for (int mi = 0; mi < moves.size; mi++) { Move m = moves.m[mi]; if (m.to == epSquare) { if (pos.getPiece(m.from) == (pos.whiteMove ? Piece.WPAWN : Piece.BPAWN)) { epValid = true; break; } } } if (!epValid) { pos.setEpSquare(-1); } } }
/*throws ChessParseError*/ /** Parse a FEN string and return a chess Position object. */ public static Position readFEN(string fen) { Position pos = new Position(); string[] words = fen.Split(' '); if (words.Length < 2) { throw new ChessParseError(/* "Too few pieces " */); } // Piece placement int row = 7; int col = 0; for (int i = 0; i < words[0].Length; i++) { char c = words[0][i]; switch (c) { case '1': col += 1; break; case '2': col += 2; break; case '3': col += 3; break; case '4': col += 4; break; case '5': col += 5; break; case '6': col += 6; break; case '7': col += 7; break; case '8': col += 8; break; case '/': row--; col = 0; break; case 'P': safeSetPiece(pos, col, row, Piece.WPAWN); col++; break; case 'N': safeSetPiece(pos, col, row, Piece.WKNIGHT); col++; break; case 'B': safeSetPiece(pos, col, row, Piece.WBISHOP); col++; break; case 'R': safeSetPiece(pos, col, row, Piece.WROOK); col++; break; case 'Q': safeSetPiece(pos, col, row, Piece.WQUEEN); col++; break; case 'K': safeSetPiece(pos, col, row, Piece.WKING); col++; break; case 'p': safeSetPiece(pos, col, row, Piece.BPAWN); col++; break; case 'n': safeSetPiece(pos, col, row, Piece.BKNIGHT); col++; break; case 'b': safeSetPiece(pos, col, row, Piece.BBISHOP); col++; break; case 'r': safeSetPiece(pos, col, row, Piece.BROOK); col++; break; case 'q': safeSetPiece(pos, col, row, Piece.BQUEEN); col++; break; case 'k': safeSetPiece(pos, col, row, Piece.BKING); col++; break; default: throw new ChessParseError(/* "Invalid piece" */); } } if (words[1].Length == 0) { throw new ChessParseError(/*"Invalid side"*/); } pos.setWhiteMove(words[1][0] == 'w'); // Castling rights int castleMask = 0; if (words.Length > 2) { for (int i = 0; i < words[2].Length; i++) { char c = words[2][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 ChessParseError(/* "Invalid castling flags" */); } } } pos.setCastleMask(castleMask); if (words.Length > 3) { // En passant target square string epstring = words[3]; if (epstring != "-") { if (epstring.Length < 2) { throw new ChessParseError(/* "Invalid en passant square" */); } pos.setEpSquare(getSquare(epstring)); } } try { if (words.Length > 4) { pos.halfMoveClock = int.Parse(words[4]); } if (words.Length > 5) { pos.fullMoveCounter = int.Parse(words[5]); } } catch (NumberFormatException nfe) { // Ignore errors here, since the fields are optional } // Each side must have exactly one king int wKings = 0; int bKings = 0; for (int x = 0; x < 8; x++) { for (int y = 0; y < 8; y++) { int p = pos.getPiece(Position.getSquare(x, y)); if (p == Piece.WKING) { wKings++; } else if (p == Piece.BKING) { bKings++; } } } if (wKings != 1) { throw new ChessParseError(/* "White must have exactly one king" */); } if (bKings != 1) { throw new ChessParseError(/* "Black must have exactly one king" */); } // Make sure king can not be captured Position pos2 = new Position(pos); pos2.setWhiteMove(!pos.whiteMove); if (MoveGen.inCheck(pos2)) { throw new ChessParseError(/* "King capture possible" */); } fixupEPSquare(pos); return pos; }