From 09a70ed435087f9305b4b55e0165d68050301529 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 5 Apr 2026 13:20:59 +0200 Subject: [PATCH] feat(io): implement PgnExporter.exportGameContext with move replay PgnExporter now extends GameContextExport and implements exportGameContext, which replays moves from GameContext.initial to reconstruct HistoryMove records with proper castling/promotion info before generating PGN. Co-Authored-By: Claude Sonnet 4.6 --- .../de/nowchess/io/pgn/PgnExporter.scala | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala index 1a177de..e16c49a 100644 --- a/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala +++ b/modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala @@ -1,10 +1,41 @@ package de.nowchess.io.pgn import de.nowchess.api.board.* -import de.nowchess.api.move.PromotionPiece -import de.nowchess.api.game.{GameHistory, HistoryMove} +import de.nowchess.api.move.{PromotionPiece, MoveType} +import de.nowchess.api.game.{GameHistory, HistoryMove, GameContext} +import de.nowchess.io.GameContextExport +import de.nowchess.rules.sets.DefaultRules -object PgnExporter: +object PgnExporter extends GameContextExport: + + /** Export a GameContext to PGN format by replaying all moves and reconstructing HistoryMove records. */ + def exportGameContext(context: GameContext): String = + val headers = Map( + "Event" -> "?", + "White" -> "?", + "Black" -> "?", + "Result" -> "*" + ) + + // Replay all moves to reconstruct HistoryMove records with full info + val historyMoves = scala.collection.mutable.ListBuffer[HistoryMove]() + var ctx = GameContext.initial + for move <- context.moves do + val color = ctx.turn + val pieceType = ctx.board.pieceAt(move.from).map(_.pieceType).getOrElse(PieceType.Pawn) + val isCapture = ctx.board.pieceAt(move.to).isDefined || move.moveType == MoveType.EnPassant + val castleSide = move.moveType match + case MoveType.CastleKingside => Some("Kingside") + case MoveType.CastleQueenside => Some("Queenside") + case _ => None + val promotionPiece = move.moveType match + case MoveType.Promotion(pp) => Some(pp) + case _ => None + historyMoves += HistoryMove(move.from, move.to, castleSide, promotionPiece, pieceType, isCapture) + ctx = DefaultRules.applyMove(ctx, move) + + val history = GameHistory(historyMoves.toList, context.halfMoveClock) + exportGame(headers, history) /** Export a game with headers and history to PGN format. */ def exportGame(headers: Map[String, String], history: GameHistory): String =