TextIO { public static Position readFEN(String fen) throws ChessParseError { Position pos = new Position(); String[] words = fen.split(" "); if (words.length < 2) { throw new ChessParseError("Too few spaces"); } int row = 7; int col = 0; for (int i = 0; i < words[0].length(); i++) { char c = words[0].charAt(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].charAt(0) == 'w'); int castleMask = 0; if (words.length > 2) { for (int i = 0; i < words[2].length(); i++) { char c = words[2].charAt(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) { String epString = words[3]; if (!epString.equals("-")) { if (epString.length() < 2) { throw new ChessParseError("Invalid en passant square"); } pos.setEpSquare(getSquare(epString)); } } try { if (words.length > 4) { pos.halfMoveClock = Integer.parseInt(words[4]); } if (words.length > 5) { pos.fullMoveCounter = Integer.parseInt(words[5]); } } catch (NumberFormatException nfe) { } 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"); } Position pos2 = new Position(pos); pos2.setWhiteMove(!pos.whiteMove); if (MoveGen.inCheck(pos2)) { throw new ChessParseError("King capture possible"); } fixupEPSquare(pos); return pos; }  static Position readFEN(String fen); static void fixupEPSquare(Position pos); static String toFEN(Position pos); static String moveToString(Position pos, Move move, boolean longForm); static String moveToUCIString(Move m); static Move uciStringToMove(String move); static Move stringToMove(Position pos, String strMove); static int getSquare(String s); static String squareToString(int square); static String asciiBoard(Position pos); static final String startPosFEN; }
@Test public void testReadFEN() throws ChessParseError { System.out.println("readFEN"); String fen = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R b kq - 0 1"; Position pos = TextIO.readFEN(fen); assertEquals(fen, TextIO.toFEN(pos)); assertEquals(pos.getPiece(Position.getSquare(0, 3)), Piece.WQUEEN); assertEquals(pos.getPiece(Position.getSquare(4, 7)), Piece.BKING); assertEquals(pos.getPiece(Position.getSquare(4, 1)), Piece.WKING); assertEquals(pos.whiteMove, false); assertEquals(pos.a1Castle(), false); assertEquals(pos.h1Castle(), false); assertEquals(pos.a8Castle(), true); assertEquals(pos.h8Castle(), true); fen = "8/3k4/8/5pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 1 2"; pos = TextIO.readFEN(fen); assertEquals(fen, TextIO.toFEN(pos)); assertEquals(1, pos.halfMoveClock); assertEquals(2, pos.fullMoveCounter); boolean wasError = testFENParseError("8/8/8/8/8/8/8/kk1K4 w - - 0 1"); assertEquals(true, wasError); wasError = testFENParseError("8/8/8/8/8/8/8/k1RK4 w - - 0 1"); assertEquals(true, wasError); fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"; pos = TextIO.readFEN(fen); assertEquals(-1, pos.getEpSquare()); wasError = testFENParseError("8/8/8/8/4k3/8/8/8/KBN5 w - - 0 1"); assertEquals(true, wasError); wasError = testFENParseError("8K/8/8/8/4k3/8/8/8 w - - 0 1"); assertEquals(true, wasError); wasError = testFENParseError("kp6/8/8/8/8/8/8/K7 w - - 0 1"); assertEquals(true, wasError); wasError = testFENParseError("kr/pppp/8/8/8/8/8/KBR w"); assertEquals(false, wasError); wasError = testFENParseError("k/8/8/8/8/8/8/K"); assertEquals(true, wasError); wasError = testFENParseError(""); assertEquals(true, wasError); wasError = testFENParseError(" |"); assertEquals(true, wasError); wasError = testFENParseError("1B1B4/6k1/7r/7P/6q1/r7/q7/7K b - - acn 6; acs 0;"); assertEquals(false, wasError); }