feat: refactor IO module to API structure and add FEN export functionality
Build & Test (NowChessSystems) TeamCity build failed
Build & Test (NowChessSystems) TeamCity build failed
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
package de.nowchess.rules.pgn
|
||||
|
||||
import de.nowchess.api.board.*
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.api.io.GameContextExport
|
||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||
import de.nowchess.rules.sets.DefaultRules
|
||||
|
||||
object PgnExporter extends GameContextExport:
|
||||
|
||||
def exportGameContext(context: GameContext): String =
|
||||
exportGame(
|
||||
Map("Event" -> "?", "White" -> "?", "Black" -> "?", "Result" -> "*"),
|
||||
context.moves,
|
||||
)
|
||||
|
||||
def exportGame(headers: Map[String, String], moves: List[Move]): String =
|
||||
val headerLines = headers.map { case (k, v) => s"""[$k "$v"]""" }.mkString("\n")
|
||||
val moveText = if moves.isEmpty then "" else buildMoveText(headers, moves)
|
||||
if headerLines.isEmpty then moveText
|
||||
else if moveText.isEmpty then headerLines
|
||||
else s"$headerLines\n\n$moveText"
|
||||
|
||||
private def buildMoveText(headers: Map[String, String], moves: List[Move]): String =
|
||||
val contexts = moves.scanLeft(GameContext.initial)((ctx, move) => DefaultRules.applyMove(ctx)(move))
|
||||
val sanMoves = moves.zip(contexts).map { case (move, ctx) => moveToAlgebraic(move, ctx.board) }
|
||||
val grouped = sanMoves.zipWithIndex.groupBy(_._2 / 2)
|
||||
val moveLines = grouped.toList.sortBy(_._1).map { case (n, pairs) =>
|
||||
val w = pairs.find(_._2 % 2 == 0).map(_._1).getOrElse("")
|
||||
val b = pairs.find(_._2 % 2 == 1).map(_._1).getOrElse("")
|
||||
if b.isEmpty then s"${n + 1}. $w" else s"${n + 1}. $w $b"
|
||||
}
|
||||
moveLines.mkString(" ") + s" ${headers.getOrElse("Result", "*")}"
|
||||
|
||||
private def moveToAlgebraic(move: Move, boardBefore: Board): String =
|
||||
move.moveType match
|
||||
case MoveType.CastleKingside => "O-O"
|
||||
case MoveType.CastleQueenside => "O-O-O"
|
||||
case MoveType.EnPassant => s"${move.from.file.toString.toLowerCase}x${move.to}"
|
||||
case MoveType.Promotion(pp) =>
|
||||
val suffix = pp match
|
||||
case PromotionPiece.Queen => "=Q"
|
||||
case PromotionPiece.Rook => "=R"
|
||||
case PromotionPiece.Bishop => "=B"
|
||||
case PromotionPiece.Knight => "=N"
|
||||
val isCapture = boardBefore.pieceAt(move.to).isDefined
|
||||
if isCapture then s"${move.from.file.toString.toLowerCase}x${move.to}$suffix"
|
||||
else s"${move.to}$suffix"
|
||||
case MoveType.Normal(isCapture) =>
|
||||
val dest = move.to.toString
|
||||
val capStr = if isCapture then "x" else ""
|
||||
boardBefore.pieceAt(move.from).map(_.pieceType).getOrElse(PieceType.Pawn) match
|
||||
case PieceType.Pawn => if isCapture then s"${move.from.file.toString.toLowerCase}x$dest" else dest
|
||||
case PieceType.Knight => s"N$capStr$dest"
|
||||
case PieceType.Bishop => s"B$capStr$dest"
|
||||
case PieceType.Rook => s"R$capStr$dest"
|
||||
case PieceType.Queen => s"Q$capStr$dest"
|
||||
case PieceType.King => s"K$capStr$dest"
|
||||
Reference in New Issue
Block a user