diff --git a/Fen.scala b/Fen.scala new file mode 100644 index 0000000..b18079c --- /dev/null +++ b/Fen.scala @@ -0,0 +1,98 @@ +package gambit + +object Fen: + val startPosition = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + + def parse(fen: String): Position = + val parts = fen.trim.split("\\s+") + val pos = new Position + var rank = 7 + for row <- parts(0).split("/") do + var file = 0 + for ch <- row do + if ch.isDigit then file += (ch - '0') + else + val piece = charToPiece(ch) + val sq = Square.make(file, rank) + pos.board(sq) = piece + if (piece & 7) == Piece.King then pos.kingSquare(piece >> 3) = sq + file += 1 + rank -= 1 + + pos.sideToMove = if parts(1) == "w" then Color.White else Color.Black + + var castle = 0 + if parts(2) != "-" then + for ch <- parts(2) do + ch match + case 'K' => castle |= Castling.WhiteKing + case 'Q' => castle |= Castling.WhiteQueen + case 'k' => castle |= Castling.BlackKing + case 'q' => castle |= Castling.BlackQueen + case _ => () + pos.castling = castle + + pos.epSquare = if parts(3) == "-" then -1 else Square.fromName(parts(3)) + pos.halfmove = if parts.length > 4 then parts(4).toInt else 0 + pos.fullmove = if parts.length > 5 then parts(5).toInt else 1 + pos.hash = pos.computeHash() + pos + + def toFen(pos: Position): String = + val sb = new StringBuilder + var rank = 7 + while rank >= 0 do + var empty = 0 + var file = 0 + while file < 8 do + val p = pos.board(Square.make(file, rank)) + if p == Piece.Empty then empty += 1 + else + if empty > 0 then + sb.append(empty) + empty = 0 + sb.append(pieceToChar(p)) + file += 1 + if empty > 0 then sb.append(empty) + if rank > 0 then sb.append('/') + rank -= 1 + + sb.append(' ') + sb.append(if pos.sideToMove == Color.White then 'w' else 'b') + sb.append(' ') + if pos.castling == 0 then sb.append('-') + else + if (pos.castling & Castling.WhiteKing) != 0 then sb.append('K') + if (pos.castling & Castling.WhiteQueen) != 0 then sb.append('Q') + if (pos.castling & Castling.BlackKing) != 0 then sb.append('k') + if (pos.castling & Castling.BlackQueen) != 0 then sb.append('q') + sb.append(' ') + sb.append(if pos.epSquare < 0 then "-" else Square.name(pos.epSquare)) + sb.append(' ') + sb.append(pos.halfmove) + sb.append(' ') + sb.append(pos.fullmove) + sb.toString + + private def charToPiece(ch: Char): Int = + val color = if ch.isUpper then Color.White else Color.Black + val pieceType = ch.toLower match + case 'p' => Piece.Pawn + case 'n' => Piece.Knight + case 'b' => Piece.Bishop + case 'r' => Piece.Rook + case 'q' => Piece.Queen + case 'k' => Piece.King + case _ => Piece.Empty + Piece.make(color, pieceType) + + private def pieceToChar(piece: Int): Char = + val letter = Piece.typeOf(piece) match + case Piece.Pawn => 'p' + case Piece.Knight => 'n' + case Piece.Bishop => 'b' + case Piece.Rook => 'r' + case Piece.Queen => 'q' + case Piece.King => 'k' + case _ => '?' + if Piece.colorOf(piece) == Color.White then letter.toUpper else letter