refactor(core): streamline FEN and PGN export/import methods in ChessBoardView
This commit is contained in:
Generated
-18
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CopilotDiffPersistence">
|
||||
<option name="pendingDiffs">
|
||||
<map>
|
||||
<entry key="$PROJECT_DIR$/modules/ui/build.gradle.kts">
|
||||
<value>
|
||||
<PendingDiffInfo>
|
||||
<option name="filePath" value="$PROJECT_DIR$/modules/ui/build.gradle.kts" />
|
||||
<option name="originalContent" value="plugins { id("scala") id("org.scoverage") application } group = "de.nowchess" version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map<String, String> repositories { mavenCentral() } scala { scalaVersion = versions["SCALA3"]!! } scoverage { scoverageVersion.set(versions["SCOVERAGE"]!!) excludedPackages.set(listOf( "de.nowchess.ui.gui" )) } application { mainClass.set("de.nowchess.ui.Main") } tasks.withType<ScalaCompile> { scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8") } tasks.named<JavaExec>("run") { jvmArgs("-Dfile.encoding=UTF-8", "-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8") standardInput = System.`in` } dependencies { implementation("org.scala-lang:scala3-compiler_3") { version { strictly(versions["SCALA3"]!!) } } implementation("org.scala-lang:scala3-library_3") { version { strictly(versions["SCALA3"]!!) } } implementation(project(":modules:core")) implementation(project(":modules:rule")) implementation(project(":modules:api")) implementation(project(":modules:io")) // ScalaFX dependencies implementation("org.scalafx:scalafx_3:${versions["SCALAFX"]!!}") // JavaFX dependencies for the current platform val javaFXVersion = versions["JAVAFX"]!! val osName = System.getProperty("os.name").lowercase() val platform = when { osName.contains("win") -> "win" osName.contains("mac") -> "mac" osName.contains("linux") -> "linux" else -> "linux" } listOf("base", "controls", "graphics", "media").forEach { module -> implementation("org.openjfx:javafx-$module:$javaFXVersion:$platform") } testImplementation(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}") testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.test { useJUnitPlatform { includeEngines("scalatest") testLogging { events("skipped", "failed") } } finalizedBy(tasks.reportScoverage) } tasks.reportScoverage { dependsOn(tasks.test) } " />
|
||||
<option name="updatedContent" value="import org.gradle.api.file.DuplicatesStrategy import org.gradle.jvm.tasks.Jar plugins { id("scala") id("org.scoverage") application } group = "de.nowchess" version = "1.0-SNAPSHOT" @Suppress("UNCHECKED_CAST") val versions = rootProject.extra["VERSIONS"] as Map<String, String> repositories { mavenCentral() } scala { scalaVersion = versions["SCALA3"]!! } scoverage { scoverageVersion.set(versions["SCOVERAGE"]!!) excludedPackages.set(listOf( "de.nowchess.ui.gui" )) } application { mainClass.set("de.nowchess.ui.Main") } tasks.withType<ScalaCompile> { scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8") } tasks.named<JavaExec>("run") { jvmArgs("-Dfile.encoding=UTF-8", "-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8") standardInput = System.`in` } tasks.named<Jar>("jar") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } dependencies { implementation("org.scala-lang:scala3-compiler_3") { version { strictly(versions["SCALA3"]!!) } } implementation("org.scala-lang:scala3-library_3") { version { strictly(versions["SCALA3"]!!) } } implementation(project(":modules:core")) implementation(project(":modules:rule")) implementation(project(":modules:api")) implementation(project(":modules:io")) // ScalaFX dependencies implementation("org.scalafx:scalafx_3:${versions["SCALAFX"]!!}") // JavaFX dependencies for the current platform val javaFXVersion = versions["JAVAFX"]!! val osName = System.getProperty("os.name").lowercase() val platform = when { osName.contains("win") -> "win" osName.contains("mac") -> "mac" osName.contains("linux") -> "linux" else -> "linux" } listOf("base", "controls", "graphics", "media").forEach { module -> implementation("org.openjfx:javafx-$module:$javaFXVersion:$platform") } testImplementation(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}") testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } tasks.test { useJUnitPlatform { includeEngines("scalatest") testLogging { events("skipped", "failed") } } finalizedBy(tasks.reportScoverage) } tasks.reportScoverage { dependsOn(tasks.test) } " />
|
||||
</PendingDiffInfo>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -217,8 +217,12 @@ class GameEngine(
|
||||
if ruleSet.isCheckmate(currentContext) then
|
||||
val winner = currentContext.turn.opposite
|
||||
notifyObservers(CheckmateEvent(currentContext, winner))
|
||||
invoker.clear()
|
||||
currentContext = GameContext.initial
|
||||
else if ruleSet.isStalemate(currentContext) then
|
||||
notifyObservers(StalemateEvent(currentContext))
|
||||
invoker.clear()
|
||||
currentContext = GameContext.initial
|
||||
else if ruleSet.isCheck(currentContext) then
|
||||
notifyObservers(CheckDetectedEvent(currentContext))
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ class GameEngineNotationTest extends AnyFunSuite with Matchers:
|
||||
// Verify the captured pawn was found (computeCaptured EnPassant branch)
|
||||
val moveEvt = events.collect { case e: MoveExecutedEvent => e }.head
|
||||
moveEvt.capturedPiece shouldBe defined
|
||||
moveEvt.capturedPiece.get should include ("black")
|
||||
moveEvt.capturedPiece.get should include ("Black")
|
||||
|
||||
events.clear()
|
||||
engine.undo()
|
||||
|
||||
@@ -5,16 +5,19 @@ import scalafx.Includes.*
|
||||
import scalafx.application.Platform
|
||||
import scalafx.geometry.{Insets, Pos}
|
||||
import scalafx.scene.control.{Button, ButtonType, ChoiceDialog, Label}
|
||||
import scalafx.scene.layout.{BorderPane, GridPane, HBox, VBox, StackPane}
|
||||
import scalafx.scene.layout.{BorderPane, GridPane, HBox, StackPane, VBox}
|
||||
import scalafx.scene.paint.Color as FXColor
|
||||
import scalafx.scene.shape.Rectangle
|
||||
import scalafx.scene.text.{Font, Text}
|
||||
import scalafx.stage.Stage
|
||||
import de.nowchess.api.board.{Board, Color, Piece, PieceType, Square, File, Rank}
|
||||
import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
|
||||
import de.nowchess.api.game.{GameHistory, HistoryMove}
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
import de.nowchess.chess.command.{MoveCommand, MoveResult}
|
||||
import de.nowchess.chess.engine.GameEngine
|
||||
import de.nowchess.io.fen.{FenExporter, FenParser}
|
||||
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
||||
import de.nowchess.io.{GameContextExport, GameContextImport}
|
||||
|
||||
/** ScalaFX chess board view that displays the game state.
|
||||
* Uses chess sprites and color palette.
|
||||
@@ -276,33 +279,32 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B
|
||||
case _ => engine.completePromotion(PromotionPiece.Queen) // Default
|
||||
|
||||
private def doFenExport(): Unit =
|
||||
val fen = de.nowchess.io.fen.FenExporter.gameContextToFen(engine.context)
|
||||
showCopyDialog("FEN Export", fen)
|
||||
doExport(FenExporter, "FEN")
|
||||
|
||||
private def doFenImport(): Unit =
|
||||
showInputDialog("FEN Import", rows = 1).foreach { fen =>
|
||||
de.nowchess.io.fen.FenParser.parseFen(fen) match
|
||||
case Some(gameContext) =>
|
||||
engine.loadPosition(gameContext)
|
||||
case None =>
|
||||
showMessage("⚠️ Invalid FEN string")
|
||||
}
|
||||
doImport(FenParser, "FEN")
|
||||
|
||||
private def doPgnExport(): Unit =
|
||||
val pgn = de.nowchess.io.pgn.PgnExporter.exportGame(
|
||||
Map("Event" -> "NowChess Game", "Date" -> "2026.04.04"),
|
||||
exportableGameHistory()
|
||||
)
|
||||
showCopyDialog("PGN Export", pgn)
|
||||
doExport(PgnExporter, "PGN")
|
||||
|
||||
private def doPgnImport(): Unit =
|
||||
showInputDialog("PGN Import", rows = 5).foreach { pgn =>
|
||||
engine.loadPgn(pgn) match
|
||||
case Right(_) =>
|
||||
showMessage("✓ PGN loaded successfully!")
|
||||
doImport(PgnParser, "PGN")
|
||||
|
||||
private def doExport(exporter: GameContextExport, formatName: String): Unit = {
|
||||
val exported = exporter.exportGameContext(engine.context)
|
||||
showCopyDialog(s"$formatName Export", exported)
|
||||
}
|
||||
|
||||
private def doImport(importer: GameContextImport, formatName: String): Unit = {
|
||||
showInputDialog(s"$formatName Import", rows = 5).foreach { input =>
|
||||
importer.importGameContext(input) match
|
||||
case Right(gameContext) =>
|
||||
engine.loadPosition(gameContext)
|
||||
showMessage(s"✓ $formatName loaded successfully!")
|
||||
case Left(err) =>
|
||||
showMessage(s"⚠️ PGN Error: $err")
|
||||
showMessage(s"⚠️ $formatName Error: $err")
|
||||
}
|
||||
}
|
||||
|
||||
private def showCopyDialog(title: String, content: String): Unit =
|
||||
val area = new javafx.scene.control.TextArea(content)
|
||||
@@ -335,33 +337,3 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B
|
||||
val result = dialog.showAndWait()
|
||||
if result.isPresent && result.get != null && result.get.nonEmpty then Some(result.get) else None
|
||||
|
||||
private def exportableGameHistory(): GameHistory =
|
||||
val moveCommands = engine.commandHistory.collect { case moveCmd: MoveCommand => moveCmd }
|
||||
val activeMoveCount = engine.context.moves.length
|
||||
val historyMoves = moveCommands.take(activeMoveCount).flatMap: moveCmd =>
|
||||
moveCmd.previousContext.flatMap: previousContext =>
|
||||
moveCmd.moveResult.collect:
|
||||
case MoveResult.Successful(_, captured) =>
|
||||
val movingPiece = previousContext.board.pieceAt(moveCmd.from)
|
||||
val pieceType = movingPiece.map(_.pieceType).getOrElse(PieceType.Pawn)
|
||||
val castleSide = moveCmd.notation match
|
||||
case "O-O" => Some("Kingside")
|
||||
case "O-O-O" => Some("Queenside")
|
||||
case _ => None
|
||||
val promotionPiece = moveCmd.notation.split("=").lastOption.flatMap:
|
||||
case "Q" => Some(PromotionPiece.Queen)
|
||||
case "R" => Some(PromotionPiece.Rook)
|
||||
case "B" => Some(PromotionPiece.Bishop)
|
||||
case "N" => Some(PromotionPiece.Knight)
|
||||
case _ => None
|
||||
HistoryMove(
|
||||
from = moveCmd.from,
|
||||
to = moveCmd.to,
|
||||
castleSide = castleSide,
|
||||
promotionPiece = promotionPiece,
|
||||
pieceType = pieceType,
|
||||
isCapture = captured.isDefined
|
||||
)
|
||||
|
||||
GameHistory(historyMoves.toList)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user