feat: add FEN exporter and round-trip tests
Implements FenExporter.boardToFen() converting Board to FEN piece-placement string, and adds three round-trip tests (initial position, empty board, partial position). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package de.nowchess.chess.notation
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
|
||||
object FenExporter:
|
||||
|
||||
/** Convert a Board to FEN piece-placement string (rank 8 to rank 1, separated by '/'). */
|
||||
def boardToFen(board: Board): String =
|
||||
Rank.values.reverse
|
||||
.map(rank => buildRankString(board, rank))
|
||||
.mkString("/")
|
||||
|
||||
/** Build the FEN representation for a single rank. */
|
||||
private def buildRankString(board: Board, rank: Rank): String =
|
||||
val rankSquares = File.values.map(file => Square(file, rank))
|
||||
val rankChars = scala.collection.mutable.ListBuffer[Char]()
|
||||
var emptyCount = 0
|
||||
|
||||
for square <- rankSquares do
|
||||
board.pieceAt(square) match
|
||||
case Some(piece) =>
|
||||
if emptyCount > 0 then
|
||||
rankChars += emptyCount.toString.charAt(0)
|
||||
emptyCount = 0
|
||||
rankChars += pieceToPgnChar(piece)
|
||||
case None =>
|
||||
emptyCount += 1
|
||||
|
||||
if emptyCount > 0 then rankChars += emptyCount.toString.charAt(0)
|
||||
rankChars.mkString
|
||||
|
||||
/** Convert a Piece to its FEN character (uppercase = White, lowercase = Black). */
|
||||
private def pieceToPgnChar(piece: Piece): Char =
|
||||
val base = piece.pieceType match
|
||||
case PieceType.Pawn => 'p'
|
||||
case PieceType.Knight => 'n'
|
||||
case PieceType.Bishop => 'b'
|
||||
case PieceType.Rook => 'r'
|
||||
case PieceType.Queen => 'q'
|
||||
case PieceType.King => 'k'
|
||||
if piece.color == Color.White then base.toUpper else base
|
||||
@@ -40,3 +40,24 @@ class FenParserTest extends AnyFunSuite with Matchers:
|
||||
|
||||
board.map(_.pieceAt(Square(File.E, Rank.R6))) shouldBe Some(Some(Piece.BlackKing))
|
||||
board.map(_.pieceAt(Square(File.E, Rank.R4))) shouldBe Some(Some(Piece.WhiteKing))
|
||||
|
||||
test("testRoundTripInitialPosition"):
|
||||
val originalFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"
|
||||
val board = FenParser.parseBoard(originalFen)
|
||||
val exportedFen = board.map(FenExporter.boardToFen)
|
||||
|
||||
exportedFen shouldBe Some(originalFen)
|
||||
|
||||
test("testRoundTripEmptyBoard"):
|
||||
val originalFen = "8/8/8/8/8/8/8/8"
|
||||
val board = FenParser.parseBoard(originalFen)
|
||||
val exportedFen = board.map(FenExporter.boardToFen)
|
||||
|
||||
exportedFen shouldBe Some(originalFen)
|
||||
|
||||
test("testRoundTripPartialPosition"):
|
||||
val originalFen = "8/8/4k3/8/4K3/8/8/8"
|
||||
val board = FenParser.parseBoard(originalFen)
|
||||
val exportedFen = board.map(FenExporter.boardToFen)
|
||||
|
||||
exportedFen shouldBe Some(originalFen)
|
||||
|
||||
Reference in New Issue
Block a user