From 432385c7b052d56e0915f90cc8f71d1ef943e2a4 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 5 Apr 2026 17:08:54 +0200 Subject: [PATCH] refactor(core): replace GameHistory with HistoryMove in PGN export logic --- .../de/nowchess/api/game/GameHistory.scala | 51 --------- .../de/nowchess/api/game/HistoryMove.scala | 15 +++ .../chess/logic/GameHistoryTest.scala | 105 ------------------ .../chess/logic/HistoryMoveTest.scala | 48 ++++++++ .../de/nowchess/io/pgn/PgnExporter.scala | 13 +-- .../de/nowchess/io/pgn/PgnExporterTest.scala | 74 ++++++------ .../de/nowchess/io/pgn/PgnValidatorTest.scala | 1 - .../de/nowchess/ui/gui/ChessBoardView.scala | 5 +- .../de/nowchess/ui/terminal/TerminalUI.scala | 4 +- .../de/nowchess/ui/utils}/PieceUnicode.scala | 2 +- .../de/nowchess/ui/utils}/Renderer.scala | 4 +- .../nowchess/ui/utils}/PieceUnicodeTest.scala | 2 +- .../de/nowchess/ui/utils}/RendererTest.scala | 4 +- 13 files changed, 110 insertions(+), 218 deletions(-) delete mode 100644 modules/api/src/main/scala/de/nowchess/api/game/GameHistory.scala create mode 100644 modules/api/src/main/scala/de/nowchess/api/game/HistoryMove.scala delete mode 100644 modules/core/src/test/scala/de/nowchess/chess/logic/GameHistoryTest.scala create mode 100644 modules/core/src/test/scala/de/nowchess/chess/logic/HistoryMoveTest.scala rename modules/{core/src/main/scala/de/nowchess/chess/view => ui/src/main/scala/de/nowchess/ui/utils}/PieceUnicode.scala (96%) rename modules/{core/src/main/scala/de/nowchess/chess/view => ui/src/main/scala/de/nowchess/ui/utils}/Renderer.scala (91%) rename modules/{core/src/test/scala/de/nowchess/chess/view => ui/src/test/scala/de/nowchess/ui/utils}/PieceUnicodeTest.scala (97%) rename modules/{core/src/test/scala/de/nowchess/chess/view => ui/src/test/scala/de/nowchess/ui/utils}/RendererTest.scala (94%) diff --git a/modules/api/src/main/scala/de/nowchess/api/game/GameHistory.scala b/modules/api/src/main/scala/de/nowchess/api/game/GameHistory.scala deleted file mode 100644 index 868f364..0000000 --- a/modules/api/src/main/scala/de/nowchess/api/game/GameHistory.scala +++ /dev/null @@ -1,51 +0,0 @@ -package de.nowchess.api.game - -import de.nowchess.api.board.{PieceType, Square} -import de.nowchess.api.move.{Move, PromotionPiece} - -/** A single move recorded in the game history. Distinct from api.move.Move which represents user intent. */ -case class HistoryMove( - from: Square, - to: Square, - castleSide: Option[String] = None, - promotionPiece: Option[PromotionPiece] = None, - pieceType: PieceType = PieceType.Pawn, - isCapture: Boolean = false -) - -/** Complete game history: ordered list of moves plus the half-move clock for the 50-move rule. - * - * @param moves moves played so far, oldest first - * @param halfMoveClock plies since the last pawn move or capture (FIDE 50-move rule counter) - * - * Deprecated: Use GameContext instead. This exists for compatibility during migration. - */ -case class GameHistory(moves: List[HistoryMove] = List.empty, halfMoveClock: Int = 0): - - /** Add a raw HistoryMove record. Clock increments by 1. - * Use the coordinate overload when you know whether the move is a pawn move or capture. - */ - def addMove(move: HistoryMove): GameHistory = - GameHistory(moves :+ move, halfMoveClock + 1) - - /** Add a move by coordinates. - * - * @param wasPawnMove true when the moving piece is a pawn — resets the clock to 0 - * @param wasCapture true when a piece was captured (including en passant) — resets the clock to 0 - * - * If neither flag is set the clock increments by 1. - */ - def addMove( - from: Square, - to: Square, - castleSide: Option[String] = None, - promotionPiece: Option[PromotionPiece] = None, - wasPawnMove: Boolean = false, - wasCapture: Boolean = false, - pieceType: PieceType = PieceType.Pawn - ): GameHistory = - val newClock = if wasPawnMove || wasCapture then 0 else halfMoveClock + 1 - GameHistory(moves :+ HistoryMove(from, to, castleSide, promotionPiece, pieceType, wasCapture), newClock) - -object GameHistory: - val empty: GameHistory = GameHistory() diff --git a/modules/api/src/main/scala/de/nowchess/api/game/HistoryMove.scala b/modules/api/src/main/scala/de/nowchess/api/game/HistoryMove.scala new file mode 100644 index 0000000..bbb8616 --- /dev/null +++ b/modules/api/src/main/scala/de/nowchess/api/game/HistoryMove.scala @@ -0,0 +1,15 @@ +package de.nowchess.api.game + +import de.nowchess.api.board.{PieceType, Square} +import de.nowchess.api.move.PromotionPiece + +/** A single move recorded in game history and PGN workflows. */ +case class HistoryMove( + from: Square, + to: Square, + castleSide: Option[String] = None, + promotionPiece: Option[PromotionPiece] = None, + pieceType: PieceType = PieceType.Pawn, + isCapture: Boolean = false +) + diff --git a/modules/core/src/test/scala/de/nowchess/chess/logic/GameHistoryTest.scala b/modules/core/src/test/scala/de/nowchess/chess/logic/GameHistoryTest.scala deleted file mode 100644 index 919b736..0000000 --- a/modules/core/src/test/scala/de/nowchess/chess/logic/GameHistoryTest.scala +++ /dev/null @@ -1,105 +0,0 @@ -package de.nowchess.chess.logic - -import de.nowchess.api.board.* -import de.nowchess.api.move.PromotionPiece -import de.nowchess.api.game.{GameHistory, HistoryMove} -import org.scalatest.funsuite.AnyFunSuite -import org.scalatest.matchers.should.Matchers - -class GameHistoryTest extends AnyFunSuite with Matchers: - - private def sq(f: File, r: Rank): Square = Square(f, r) - - test("GameHistory starts empty"): - val history = GameHistory.empty - history.moves shouldBe empty - - test("GameHistory can add a move"): - val history = GameHistory.empty.addMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4)) - history.moves should have length 1 - history.moves.head.from shouldBe sq(File.E, Rank.R2) - history.moves.head.to shouldBe sq(File.E, Rank.R4) - history.moves.head.castleSide shouldBe None - - test("GameHistory can add multiple moves in order"): - val h1 = GameHistory.empty - val h2 = h1.addMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4)) - val h3 = h2.addMove(sq(File.C, Rank.R7), sq(File.C, Rank.R5)) - h3.moves should have length 2 - h3.moves(0).from shouldBe sq(File.E, Rank.R2) - h3.moves(1).from shouldBe sq(File.C, Rank.R7) - - test("GameHistory can add a castle move"): - val history = GameHistory.empty.addMove( - sq(File.E, Rank.R1), - sq(File.G, Rank.R1), - Some("Kingside") - ) - history.moves.head.castleSide shouldBe Some("Kingside") - - test("GameHistory.addMove with two arguments uses None for castleSide default"): - val history = GameHistory.empty.addMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4)) - history.moves should have length 1 - history.moves.head.castleSide shouldBe None - - test("Move with promotion records the promotion piece"): - val move = HistoryMove(sq(File.E, Rank.R7), sq(File.E, Rank.R8), None, Some(PromotionPiece.Queen)) - move.promotionPiece should be (Some(PromotionPiece.Queen)) - - test("Normal move has no promotion piece"): - val move = HistoryMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4), None, None) - move.promotionPiece should be (None) - - test("addMove with promotion stores promotionPiece"): - val history = GameHistory.empty - val newHistory = history.addMove(sq(File.E, Rank.R7), sq(File.E, Rank.R8), None, Some(PromotionPiece.Rook)) - newHistory.moves should have length 1 - newHistory.moves.head.promotionPiece should be (Some(PromotionPiece.Rook)) - - test("addMove with castleSide only uses promotionPiece default (None)"): - val history = GameHistory.empty - // With overload 3 removed, this uses the 4-param version and triggers addMove$default$4 - val newHistory = history.addMove(sq(File.E, Rank.R1), sq(File.G, Rank.R1), Some("Kingside")) - newHistory.moves should have length 1 - newHistory.moves.head.castleSide should be (Some("Kingside")) - newHistory.moves.head.promotionPiece should be (None) - - test("addMove using named parameters with only promotion, using castleSide default"): - val history = GameHistory.empty - val newHistory = history.addMove(from = sq(File.E, Rank.R7), to = sq(File.E, Rank.R8), promotionPiece = Some(PromotionPiece.Queen)) - newHistory.moves should have length 1 - newHistory.moves.head.castleSide should be (None) - newHistory.moves.head.promotionPiece should be (Some(PromotionPiece.Queen)) - - // ──── half-move clock ──────────────────────────────────────────────── - - test("halfMoveClock starts at 0"): - GameHistory.empty.halfMoveClock shouldBe 0 - - test("halfMoveClock increments on a non-pawn non-capture move"): - val h = GameHistory.empty.addMove(sq(File.G, Rank.R1), sq(File.F, Rank.R3)) - h.halfMoveClock shouldBe 1 - - test("halfMoveClock resets to 0 on a pawn move"): - val h = GameHistory.empty.addMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4), wasPawnMove = true) - h.halfMoveClock shouldBe 0 - - test("halfMoveClock resets to 0 on a capture"): - val h = GameHistory.empty.addMove(sq(File.E, Rank.R5), sq(File.D, Rank.R6), wasCapture = true) - h.halfMoveClock shouldBe 0 - - test("halfMoveClock resets to 0 when both wasPawnMove and wasCapture are true"): - val h = GameHistory.empty.addMove(sq(File.E, Rank.R5), sq(File.D, Rank.R6), wasPawnMove = true, wasCapture = true) - h.halfMoveClock shouldBe 0 - - test("halfMoveClock carries across multiple moves"): - val h = GameHistory.empty - .addMove(sq(File.G, Rank.R1), sq(File.F, Rank.R3)) // +1 → 1 - .addMove(sq(File.G, Rank.R8), sq(File.F, Rank.R6)) // +1 → 2 - .addMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4), wasPawnMove = true) // reset → 0 - .addMove(sq(File.B, Rank.R1), sq(File.C, Rank.R3)) // +1 → 1 - h.halfMoveClock shouldBe 1 - - test("GameHistory can be initialised with a non-zero halfMoveClock"): - val h = GameHistory(halfMoveClock = 42) - h.halfMoveClock shouldBe 42 diff --git a/modules/core/src/test/scala/de/nowchess/chess/logic/HistoryMoveTest.scala b/modules/core/src/test/scala/de/nowchess/chess/logic/HistoryMoveTest.scala new file mode 100644 index 0000000..0c375c3 --- /dev/null +++ b/modules/core/src/test/scala/de/nowchess/chess/logic/HistoryMoveTest.scala @@ -0,0 +1,48 @@ +package de.nowchess.chess.logic + +import de.nowchess.api.board.* +import de.nowchess.api.move.PromotionPiece +import de.nowchess.api.game.HistoryMove +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers + +class HistoryMoveTest extends AnyFunSuite with Matchers: + + private def sq(f: File, r: Rank): Square = Square(f, r) + + test("Move with promotion records the promotion piece"): + val move = HistoryMove(sq(File.E, Rank.R7), sq(File.E, Rank.R8), None, Some(PromotionPiece.Queen)) + move.promotionPiece should be (Some(PromotionPiece.Queen)) + + test("Normal move has no promotion piece"): + val move = HistoryMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4), None, None) + move.promotionPiece should be (None) + + test("HistoryMove default values are compatible with normal pawn move"): + val move = HistoryMove(sq(File.E, Rank.R2), sq(File.E, Rank.R4)) + move.castleSide shouldBe None + move.promotionPiece shouldBe None + move.pieceType shouldBe PieceType.Pawn + move.isCapture shouldBe false + + test("HistoryMove stores castle side and capture marker"): + val move = HistoryMove( + from = sq(File.E, Rank.R1), + to = sq(File.G, Rank.R1), + castleSide = Some("Kingside"), + pieceType = PieceType.King + ) + move.castleSide shouldBe Some("Kingside") + move.pieceType shouldBe PieceType.King + + test("HistoryMove stores explicit piece and capture flags"): + val move = HistoryMove( + from = sq(File.G, Rank.R1), + to = sq(File.F, Rank.R3), + pieceType = PieceType.Knight, + isCapture = true + ) + move.pieceType shouldBe PieceType.Knight + move.isCapture shouldBe true + + 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 326b43b..15bf187 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 @@ -2,7 +2,7 @@ package de.nowchess.io.pgn import de.nowchess.api.board.* import de.nowchess.api.move.{PromotionPiece, MoveType} -import de.nowchess.api.game.{GameHistory, HistoryMove, GameContext} +import de.nowchess.api.game.{GameContext, HistoryMove} import de.nowchess.io.GameContextExport import de.nowchess.rules.sets.DefaultRules @@ -42,18 +42,17 @@ object PgnExporter extends GameContextExport: 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) + exportGame(headers, historyMoves.toList) - /** Export a game with headers and history to PGN format. */ - def exportGame(headers: Map[String, String], history: GameHistory): String = + /** Export a game with headers and history moves to PGN format. */ + def exportGame(headers: Map[String, String], moves: List[HistoryMove]): String = val headerLines = headers.map { case (key, value) => s"""[$key "$value"]""" }.mkString("\n") - val moveText = if history.moves.isEmpty then "" + val moveText = if moves.isEmpty then "" else - val groupedMoves = history.moves.zipWithIndex.groupBy(_._2 / 2) + val groupedMoves = moves.zipWithIndex.groupBy(_._2 / 2) val moveLines = for (moveNumber, movePairs) <- groupedMoves.toList.sortBy(_._1) yield val moveNum = moveNumber + 1 val whiteMoveStr = movePairs.find(_._2 % 2 == 0).map(p => moveToAlgebraic(p._1)).getOrElse("") diff --git a/modules/io/src/test/scala/de/nowchess/io/pgn/PgnExporterTest.scala b/modules/io/src/test/scala/de/nowchess/io/pgn/PgnExporterTest.scala index d45846d..b96dc86 100644 --- a/modules/io/src/test/scala/de/nowchess/io/pgn/PgnExporterTest.scala +++ b/modules/io/src/test/scala/de/nowchess/io/pgn/PgnExporterTest.scala @@ -2,8 +2,7 @@ package de.nowchess.io.pgn import de.nowchess.api.board.{PieceType, *} import de.nowchess.api.move.{PromotionPiece, Move, MoveType} -import de.nowchess.api.game.{GameHistory, HistoryMove, GameContext} -import de.nowchess.io.pgn.PgnParser +import de.nowchess.api.game.{GameContext, HistoryMove} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers @@ -11,8 +10,7 @@ class PgnExporterTest extends AnyFunSuite with Matchers: test("export empty game") { val headers = Map("Event" -> "Test", "White" -> "A", "Black" -> "B") - val history = GameHistory.empty - val pgn = PgnExporter.exportGame(headers, history) + val pgn = PgnExporter.exportGame(headers, List.empty) pgn.contains("[Event \"Test\"]") shouldBe true pgn.contains("[White \"A\"]") shouldBe true @@ -21,97 +19,87 @@ class PgnExporterTest extends AnyFunSuite with Matchers: test("export single move") { val headers = Map("Event" -> "Test", "White" -> "A", "Black" -> "B") - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) - val pgn = PgnExporter.exportGame(headers, history) + val moves = List(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) + val pgn = PgnExporter.exportGame(headers, moves) pgn.contains("1. e4") shouldBe true } test("export castling") { val headers = Map("Event" -> "Test") - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R1), Square(File.G, Rank.R1), Some("Kingside"))) - val pgn = PgnExporter.exportGame(headers, history) + val moves = List(HistoryMove(Square(File.E, Rank.R1), Square(File.G, Rank.R1), Some("Kingside"))) + val pgn = PgnExporter.exportGame(headers, moves) pgn.contains("O-O") shouldBe true } test("export game sequence") { val headers = Map("Event" -> "Test", "White" -> "A", "Black" -> "B", "Result" -> "1-0") - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) - .addMove(HistoryMove(Square(File.C, Rank.R7), Square(File.C, Rank.R5), None)) - .addMove(HistoryMove(Square(File.G, Rank.R1), Square(File.F, Rank.R3), None, pieceType = PieceType.Knight)) - val pgn = PgnExporter.exportGame(headers, history) + val moves = List( + HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None), + HistoryMove(Square(File.C, Rank.R7), Square(File.C, Rank.R5), None), + HistoryMove(Square(File.G, Rank.R1), Square(File.F, Rank.R3), None, pieceType = PieceType.Knight) + ) + val pgn = PgnExporter.exportGame(headers, moves) pgn.contains("1. e4 c5") shouldBe true pgn.contains("2. Nf3") shouldBe true } test("export game with no headers returns only move text") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn shouldBe "1. e4 *" } test("export queenside castling") { val headers = Map("Event" -> "Test") - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R1), Square(File.C, Rank.R1), Some("Queenside"))) - val pgn = PgnExporter.exportGame(headers, history) + val moves = List(HistoryMove(Square(File.E, Rank.R1), Square(File.C, Rank.R1), Some("Queenside"))) + val pgn = PgnExporter.exportGame(headers, moves) pgn.contains("O-O-O") shouldBe true } test("exportGame encodes promotion to Queen as =Q suffix") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Queen))) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Queen))) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn should include ("e8=Q") } test("exportGame encodes promotion to Rook as =R suffix") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Rook))) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Rook))) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn should include ("e8=R") } test("exportGame encodes promotion to Bishop as =B suffix") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Bishop))) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Bishop))) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn should include ("e8=B") } test("exportGame encodes promotion to Knight as =N suffix") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Knight))) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R7), Square(File.E, Rank.R8), None, Some(PromotionPiece.Knight))) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn should include ("e8=N") } test("exportGame does not add suffix for normal moves") { - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None, None)) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None, None)) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn should include ("e4") - pgn should not include ("=") + pgn should not include "=" } test("exportGame uses Result header as termination marker"): - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) - val pgn = PgnExporter.exportGame(Map("Result" -> "1/2-1/2"), history) + val moves = List(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) + val pgn = PgnExporter.exportGame(Map("Result" -> "1/2-1/2"), moves) pgn should endWith("1/2-1/2") test("exportGame with no Result header still uses * as default"): - val history = GameHistory() - .addMove(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) - val pgn = PgnExporter.exportGame(Map.empty, history) + val moves = List(HistoryMove(Square(File.E, Rank.R2), Square(File.E, Rank.R4), None)) + val pgn = PgnExporter.exportGame(Map.empty, moves) pgn shouldBe "1. e4 *" test("exportGameContext: moves are preserved in output") { diff --git a/modules/io/src/test/scala/de/nowchess/io/pgn/PgnValidatorTest.scala b/modules/io/src/test/scala/de/nowchess/io/pgn/PgnValidatorTest.scala index c105599..d656db4 100644 --- a/modules/io/src/test/scala/de/nowchess/io/pgn/PgnValidatorTest.scala +++ b/modules/io/src/test/scala/de/nowchess/io/pgn/PgnValidatorTest.scala @@ -2,7 +2,6 @@ package de.nowchess.io.pgn import de.nowchess.api.board.* import de.nowchess.api.move.PromotionPiece -import de.nowchess.api.game.{GameHistory, HistoryMove} import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers diff --git a/modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala b/modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala index de4d186..e46ac69 100644 --- a/modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala +++ b/modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala @@ -11,7 +11,6 @@ import scalafx.scene.shape.Rectangle import scalafx.scene.text.{Font, Text} import scalafx.stage.Stage 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 @@ -178,7 +177,7 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B if piece.color == currentTurn then selectedSquare = Some(clickedSquare) highlightSquare(rank, file, PieceSprites.SquareColors.Selected) - + val legalDests = engine.ruleSet.legalMoves(engine.context, clickedSquare) .collect { case move if move.from == clickedSquare => move.to } legalDests.foreach { sq => @@ -289,7 +288,7 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B private def doPgnImport(): Unit = doImport(PgnParser, "PGN") - + private def doExport(exporter: GameContextExport, formatName: String): Unit = { val exported = exporter.exportGameContext(engine.context) showCopyDialog(s"$formatName Export", exported) diff --git a/modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala b/modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala index b23a436..a71a531 100644 --- a/modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala +++ b/modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala @@ -3,8 +3,8 @@ package de.nowchess.ui.terminal import scala.io.StdIn import de.nowchess.api.move.PromotionPiece import de.nowchess.chess.engine.GameEngine -import de.nowchess.chess.observer.{Observer, GameEvent, *} -import de.nowchess.chess.view.Renderer +import de.nowchess.chess.observer.* +import de.nowchess.ui.utils.Renderer /** Terminal UI that implements Observer pattern. * Subscribes to GameEngine and receives state change events. diff --git a/modules/core/src/main/scala/de/nowchess/chess/view/PieceUnicode.scala b/modules/ui/src/main/scala/de/nowchess/ui/utils/PieceUnicode.scala similarity index 96% rename from modules/core/src/main/scala/de/nowchess/chess/view/PieceUnicode.scala rename to modules/ui/src/main/scala/de/nowchess/ui/utils/PieceUnicode.scala index db1210a..96c9548 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/view/PieceUnicode.scala +++ b/modules/ui/src/main/scala/de/nowchess/ui/utils/PieceUnicode.scala @@ -1,4 +1,4 @@ -package de.nowchess.chess.view +package de.nowchess.ui.utils import de.nowchess.api.board.{Color, Piece, PieceType} diff --git a/modules/core/src/main/scala/de/nowchess/chess/view/Renderer.scala b/modules/ui/src/main/scala/de/nowchess/ui/utils/Renderer.scala similarity index 91% rename from modules/core/src/main/scala/de/nowchess/chess/view/Renderer.scala rename to modules/ui/src/main/scala/de/nowchess/ui/utils/Renderer.scala index b572860..8c9ef47 100644 --- a/modules/core/src/main/scala/de/nowchess/chess/view/Renderer.scala +++ b/modules/ui/src/main/scala/de/nowchess/ui/utils/Renderer.scala @@ -1,6 +1,6 @@ -package de.nowchess.chess.view +package de.nowchess.ui.utils -import de.nowchess.api.board.{Board, Color, File, Rank, Square} +import de.nowchess.api.board.* object Renderer: diff --git a/modules/core/src/test/scala/de/nowchess/chess/view/PieceUnicodeTest.scala b/modules/ui/src/test/scala/de/nowchess/ui/utils/PieceUnicodeTest.scala similarity index 97% rename from modules/core/src/test/scala/de/nowchess/chess/view/PieceUnicodeTest.scala rename to modules/ui/src/test/scala/de/nowchess/ui/utils/PieceUnicodeTest.scala index 9cd29ee..6c65d87 100644 --- a/modules/core/src/test/scala/de/nowchess/chess/view/PieceUnicodeTest.scala +++ b/modules/ui/src/test/scala/de/nowchess/ui/utils/PieceUnicodeTest.scala @@ -1,4 +1,4 @@ -package de.nowchess.chess.view +package de.nowchess.ui.utils import de.nowchess.api.board.Piece import org.scalatest.funsuite.AnyFunSuite diff --git a/modules/core/src/test/scala/de/nowchess/chess/view/RendererTest.scala b/modules/ui/src/test/scala/de/nowchess/ui/utils/RendererTest.scala similarity index 94% rename from modules/core/src/test/scala/de/nowchess/chess/view/RendererTest.scala rename to modules/ui/src/test/scala/de/nowchess/ui/utils/RendererTest.scala index 58bea70..3d41122 100644 --- a/modules/core/src/test/scala/de/nowchess/chess/view/RendererTest.scala +++ b/modules/ui/src/test/scala/de/nowchess/ui/utils/RendererTest.scala @@ -1,6 +1,6 @@ -package de.nowchess.chess.view +package de.nowchess.ui.utils -import de.nowchess.api.board.{Board, File, Piece, Rank, Square} +import de.nowchess.api.board.* import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers