Compare commits
2 Commits
rule-0.6.0
...
api-0.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b5a2966ada | |||
| 74a4fce0ca |
Generated
+1
-1
@@ -5,7 +5,7 @@
|
||||
<option name="deprecationWarnings" value="true" />
|
||||
<option name="uncheckedWarnings" value="true" />
|
||||
</profile>
|
||||
<profile name="Gradle 2" modules="NowChessSystems.modules.bot.main,NowChessSystems.modules.bot.scoverage,NowChessSystems.modules.bot.test,NowChessSystems.modules.core.integrationTest,NowChessSystems.modules.core.main,NowChessSystems.modules.core.native-test,NowChessSystems.modules.core.quarkus-generated-sources,NowChessSystems.modules.core.quarkus-test-generated-sources,NowChessSystems.modules.core.scoverage,NowChessSystems.modules.core.test,NowChessSystems.modules.io.main,NowChessSystems.modules.io.scoverage,NowChessSystems.modules.io.test,NowChessSystems.modules.rule.main,NowChessSystems.modules.rule.scoverage,NowChessSystems.modules.rule.test,NowChessSystems.modules.ui.main,NowChessSystems.modules.ui.scoverage,NowChessSystems.modules.ui.test">
|
||||
<profile name="Gradle 2" modules="NowChessSystems.modules.bot.main,NowChessSystems.modules.bot.scoverage,NowChessSystems.modules.bot.test,NowChessSystems.modules.core.integrationTest,NowChessSystems.modules.core.main,NowChessSystems.modules.core.native-test,NowChessSystems.modules.core.quarkus-generated-sources,NowChessSystems.modules.core.quarkus-test-generated-sources,NowChessSystems.modules.core.scoverage,NowChessSystems.modules.core.test,NowChessSystems.modules.io.integrationTest,NowChessSystems.modules.io.main,NowChessSystems.modules.io.native-test,NowChessSystems.modules.io.quarkus-generated-sources,NowChessSystems.modules.io.quarkus-test-generated-sources,NowChessSystems.modules.io.scoverage,NowChessSystems.modules.io.test,NowChessSystems.modules.rule.main,NowChessSystems.modules.rule.scoverage,NowChessSystems.modules.rule.test,NowChessSystems.modules.ui.main,NowChessSystems.modules.ui.scoverage,NowChessSystems.modules.ui.test">
|
||||
<option name="deprecationWarnings" value="true" />
|
||||
<option name="uncheckedWarnings" value="true" />
|
||||
<parameters>
|
||||
|
||||
@@ -9,6 +9,7 @@ Scala 3.5.1 · Gradle 9
|
||||
./compile # Compile all modules — always run
|
||||
./test # Run all tests
|
||||
./coverage # Check coverage
|
||||
./lint # Run linters
|
||||
```
|
||||
Use consistently.
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ Scala 3.5.1 · Gradle 9
|
||||
./compile # Compile all modules — always run
|
||||
./test # Run all tests
|
||||
./coverage # Check coverage
|
||||
./lint # Run linters
|
||||
```
|
||||
Try to stick to these commands for consistency.
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
vars {
|
||||
baseUrl: http://localhost:8080
|
||||
ioBaseUrl: http://localhost:8081
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
meta {
|
||||
name: Export FEN
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
http {
|
||||
method: POST
|
||||
url: {{ioBaseUrl}}/io/export/fen
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"board": {
|
||||
"a1": {"color": "White", "pieceType": "Rook"},
|
||||
"b1": {"color": "White", "pieceType": "Knight"},
|
||||
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||
"d1": {"color": "White", "pieceType": "Queen"},
|
||||
"e1": {"color": "White", "pieceType": "King"},
|
||||
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||
"g1": {"color": "White", "pieceType": "Knight"},
|
||||
"h1": {"color": "White", "pieceType": "Rook"},
|
||||
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||
"e8": {"color": "Black", "pieceType": "King"},
|
||||
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||
},
|
||||
"turn": "White",
|
||||
"castlingRights": {
|
||||
"whiteKingSide": true,
|
||||
"whiteQueenSide": true,
|
||||
"blackKingSide": true,
|
||||
"blackQueenSide": true
|
||||
},
|
||||
"enPassantSquare": null,
|
||||
"halfMoveClock": 0,
|
||||
"moves": [],
|
||||
"result": null,
|
||||
"initialBoard": {
|
||||
"a1": {"color": "White", "pieceType": "Rook"},
|
||||
"b1": {"color": "White", "pieceType": "Knight"},
|
||||
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||
"d1": {"color": "White", "pieceType": "Queen"},
|
||||
"e1": {"color": "White", "pieceType": "King"},
|
||||
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||
"g1": {"color": "White", "pieceType": "Knight"},
|
||||
"h1": {"color": "White", "pieceType": "Rook"},
|
||||
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||
"e8": {"color": "Black", "pieceType": "King"},
|
||||
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
meta {
|
||||
name: Export PGN
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
http {
|
||||
method: POST
|
||||
url: {{ioBaseUrl}}/io/export/pgn
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"board": {
|
||||
"a1": {"color": "White", "pieceType": "Rook"},
|
||||
"b1": {"color": "White", "pieceType": "Knight"},
|
||||
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||
"d1": {"color": "White", "pieceType": "Queen"},
|
||||
"e1": {"color": "White", "pieceType": "King"},
|
||||
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||
"g1": {"color": "White", "pieceType": "Knight"},
|
||||
"h1": {"color": "White", "pieceType": "Rook"},
|
||||
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||
"e8": {"color": "Black", "pieceType": "King"},
|
||||
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||
},
|
||||
"turn": "White",
|
||||
"castlingRights": {
|
||||
"whiteKingSide": true,
|
||||
"whiteQueenSide": true,
|
||||
"blackKingSide": true,
|
||||
"blackQueenSide": true
|
||||
},
|
||||
"enPassantSquare": null,
|
||||
"halfMoveClock": 0,
|
||||
"moves": [],
|
||||
"result": null,
|
||||
"initialBoard": {
|
||||
"a1": {"color": "White", "pieceType": "Rook"},
|
||||
"b1": {"color": "White", "pieceType": "Knight"},
|
||||
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||
"d1": {"color": "White", "pieceType": "Queen"},
|
||||
"e1": {"color": "White", "pieceType": "King"},
|
||||
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||
"g1": {"color": "White", "pieceType": "Knight"},
|
||||
"h1": {"color": "White", "pieceType": "Rook"},
|
||||
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||
"e8": {"color": "Black", "pieceType": "King"},
|
||||
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
meta {
|
||||
name: export
|
||||
seq: 2
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
meta {
|
||||
name: Import FEN
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
http {
|
||||
method: POST
|
||||
url: {{ioBaseUrl}}/io/import/fen
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
meta {
|
||||
name: Import PGN
|
||||
type: http
|
||||
seq: 2
|
||||
}
|
||||
|
||||
http {
|
||||
method: POST
|
||||
url: {{ioBaseUrl}}/io/import/pgn
|
||||
body: json
|
||||
auth: none
|
||||
}
|
||||
|
||||
headers {
|
||||
Content-Type: application/json
|
||||
}
|
||||
|
||||
body:json {
|
||||
{
|
||||
"pgn": "1. e4 e5 2. Nf3 Nc6 *"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
meta {
|
||||
name: import
|
||||
seq: 1
|
||||
}
|
||||
+6
-2
@@ -36,7 +36,11 @@ val coverageExclusions = listOf(
|
||||
"**/core/src/main/scala/de/nowchess/chess/registry/GameEntry.scala",
|
||||
"**/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala",
|
||||
// GameResource — REST integration layer with @Inject var fields; mocking dependencies for unit tests is infeasible with Quarkus DI; integration tests would require @QuarkusTest which Scoverage doesn't instrument
|
||||
"**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala"
|
||||
"**/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala",
|
||||
// IoResource — same rationale as GameResource; @QuarkusTest not instrumented by Scoverage
|
||||
"**/io/src/main/scala/de/nowchess/io/service/resource/IoResource.scala",
|
||||
// JacksonConfig — Quarkus lifecycle hook, no testable logic beyond ObjectMapper registration
|
||||
"**/io/src/main/scala/de/nowchess/io/service/config/JacksonConfig.scala",
|
||||
)
|
||||
|
||||
// Converts a Sonar-style glob to a scoverage regex (matched against full source path).
|
||||
@@ -72,7 +76,7 @@ sonar {
|
||||
val versions = mapOf(
|
||||
"QUARKUS_SCALA3" to "1.0.0",
|
||||
"SCALA3" to "3.5.1",
|
||||
"SCALA_LIBRARY" to "2.13.18",
|
||||
"SCALA_LIBRARY" to "2.13.16",
|
||||
"SCALATEST" to "3.2.19",
|
||||
"SCALATEST_JUNIT" to "0.1.11",
|
||||
"SCOVERAGE" to "2.1.1",
|
||||
|
||||
@@ -60,3 +60,13 @@
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
## (2026-04-21)
|
||||
|
||||
### Features
|
||||
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=8
|
||||
MINOR=9
|
||||
PATCH=0
|
||||
|
||||
@@ -341,3 +341,32 @@
|
||||
* 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-21)
|
||||
|
||||
### 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-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* 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-17 Implement basic ScalaFX UI ([#14](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/14)) ([3ff8031](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3ff80318b4f16c59733a46498581a5c27f048287))
|
||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||
* NCS-40 Rework Draw System ([#34](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/34)) ([0091d50](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/0091d50467e9f955f23570128b96c977c01bc51b))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
* 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))
|
||||
|
||||
@@ -74,6 +74,7 @@ dependencies {
|
||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
||||
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
||||
testImplementation("io.quarkus:quarkus-junit5")
|
||||
testImplementation("io.quarkus:quarkus-junit5-mockito")
|
||||
testImplementation("io.rest-assured:rest-assured")
|
||||
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
greeting:
|
||||
message: "hello"
|
||||
|
||||
quarkus:
|
||||
http:
|
||||
port: 8080
|
||||
application:
|
||||
name: nowchess-core
|
||||
rest-client:
|
||||
io-service:
|
||||
url: http://localhost:8081
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package de.nowchess.chess.client
|
||||
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest}
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient
|
||||
|
||||
@Path("/io")
|
||||
@RegisterRestClient(configKey = "io-service")
|
||||
trait IoServiceClient:
|
||||
|
||||
@POST
|
||||
@Path("/import/fen")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def importFen(body: ImportFenRequest): GameContext
|
||||
|
||||
@POST
|
||||
@Path("/import/pgn")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def importPgn(body: ImportPgnRequest): GameContext
|
||||
|
||||
@POST
|
||||
@Path("/export/fen")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||
def exportFen(ctx: GameContext): String
|
||||
|
||||
@POST
|
||||
@Path("/export/pgn")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array("application/x-chess-pgn"))
|
||||
def exportPgn(ctx: GameContext): String
|
||||
@@ -2,7 +2,10 @@ package de.nowchess.chess.config
|
||||
|
||||
import com.fasterxml.jackson.core.Version
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import de.nowchess.api.board.Square
|
||||
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
|
||||
import io.quarkus.jackson.ObjectMapperCustomizer
|
||||
import jakarta.inject.Singleton
|
||||
|
||||
@@ -15,3 +18,7 @@ class JacksonConfig extends ObjectMapperCustomizer:
|
||||
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
||||
// scalafix:on DisableSyntax.null
|
||||
})
|
||||
val squareModule = new SimpleModule()
|
||||
squareModule.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||
squareModule.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||
mapper.registerModule(squareModule)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package de.nowchess.chess.config
|
||||
|
||||
import de.nowchess.api.board.{CastlingRights, Color, File, Piece, PieceType, Rank, Square}
|
||||
import de.nowchess.api.dto.*
|
||||
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
|
||||
@RegisterForReflection(
|
||||
@@ -18,6 +21,19 @@ import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
classOf[LegalMovesResponseDto],
|
||||
classOf[OkResponseDto],
|
||||
classOf[PlayerInfoDto],
|
||||
classOf[GameContext],
|
||||
classOf[Color],
|
||||
classOf[Piece],
|
||||
classOf[PieceType],
|
||||
classOf[CastlingRights],
|
||||
classOf[Square],
|
||||
classOf[File],
|
||||
classOf[Rank],
|
||||
classOf[Move],
|
||||
classOf[MoveType],
|
||||
classOf[PromotionPiece],
|
||||
classOf[GameResult],
|
||||
classOf[DrawReason],
|
||||
),
|
||||
)
|
||||
class NativeReflectionConfig
|
||||
|
||||
@@ -6,18 +6,21 @@ import de.nowchess.api.dto.*
|
||||
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
||||
import de.nowchess.chess.client.IoServiceClient
|
||||
import de.nowchess.chess.controller.Parser
|
||||
import de.nowchess.chess.engine.GameEngine
|
||||
import de.nowchess.chess.exception.{BadRequestException, GameNotFoundException}
|
||||
import de.nowchess.chess.observer.*
|
||||
import de.nowchess.chess.registry.{GameEntry, GameRegistry}
|
||||
import de.nowchess.io.fen.{FenExporter, FenParser}
|
||||
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
||||
import de.nowchess.io.fen.FenExporter
|
||||
import de.nowchess.io.pgn.PgnExporter
|
||||
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest}
|
||||
import io.smallrye.mutiny.Multi
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.{MediaType, Response}
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import scala.compiletime.uninitialized
|
||||
@@ -32,6 +35,10 @@ class GameResource:
|
||||
|
||||
@Inject
|
||||
var objectMapper: ObjectMapper = uninitialized
|
||||
|
||||
@Inject
|
||||
@RestClient
|
||||
var ioClient: IoServiceClient = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
||||
@@ -142,7 +149,6 @@ class GameResource:
|
||||
val black = playerInfoFrom(req.black, DefaultBlack)
|
||||
val entry = newEntry(GameContext.initial, white, black)
|
||||
registry.store(entry)
|
||||
println(s"Created game ${entry.gameId}")
|
||||
created(toGameFullDto(entry))
|
||||
|
||||
@GET
|
||||
@@ -264,9 +270,7 @@ class GameResource:
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def importFen(body: ImportFenRequestDto): Response =
|
||||
val ctx = FenParser.parseFen(body.fen) match
|
||||
case Left(err) => throw BadRequestException("INVALID_FEN", err, Some("fen"))
|
||||
case Right(ctx) => ctx
|
||||
val ctx = ioClient.importFen(ImportFenRequest(body.fen))
|
||||
val white = playerInfoFrom(body.white, DefaultWhite)
|
||||
val black = playerInfoFrom(body.black, DefaultBlack)
|
||||
val entry = newEntry(ctx, white, black)
|
||||
@@ -278,11 +282,8 @@ class GameResource:
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
def importPgn(body: ImportPgnRequestDto): Response =
|
||||
val engine = GameEngine()
|
||||
engine.loadGame(PgnParser, body.pgn) match
|
||||
case Left(err) => throw BadRequestException("INVALID_PGN", err, Some("pgn"))
|
||||
case Right(_) => ()
|
||||
val entry = GameEntry(registry.generateId(), engine, DefaultWhite, DefaultBlack)
|
||||
val ctx = ioClient.importPgn(ImportPgnRequest(body.pgn))
|
||||
val entry = newEntry(ctx, DefaultWhite, DefaultBlack)
|
||||
registry.store(entry)
|
||||
created(toGameFullDto(entry))
|
||||
|
||||
@@ -291,21 +292,12 @@ class GameResource:
|
||||
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||
def exportFen(@PathParam("gameId") gameId: String): Response =
|
||||
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||
ok(FenExporter.exportGameContext(entry.engine.context))
|
||||
ok(ioClient.exportFen(entry.engine.context))
|
||||
|
||||
@GET
|
||||
@Path("/{gameId}/export/pgn")
|
||||
@Produces(Array("application/x-chess-pgn"))
|
||||
def exportPgn(@PathParam("gameId") gameId: String): Response =
|
||||
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||
val pgn = PgnExporter.exportGame(
|
||||
Map(
|
||||
"Event" -> "NowChess game",
|
||||
"White" -> entry.white.displayName,
|
||||
"Black" -> entry.black.displayName,
|
||||
"Result" -> "*",
|
||||
),
|
||||
entry.engine.context.moves,
|
||||
)
|
||||
ok(pgn)
|
||||
ok(ioClient.exportPgn(entry.engine.context))
|
||||
// scalafix:on DisableSyntax.throw
|
||||
|
||||
+22
-1
@@ -1,11 +1,19 @@
|
||||
package de.nowchess.chess.resource
|
||||
|
||||
import de.nowchess.api.dto.*
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.chess.client.IoServiceClient
|
||||
import de.nowchess.chess.exception.BadRequestException
|
||||
import de.nowchess.io.fen.FenExporter
|
||||
import de.nowchess.io.pgn.PgnParser
|
||||
import io.quarkus.test.InjectMock
|
||||
import io.quarkus.test.junit.QuarkusTest
|
||||
import jakarta.inject.Inject
|
||||
import org.junit.jupiter.api.{DisplayName, Test}
|
||||
import org.eclipse.microprofile.rest.client.inject.RestClient
|
||||
import org.junit.jupiter.api.{BeforeEach, DisplayName, Test}
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mockito.when
|
||||
|
||||
import scala.compiletime.uninitialized
|
||||
|
||||
@@ -17,6 +25,19 @@ class GameResourceIntegrationTest:
|
||||
@Inject
|
||||
var resource: GameResource = uninitialized
|
||||
|
||||
@InjectMock
|
||||
@RestClient
|
||||
var ioClient: IoServiceClient = uninitialized
|
||||
|
||||
@BeforeEach
|
||||
def setupMocks(): Unit =
|
||||
when(ioClient.importFen(any())).thenReturn(GameContext.initial)
|
||||
when(ioClient.importPgn(any())).thenReturn(
|
||||
PgnParser.importGameContext("1. e4 c5").toOption.get,
|
||||
)
|
||||
when(ioClient.exportFen(any())).thenReturn(FenExporter.exportGameContext(GameContext.initial))
|
||||
when(ioClient.exportPgn(any())).thenReturn("1. e4 c5")
|
||||
|
||||
@Test
|
||||
@DisplayName("createGame returns 201")
|
||||
def testCreateGame(): Unit =
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=16
|
||||
MINOR=17
|
||||
PATCH=0
|
||||
|
||||
@@ -76,3 +76,14 @@
|
||||
* NCS-30 FEN Parser using ParserCombinators ([#21](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/21)) ([b4bc72f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b4bc72f7e49f94d6e1bc805c68680e5fe8ef8e36))
|
||||
* NCS-31 FastParse FEN ([#22](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/22)) ([7a045d3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7a045d31d757bbc5aa6f4bad2664ebe8b8519cac))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
## (2026-04-21)
|
||||
|
||||
### Features
|
||||
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-29 JSON - Cherry Picked ([#28](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/28)) ([dbcafd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dbcafd286993e0604a6fa286c5543581a149439e))
|
||||
* NCS-30 FEN Parser using ParserCombinators ([#21](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/21)) ([b4bc72f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b4bc72f7e49f94d6e1bc805c68680e5fe8ef8e36))
|
||||
* NCS-31 FastParse FEN ([#22](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/22)) ([7a045d3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7a045d31d757bbc5aa6f4bad2664ebe8b8519cac))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id("scala")
|
||||
id("org.scoverage") version "8.1"
|
||||
id("io.quarkus")
|
||||
}
|
||||
|
||||
group = "de.nowchess"
|
||||
@@ -28,6 +29,10 @@ tasks.withType<ScalaCompile> {
|
||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
||||
}
|
||||
|
||||
val quarkusPlatformGroupId: String by project
|
||||
val quarkusPlatformArtifactId: String by project
|
||||
val quarkusPlatformVersion: String by project
|
||||
|
||||
dependencies {
|
||||
|
||||
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||
@@ -53,19 +58,51 @@ dependencies {
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
||||
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions["JACKSON"]!!}")
|
||||
|
||||
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
||||
implementation("io.quarkus:quarkus-rest")
|
||||
implementation("io.quarkus:quarkus-rest-jackson")
|
||||
implementation("io.quarkus:quarkus-arc")
|
||||
implementation("io.quarkus:quarkus-config-yaml")
|
||||
implementation("io.quarkus:quarkus-smallrye-health")
|
||||
implementation("io.quarkus:quarkus-smallrye-openapi")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
||||
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
||||
testImplementation("io.quarkus:quarkus-junit5")
|
||||
testImplementation("io.rest-assured:rest-assured")
|
||||
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||
}
|
||||
|
||||
|
||||
configurations.matching { !it.name.startsWith("scoverage") }.configureEach {
|
||||
resolutionStrategy.force("org.scala-lang:scala-library:${versions["SCALA_LIBRARY"]!!}")
|
||||
}
|
||||
configurations.scoverage {
|
||||
resolutionStrategy.eachDependency {
|
||||
if (requested.group == "org.scoverage" && requested.name.startsWith("scalac-scoverage-plugin_")) {
|
||||
useTarget("${requested.group}:scalac-scoverage-plugin_2.13.16:2.3.0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile> {
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
}
|
||||
|
||||
tasks.withType<Jar>().configureEach {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform {
|
||||
includeEngines("scalatest")
|
||||
includeEngines("scalatest", "junit-jupiter")
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
events("passed", "skipped", "failed")
|
||||
}
|
||||
}
|
||||
finalizedBy(tasks.reportScoverage)
|
||||
@@ -73,3 +110,6 @@ tasks.test {
|
||||
tasks.reportScoverage {
|
||||
dependsOn(tasks.test)
|
||||
}
|
||||
tasks.jar {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
quarkus:
|
||||
http:
|
||||
port: 8081
|
||||
application:
|
||||
name: nowchess-io
|
||||
smallrye-openapi:
|
||||
info-title: NowChess IO Service
|
||||
info-version: 1.0.0
|
||||
info-description: Chess notation import and export — FEN and PGN
|
||||
path: /openapi
|
||||
swagger-ui:
|
||||
always-include: true
|
||||
path: /swagger-ui
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.nowchess.io.json
|
||||
|
||||
import com.fasterxml.jackson.databind.{DeserializationContext, KeyDeserializer}
|
||||
import de.nowchess.api.board.Square
|
||||
|
||||
class SquareKeyDeserializer extends KeyDeserializer:
|
||||
override def deserializeKey(key: String, ctx: DeserializationContext): AnyRef =
|
||||
Square.fromAlgebraic(key).orNull
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.nowchess.io.json
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.databind.{JsonSerializer, SerializerProvider}
|
||||
import de.nowchess.api.board.Square
|
||||
|
||||
class SquareKeySerializer extends JsonSerializer[Square]:
|
||||
override def serialize(value: Square, gen: JsonGenerator, provider: SerializerProvider): Unit =
|
||||
gen.writeFieldName(value.toString)
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.nowchess.io.service.config
|
||||
|
||||
import com.fasterxml.jackson.core.Version
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import de.nowchess.api.board.Square
|
||||
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
|
||||
import io.quarkus.jackson.ObjectMapperCustomizer
|
||||
import jakarta.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class JacksonConfig extends ObjectMapperCustomizer:
|
||||
def customize(mapper: ObjectMapper): Unit =
|
||||
mapper.registerModule(new DefaultScalaModule() {
|
||||
override def version(): Version =
|
||||
// scalafix:off DisableSyntax.null
|
||||
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
||||
// scalafix:on DisableSyntax.null
|
||||
})
|
||||
val squareModule = new SimpleModule()
|
||||
squareModule.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||
squareModule.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||
mapper.registerModule(squareModule)
|
||||
@@ -0,0 +1,29 @@
|
||||
package de.nowchess.io.service.config
|
||||
|
||||
import de.nowchess.api.board.{CastlingRights, Color, File, Piece, PieceType, Rank, Square}
|
||||
import de.nowchess.api.game.{DrawReason, GameContext, GameResult}
|
||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
||||
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest, IoErrorDto}
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
|
||||
@RegisterForReflection(
|
||||
targets = Array(
|
||||
classOf[ImportFenRequest],
|
||||
classOf[ImportPgnRequest],
|
||||
classOf[IoErrorDto],
|
||||
classOf[GameContext],
|
||||
classOf[GameResult],
|
||||
classOf[DrawReason],
|
||||
classOf[Color],
|
||||
classOf[Piece],
|
||||
classOf[PieceType],
|
||||
classOf[CastlingRights],
|
||||
classOf[Square],
|
||||
classOf[File],
|
||||
classOf[Rank],
|
||||
classOf[Move],
|
||||
classOf[MoveType],
|
||||
classOf[PromotionPiece],
|
||||
),
|
||||
)
|
||||
class NativeReflectionConfig
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.nowchess.io.service.dto
|
||||
|
||||
case class ImportFenRequest(fen: String)
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.nowchess.io.service.dto
|
||||
|
||||
case class ImportPgnRequest(pgn: String)
|
||||
@@ -0,0 +1,3 @@
|
||||
package de.nowchess.io.service.dto
|
||||
|
||||
case class IoErrorDto(code: String, message: String)
|
||||
@@ -0,0 +1,77 @@
|
||||
package de.nowchess.io.service.resource
|
||||
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.io.fen.{FenExporter, FenParser}
|
||||
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
||||
import de.nowchess.io.service.dto.{ImportFenRequest, ImportPgnRequest, IoErrorDto}
|
||||
import io.smallrye.mutiny.Uni
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.ws.rs.*
|
||||
import jakarta.ws.rs.core.{MediaType, Response}
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation
|
||||
import org.eclipse.microprofile.openapi.annotations.media.{Content, Schema}
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.{APIResponse, APIResponses}
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag
|
||||
|
||||
@Path("/io")
|
||||
@ApplicationScoped
|
||||
@Tag(name = "IO", description = "Chess notation import and export")
|
||||
class IoResource:
|
||||
|
||||
@POST
|
||||
@Path("/import/fen")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
@Operation(summary = "Import FEN", description = "Parse a FEN string into a GameContext")
|
||||
@APIResponses(
|
||||
Array(
|
||||
new APIResponse(responseCode = "200", description = "Parsed GameContext"),
|
||||
new APIResponse(responseCode = "400", description = "Invalid FEN"),
|
||||
),
|
||||
)
|
||||
def importFen(body: ImportFenRequest): Uni[Response] =
|
||||
Uni.createFrom().item {
|
||||
FenParser.parseFen(body.fen) match
|
||||
case Left(err) =>
|
||||
Response.status(400).entity(IoErrorDto("INVALID_FEN", err)).build()
|
||||
case Right(ctx) =>
|
||||
Response.ok(ctx).build()
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/import/pgn")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||
@Operation(summary = "Import PGN", description = "Parse a PGN string into a GameContext")
|
||||
@APIResponses(
|
||||
Array(
|
||||
new APIResponse(responseCode = "200", description = "Parsed GameContext"),
|
||||
new APIResponse(responseCode = "400", description = "Invalid PGN"),
|
||||
),
|
||||
)
|
||||
def importPgn(body: ImportPgnRequest): Uni[Response] =
|
||||
Uni.createFrom().item {
|
||||
PgnParser.importGameContext(body.pgn) match
|
||||
case Left(err) =>
|
||||
Response.status(400).entity(IoErrorDto("INVALID_PGN", err)).build()
|
||||
case Right(ctx) =>
|
||||
Response.ok(ctx).build()
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/export/fen")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array(MediaType.TEXT_PLAIN))
|
||||
@Operation(summary = "Export FEN", description = "Serialize a GameContext to FEN notation")
|
||||
@APIResponse(responseCode = "200", description = "FEN string")
|
||||
def exportFen(ctx: GameContext): Uni[Response] =
|
||||
Uni.createFrom().item(Response.ok(FenExporter.exportGameContext(ctx)).build())
|
||||
|
||||
@POST
|
||||
@Path("/export/pgn")
|
||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||
@Produces(Array("application/x-chess-pgn"))
|
||||
@Operation(summary = "Export PGN", description = "Serialize a GameContext to PGN notation")
|
||||
@APIResponse(responseCode = "200", description = "PGN text")
|
||||
def exportPgn(ctx: GameContext): Uni[Response] =
|
||||
Uni.createFrom().item(Response.ok(PgnExporter.exportGameContext(ctx)).build())
|
||||
@@ -0,0 +1,62 @@
|
||||
package de.nowchess.io.json
|
||||
|
||||
import com.fasterxml.jackson.core.`type`.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import de.nowchess.api.board.{File, Rank, Square}
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class SquareKeyDeserializerTest extends AnyFunSuite with Matchers:
|
||||
|
||||
private def mapper: ObjectMapper =
|
||||
val m = new ObjectMapper()
|
||||
val mod = new SimpleModule()
|
||||
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||
m.registerModule(DefaultScalaModule)
|
||||
m.registerModule(mod)
|
||||
m
|
||||
|
||||
private def readMap(json: String): Map[Square, Int] =
|
||||
mapper.readValue(json, new TypeReference[Map[Square, Int]] {})
|
||||
|
||||
test("deserializes valid algebraic key") {
|
||||
val result = readMap("""{"e4":1}""")
|
||||
result(Square(File.E, Rank.R4)) shouldBe 1
|
||||
}
|
||||
|
||||
test("deserializes a1 corner") {
|
||||
val result = readMap("""{"a1":1}""")
|
||||
result(Square(File.A, Rank.R1)) shouldBe 1
|
||||
}
|
||||
|
||||
test("deserializes h8 corner") {
|
||||
val result = readMap("""{"h8":1}""")
|
||||
result(Square(File.H, Rank.R8)) shouldBe 1
|
||||
}
|
||||
|
||||
test("deserializes multiple squares") {
|
||||
val result = readMap("""{"a1":1,"h8":2,"e4":3}""")
|
||||
result(Square(File.A, Rank.R1)) shouldBe 1
|
||||
result(Square(File.H, Rank.R8)) shouldBe 2
|
||||
result(Square(File.E, Rank.R4)) shouldBe 3
|
||||
}
|
||||
|
||||
// scalafix:off DisableSyntax.null
|
||||
test("deserializeKey returns null for invalid square") {
|
||||
new SquareKeyDeserializer().deserializeKey("invalid", null) shouldBe null
|
||||
}
|
||||
|
||||
test("deserializeKey returns null for wrong-length key") {
|
||||
new SquareKeyDeserializer().deserializeKey("e44", null) shouldBe null
|
||||
}
|
||||
|
||||
test("deserializeKey returns null for bad file") {
|
||||
new SquareKeyDeserializer().deserializeKey("z4", null) shouldBe null
|
||||
}
|
||||
|
||||
test("deserializeKey returns null for bad rank") {
|
||||
new SquareKeyDeserializer().deserializeKey("e9", null) shouldBe null
|
||||
}
|
||||
// scalafix:on DisableSyntax.null
|
||||
@@ -0,0 +1,50 @@
|
||||
package de.nowchess.io.json
|
||||
|
||||
import com.fasterxml.jackson.core.`type`.TypeReference
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import de.nowchess.api.board.{File, Rank, Square}
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class SquareKeySerializerTest extends AnyFunSuite with Matchers:
|
||||
|
||||
private def mapper: ObjectMapper =
|
||||
val m = new ObjectMapper()
|
||||
val mod = new SimpleModule()
|
||||
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||
m.registerModule(DefaultScalaModule)
|
||||
m.registerModule(mod)
|
||||
m
|
||||
|
||||
test("serializes square as algebraic notation") {
|
||||
val json = mapper.writeValueAsString(Map(Square(File.E, Rank.R4) -> 1))
|
||||
json should include("\"e4\"")
|
||||
}
|
||||
|
||||
test("serializes a1 corner") {
|
||||
val json = mapper.writeValueAsString(Map(Square(File.A, Rank.R1) -> 1))
|
||||
json should include("\"a1\"")
|
||||
}
|
||||
|
||||
test("serializes h8 corner") {
|
||||
val json = mapper.writeValueAsString(Map(Square(File.H, Rank.R8) -> 1))
|
||||
json should include("\"h8\"")
|
||||
}
|
||||
|
||||
test("round-trips with SquareKeyDeserializer") {
|
||||
val rt = {
|
||||
val m = new ObjectMapper()
|
||||
val mod = new SimpleModule()
|
||||
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||
m.registerModule(DefaultScalaModule)
|
||||
m.registerModule(mod)
|
||||
m
|
||||
}
|
||||
val original = Map(Square(File.D, Rank.R5) -> 99)
|
||||
val json = rt.writeValueAsString(original)
|
||||
val result = rt.readValue(json, new TypeReference[Map[Square, Int]] {})
|
||||
result shouldBe original
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package de.nowchess.io.service.resource
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||
import de.nowchess.api.board.Square
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.io.json.{SquareKeyDeserializer, SquareKeySerializer}
|
||||
import io.quarkus.test.junit.QuarkusTest
|
||||
import io.restassured.RestAssured
|
||||
import io.restassured.http.ContentType
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@QuarkusTest
|
||||
class IoResourceTest:
|
||||
private lazy val testMapper: ObjectMapper =
|
||||
val m = new ObjectMapper()
|
||||
val mod = new SimpleModule()
|
||||
mod.addKeySerializer(classOf[Square], new SquareKeySerializer())
|
||||
mod.addKeyDeserializer(classOf[Square], new SquareKeyDeserializer())
|
||||
m.registerModule(new DefaultScalaModule())
|
||||
m.registerModule(mod)
|
||||
m
|
||||
|
||||
private def contextJson(ctx: GameContext): String = testMapper.writeValueAsString(ctx)
|
||||
|
||||
@Test
|
||||
def importFenReturns200(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""{"fen":"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"}""")
|
||||
.post("/io/import/fen")
|
||||
assertEquals(200, resp.statusCode())
|
||||
|
||||
@Test
|
||||
def importFenInvalidReturns400(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""{"fen":"not-a-fen"}""")
|
||||
.post("/io/import/fen")
|
||||
assertEquals(400, resp.statusCode())
|
||||
|
||||
@Test
|
||||
def importPgnReturns200(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""{"pgn":"1. e4 e5"}""")
|
||||
.post("/io/import/pgn")
|
||||
assertEquals(200, resp.statusCode())
|
||||
|
||||
@Test
|
||||
def importPgnInvalidReturns400(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""{"pgn":"not valid pgn !!!###"}""")
|
||||
.post("/io/import/pgn")
|
||||
assertEquals(400, resp.statusCode())
|
||||
|
||||
@Test
|
||||
def exportFenReturns200WithFen(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(contextJson(GameContext.initial))
|
||||
.post("/io/export/fen")
|
||||
assertEquals(200, resp.statusCode())
|
||||
assertTrue(resp.getBody.asString().contains("rnbqkbnr"))
|
||||
|
||||
@Test
|
||||
def exportPgnReturns200(): Unit =
|
||||
val resp = RestAssured
|
||||
.`given`()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(contextJson(GameContext.initial))
|
||||
.post("/io/export/pgn")
|
||||
assertEquals(200, resp.statusCode())
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=10
|
||||
MINOR=11
|
||||
PATCH=0
|
||||
|
||||
@@ -79,3 +79,16 @@
|
||||
### Bug Fixes
|
||||
|
||||
* NCS-32 Queenside Castle doesn't care about pieces in the way ([#23](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/23)) ([fe8e3c0](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fe8e3c05397f433bfa34d1999e9738c82790adf7))
|
||||
## (2026-04-21)
|
||||
|
||||
### Features
|
||||
|
||||
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* NCS-32 Queenside Castle doesn't care about pieces in the way ([#23](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/23)) ([fe8e3c0](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fe8e3c05397f433bfa34d1999e9738c82790adf7))
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=5
|
||||
MINOR=6
|
||||
PATCH=0
|
||||
|
||||
Reference in New Issue
Block a user