Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ff80318b4 | |||
| 9fb743d135 |
+5
-1
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id("org.sonarqube") version "7.2.3.7755"
|
||||
id("org.scoverage") version "8.1" apply false
|
||||
}
|
||||
|
||||
group = "de.nowchess"
|
||||
@@ -28,7 +29,10 @@ val versions = mapOf(
|
||||
"SCALA_LIBRARY" to "2.13.18",
|
||||
"SCALATEST" to "3.2.19",
|
||||
"SCALATEST_JUNIT" to "0.1.11",
|
||||
"SCOVERAGE" to "2.1.1"
|
||||
"SCOVERAGE" to "2.1.1",
|
||||
"SCALAFX" to "21.0.0-R32",
|
||||
"JAVAFX" to "21.0.1",
|
||||
"JUNIT_BOM" to "5.13.4"
|
||||
)
|
||||
extra["VERSIONS"] = versions
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
## (2026-03-29)
|
||||
## (2026-03-31)
|
||||
## (2026-04-01)
|
||||
## (2026-04-01)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=0
|
||||
PATCH=6
|
||||
PATCH=7
|
||||
|
||||
@@ -99,3 +99,24 @@
|
||||
* correct test board positions and captureOutput/withInput interaction ([f0481e2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f0481e2561b779df00925b46ee281dc36a795150))
|
||||
* update main class path in build configuration and adjust VCS directory mapping ([7b1f8b1](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7b1f8b117623d327232a1a92a8a44d18582e0189))
|
||||
* update move validation to check for king safety ([#13](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/13)) ([e5e20c5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e5e20c566e368b12ca1dc59680c34e9112bf6762))
|
||||
## (2026-04-01)
|
||||
|
||||
### Features
|
||||
|
||||
* add GameRules stub with PositionStatus enum ([76d4168](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/76d4168038de23e5d6083d4e8f0504fbf31d15a3))
|
||||
* add MovedInCheck/Checkmate/Stalemate MoveResult variants (stub dispatch) ([8b7ec57](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/8b7ec57e5ea6ee1615a1883848a426dc07d26364))
|
||||
* implement GameRules with isInCheck, legalMoves, gameStatus ([94a02ff](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/94a02ff6849436d9496c70a0f16c21666dae8e4e))
|
||||
* implement legal castling ([#1](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/1)) ([00d326c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/00d326c1ba67711fbe180f04e1100c3f01dd0254))
|
||||
* NCS-10 Implement Pawn Promotion ([#12](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/12)) ([13bfc16](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/13bfc16cfe25db78ec607db523ca6d993c13430c))
|
||||
* NCS-11 50-move rule ([#9](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/9)) ([412ed98](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/412ed986a95703a3b282276540153480ceed229d))
|
||||
* NCS-16 Core Separation via Patterns ([#10](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/10)) ([1361dfc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1361dfc89553b146864fb8ff3526cf12cf3f293a))
|
||||
* NCS-6 Implementing FEN & PGN ([#7](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/7)) ([f28e69d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f28e69dc181416aa2f221fdc4b45c2cda5efbf07))
|
||||
* NCS-9 En passant implementation ([#8](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/8)) ([919beb3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/919beb3b4bfa8caf2f90976a415fe9b19b7e9747))
|
||||
* wire check/checkmate/stalemate into processMove and gameLoop ([5264a22](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5264a225418b885c5e6ea6411b96f85e38837f6c))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add missing kings to gameLoop capture test board ([aedd787](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/aedd787b77203c2af934751dba7b784eaf165032))
|
||||
* correct test board positions and captureOutput/withInput interaction ([f0481e2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f0481e2561b779df00925b46ee281dc36a795150))
|
||||
* update main class path in build configuration and adjust VCS directory mapping ([7b1f8b1](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7b1f8b117623d327232a1a92a8a44d18582e0189))
|
||||
* update move validation to check for king safety ([#13](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/13)) ([e5e20c5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e5e20c566e368b12ca1dc59680c34e9112bf6762))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package de.nowchess.chess.engine
|
||||
|
||||
import scala.collection.mutable
|
||||
import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
|
||||
import de.nowchess.api.board.{Board, Color}
|
||||
import de.nowchess.chess.logic.GameHistory
|
||||
import de.nowchess.chess.observer.*
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
@@ -86,28 +86,6 @@ class GameEngineLoadPgnTest extends AnyFunSuite with Matchers:
|
||||
// state is reset to initial (reset happens before replay, which fails)
|
||||
engine.history.moves shouldBe empty
|
||||
|
||||
test("loadPgn: promotion in PGN is replayed correctly"):
|
||||
val engine = new GameEngine()
|
||||
val pgn =
|
||||
"""[Event "T"]
|
||||
|
||||
1. b4 h6 2. b5 h5 3. b6 h4 4. bxa7 h3 5. a8=Q
|
||||
"""
|
||||
engine.loadPgn(pgn) shouldBe Right(())
|
||||
engine.history.moves.length shouldBe 9
|
||||
|
||||
test("loadPgn: en passant capture in PGN is replayed correctly"):
|
||||
val engine = new GameEngine()
|
||||
val pgn =
|
||||
"""[Event "T"]
|
||||
|
||||
1. e4 e6 2. e5 d5 3. exd6
|
||||
"""
|
||||
engine.loadPgn(pgn) shouldBe Right(())
|
||||
engine.history.moves.length shouldBe 5
|
||||
engine.board.pieceAt(Square(File.D, Rank.R6)) shouldBe Some(Piece(Color.White, PieceType.Pawn))
|
||||
engine.board.pieceAt(Square(File.D, Rank.R5)) shouldBe None
|
||||
|
||||
// ── undo/redo notation events ─────────────────────────────────────────────
|
||||
|
||||
test("undo emits MoveUndoneEvent with pgnNotation"):
|
||||
@@ -162,16 +140,18 @@ class GameEngineLoadPgnTest extends AnyFunSuite with Matchers:
|
||||
val engine = new GameEngine()
|
||||
val cap = new EventCapture()
|
||||
engine.subscribe(cap)
|
||||
// e4, then Black plays d5, White pawn on e4 captures on d5
|
||||
engine.processUserInput("e2e4")
|
||||
engine.processUserInput("d7d5")
|
||||
engine.processUserInput("e4d5") // white pawn captures black pawn
|
||||
// White builds a capture on the a-file: b4, ... a6, b5, ... h6, bxa6
|
||||
engine.processUserInput("b2b4")
|
||||
engine.processUserInput("a7a6")
|
||||
engine.processUserInput("b4b5")
|
||||
engine.processUserInput("h7h6")
|
||||
engine.processUserInput("b5a6") // white pawn captures black pawn
|
||||
engine.undo()
|
||||
cap.events.clear()
|
||||
engine.redo()
|
||||
val evt = cap.events.last.asInstanceOf[MoveRedoneEvent]
|
||||
evt.fromSquare shouldBe "e4"
|
||||
evt.toSquare shouldBe "d5"
|
||||
evt.fromSquare shouldBe "b5"
|
||||
evt.toSquare shouldBe "a6"
|
||||
evt.capturedPiece.isDefined shouldBe true
|
||||
|
||||
test("loadPgn: clears previous game state before loading"):
|
||||
|
||||
@@ -96,32 +96,6 @@ class PgnValidatorTest extends AnyFunSuite with Matchers:
|
||||
game.moves.last.castleSide shouldBe Some(CastleSide.Queenside)
|
||||
case Left(err) => fail(s"Expected Right but got Left($err)")
|
||||
|
||||
test("validatePgn: valid promotion is accepted"):
|
||||
val pgn =
|
||||
"""[Event "Test"]
|
||||
|
||||
1. b4 h6 2. b5 h5 3. b6 h4 4. bxa7 h3 5. a8=Q
|
||||
"""
|
||||
PgnParser.validatePgn(pgn) match
|
||||
case Right(game) =>
|
||||
game.moves.takeWhile(m => m.promotionPiece.isEmpty).length shouldBe 8
|
||||
game.moves.last.promotionPiece shouldBe Some(PromotionPiece.Queen)
|
||||
case Left(err) => fail(s"Expected Right but got Left($err)")
|
||||
|
||||
test("validatePgn: en passant capture is parsed correctly"):
|
||||
val pgn =
|
||||
"""[Event "Test"]
|
||||
|
||||
1. e4 e6 2. e5 d5 3. exd6
|
||||
"""
|
||||
PgnParser.validatePgn(pgn) match
|
||||
case Right(game) =>
|
||||
game.moves.length shouldBe 5
|
||||
val epMove = game.moves.last
|
||||
epMove.isCapture shouldBe true
|
||||
epMove.pieceType shouldBe PieceType.Pawn
|
||||
case Left(err) => fail(s"Expected Right but got Left($err)")
|
||||
|
||||
test("validatePgn: disambiguation with two rooks is accepted"):
|
||||
val pieces: Map[Square, Piece] = Map(
|
||||
Square(File.A, Rank.R1) -> Piece(Color.White, PieceType.Rook),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=6
|
||||
MINOR=7
|
||||
PATCH=0
|
||||
|
||||
@@ -4,3 +4,9 @@
|
||||
|
||||
* NCS-10 Implement Pawn Promotion ([#12](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/12)) ([13bfc16](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/13bfc16cfe25db78ec607db523ca6d993c13430c))
|
||||
* NCS-16 Core Separation via Patterns ([#10](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/10)) ([1361dfc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1361dfc89553b146864fb8ff3526cf12cf3f293a))
|
||||
## (2026-04-01)
|
||||
|
||||
### Features
|
||||
|
||||
* NCS-10 Implement Pawn Promotion ([#12](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/12)) ([13bfc16](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/13bfc16cfe25db78ec607db523ca6d993c13430c))
|
||||
* NCS-16 Core Separation via Patterns ([#10](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/10)) ([1361dfc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1361dfc89553b146864fb8ff3526cf12cf3f293a))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("scala")
|
||||
id("org.scoverage") version "8.1"
|
||||
id("org.scoverage")
|
||||
application
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ dependencies {
|
||||
implementation(project(":modules:api"))
|
||||
|
||||
// ScalaFX dependencies
|
||||
implementation("org.scalafx:scalafx_3:21.0.0-R32")
|
||||
implementation("org.scalafx:scalafx_3:${versions["SCALAFX"]!!}")
|
||||
|
||||
// JavaFX dependencies for the current platform
|
||||
val javaFXVersion = "21.0.1"
|
||||
val javaFXVersion = versions["JAVAFX"]!!
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val platform = when {
|
||||
osName.contains("win") -> "win"
|
||||
@@ -71,7 +71,7 @@ dependencies {
|
||||
implementation("org.openjfx:javafx-$module:$javaFXVersion:$platform")
|
||||
}
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||
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"]!!}")
|
||||
|
||||
@@ -23,10 +23,11 @@ import de.nowchess.chess.notation.{FenExporter, FenParser, PgnExporter, PgnParse
|
||||
class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends BorderPane:
|
||||
|
||||
private val squareSize = 70.0
|
||||
private val comicSansFontFamily = "Comic Sans MS"
|
||||
private val boardGrid = new GridPane()
|
||||
private val messageLabel = new Label {
|
||||
text = "Welcome!"
|
||||
font = Font.font("Comic Sans MS", 16)
|
||||
font = Font.font(comicSansFontFamily, 16)
|
||||
padding = Insets(10)
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B
|
||||
children = Seq(
|
||||
new Label {
|
||||
text = "Chess"
|
||||
font = Font.font("Comic Sans MS", 24)
|
||||
font = Font.font(comicSansFontFamily, 24)
|
||||
style = "-fx-font-weight: bold;"
|
||||
},
|
||||
messageLabel
|
||||
@@ -69,17 +70,17 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B
|
||||
alignment = Pos.Center
|
||||
children = Seq(
|
||||
new Button("Undo") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => if engine.canUndo then engine.undo()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #B9DAD1;"
|
||||
},
|
||||
new Button("Redo") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => if engine.canRedo then engine.redo()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #B9C2DA;"
|
||||
},
|
||||
new Button("Reset") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => engine.reset()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #E1EAA9;"
|
||||
}
|
||||
@@ -90,22 +91,22 @@ class ChessBoardView(val stage: Stage, private val engine: GameEngine) extends B
|
||||
alignment = Pos.Center
|
||||
children = Seq(
|
||||
new Button("FEN Export") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => doFenExport()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #DAC4B9;"
|
||||
},
|
||||
new Button("FEN Import") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => doFenImport()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #DAD4B9;"
|
||||
},
|
||||
new Button("PGN Export") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => doPgnExport()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #C4DAB9;"
|
||||
},
|
||||
new Button("PGN Import") {
|
||||
font = Font.font("Comic Sans MS", 12)
|
||||
font = Font.font(comicSansFontFamily, 12)
|
||||
onAction = _ => doPgnImport()
|
||||
style = "-fx-background-radius: 8; -fx-background-color: #B9DAC4;"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=1
|
||||
MINOR=2
|
||||
PATCH=0
|
||||
|
||||
Reference in New Issue
Block a user