feat: NCS-37 Quarkus integration (#35)
Reviewed-on: #35 Reviewed-by: Leon Hermann <lq@blackhole.local>
@@ -47,14 +47,21 @@
|
|||||||
- class Square
|
- class Square
|
||||||
- function fromAlgebraic
|
- function fromAlgebraic
|
||||||
- function offset
|
- function offset
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`
|
||||||
|
- class Bot
|
||||||
|
- function name
|
||||||
|
- function nextMove
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/ErrorEventDto.scala` — class ErrorEventDto, function apply
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/GameFullEventDto.scala` — class GameFullEventDto, function apply
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/GameStateEventDto.scala` — class GameStateEventDto, function apply
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`
|
||||||
|
- function kingSquare
|
||||||
- function withBoard
|
- function withBoard
|
||||||
- function withTurn
|
- function withTurn
|
||||||
- function withCastlingRights
|
- function withCastlingRights
|
||||||
- function withEnPassantSquare
|
- function withEnPassantSquare
|
||||||
- function withHalfMoveClock
|
- function withHalfMoveClock
|
||||||
- function withMove
|
- _...4 more_
|
||||||
- _...2 more_
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — class PlayerId, function apply
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — class PlayerId, function apply
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/response/ApiResponse.scala`
|
- `modules/api/src/main/scala/de/nowchess/api/response/ApiResponse.scala`
|
||||||
- class ApiResponse
|
- class ApiResponse
|
||||||
@@ -79,6 +86,7 @@
|
|||||||
- `modules/bot/python/src/export.py` — function export_to_nbai: (weights_file, output_file, trained_by, train_loss)
|
- `modules/bot/python/src/export.py` — function export_to_nbai: (weights_file, output_file, trained_by, train_loss)
|
||||||
- `modules/bot/python/src/generate.py` — function play_random_game_and_collect_positions: (output_file, total_positions, samples_per_game, min_move, max_move, num_workers)
|
- `modules/bot/python/src/generate.py` — function play_random_game_and_collect_positions: (output_file, total_positions, samples_per_game, min_move, max_move, num_workers)
|
||||||
- `modules/bot/python/src/label.py` — function normalize_evaluation: (cp_value, method, scale), function label_positions_with_stockfish: (positions_file, output_file, stockfish_path, batch_size, depth, verbose, normalize, num_workers)
|
- `modules/bot/python/src/label.py` — function normalize_evaluation: (cp_value, method, scale), function label_positions_with_stockfish: (positions_file, output_file, stockfish_path, batch_size, depth, verbose, normalize, num_workers)
|
||||||
|
- `modules/bot/python/src/lichess_importer.py` — function import_lichess_evals: (input_path, output_file, max_positions, min_depth, verbose) -> int
|
||||||
- `modules/bot/python/src/tactical_positions_extractor.py`
|
- `modules/bot/python/src/tactical_positions_extractor.py`
|
||||||
- function download_and_extract_puzzle_db: (url, output_dir)
|
- function download_and_extract_puzzle_db: (url, output_dir)
|
||||||
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
||||||
@@ -90,14 +98,10 @@
|
|||||||
- function fen_to_features: (fen)
|
- function fen_to_features: (fen)
|
||||||
- function find_next_version: (base_name)
|
- function find_next_version: (base_name)
|
||||||
- function save_metadata: (weights_file, metadata)
|
- function save_metadata: (weights_file, metadata)
|
||||||
- function train_nnue: (data_file, output_file, epochs, batch_size, lr, checkpoint, stockfish_depth, use_versioning, early_stopping_patience, weight_decay, subsample_ratio)
|
- function train_nnue: (data_file, output_file, epochs, batch_size, lr, checkpoint, stockfish_depth, use_versioning, early_stopping_patience, weight_decay, subsample_ratio, hidden_sizes)
|
||||||
- function burst_train: (data_file, output_file, duration_minutes, epochs_per_season, early_stopping_patience, batch_size, lr, initial_checkpoint, stockfish_depth, use_versioning, weight_decay, subsample_ratio)
|
- function burst_train: (data_file, output_file, duration_minutes, epochs_per_season, early_stopping_patience, batch_size, lr, initial_checkpoint, stockfish_depth, use_versioning, weight_decay, subsample_ratio, hidden_sizes)
|
||||||
- class NNUEDataset
|
- class NNUEDataset
|
||||||
- _...1 more_
|
- _...1 more_
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`
|
|
||||||
- class Bot
|
|
||||||
- function name
|
|
||||||
- function nextMove
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
||||||
- class BotController
|
- class BotController
|
||||||
- function getBot
|
- function getBot
|
||||||
@@ -148,7 +152,6 @@
|
|||||||
- function bestMoveWithTime
|
- function bestMoveWithTime
|
||||||
- function loop
|
- function loop
|
||||||
- function loop
|
- function loop
|
||||||
- _...2 more_
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
||||||
- class MoveOrdering
|
- class MoveOrdering
|
||||||
- class OrderingContext
|
- class OrderingContext
|
||||||
@@ -158,6 +161,7 @@
|
|||||||
- function getHistory
|
- function getHistory
|
||||||
- _...3 more_
|
- _...3 more_
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
||||||
|
- function advance
|
||||||
- function probe
|
- function probe
|
||||||
- function store
|
- function store
|
||||||
- function clear
|
- function clear
|
||||||
@@ -181,14 +185,15 @@
|
|||||||
- function history
|
- function history
|
||||||
- function getCurrentIndex
|
- function getCurrentIndex
|
||||||
- _...3 more_
|
- _...3 more_
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala` — class JacksonConfig, function customize
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — class Parser, function parseMove
|
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — class Parser, function parseMove
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`
|
||||||
- class GameEngine
|
- class GameEngine
|
||||||
- function isPendingPromotion
|
|
||||||
- function board
|
- function board
|
||||||
- function turn
|
- function turn
|
||||||
- function context
|
- function context
|
||||||
- function canUndo
|
- function canUndo
|
||||||
|
- function canRedo
|
||||||
- _...11 more_
|
- _...11 more_
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`
|
||||||
- function context
|
- function context
|
||||||
@@ -198,6 +203,26 @@
|
|||||||
- function subscribe
|
- function subscribe
|
||||||
- function unsubscribe
|
- function unsubscribe
|
||||||
- _...1 more_
|
- _...1 more_
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/registry/GameRegistry.scala`
|
||||||
|
- class GameRegistry
|
||||||
|
- function store
|
||||||
|
- function get
|
||||||
|
- function update
|
||||||
|
- function generateId
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala`
|
||||||
|
- class GameRegistryImpl
|
||||||
|
- function store
|
||||||
|
- function get
|
||||||
|
- function update
|
||||||
|
- function generateId
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala`
|
||||||
|
- function onGameEvent
|
||||||
|
- function createGame
|
||||||
|
- function getGame
|
||||||
|
- function streamGame
|
||||||
|
- function onGameEvent
|
||||||
|
- function resignGame
|
||||||
|
- _...9 more_
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — class GameContextExport, function exportGameContext
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — class GameContextExport, function exportGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — class GameContextImport, function importGameContext
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — class GameContextImport, function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameFileService.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/GameFileService.scala`
|
||||||
@@ -247,32 +272,13 @@
|
|||||||
- function allLegalMoves
|
- function allLegalMoves
|
||||||
- function isCheck
|
- function isCheck
|
||||||
- function isCheckmate
|
- function isCheckmate
|
||||||
- _...4 more_
|
- _...5 more_
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala`
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala`
|
||||||
- class DefaultRules
|
- class DefaultRules
|
||||||
|
- function positionOf
|
||||||
- function loop
|
- function loop
|
||||||
- function toMoves
|
- function toMoves
|
||||||
- function loop
|
- function loop
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/Main.scala` — class Main, function main
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala`
|
|
||||||
- class ChessBoardView
|
|
||||||
- function updateBoard
|
|
||||||
- function updateUndoRedoButtons
|
|
||||||
- function showMessage
|
|
||||||
- function showPromotionDialog
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/ChessGUI.scala`
|
|
||||||
- class ChessGUIApp
|
|
||||||
- class ChessGUILauncher
|
|
||||||
- function getEngine
|
|
||||||
- function launch
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/GUIObserver.scala` — class GUIObserver
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/PieceSprites.scala`
|
|
||||||
- class PieceSprites
|
|
||||||
- function loadPieceImage
|
|
||||||
- class SquareColors
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala` — class TerminalUI, function start
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/utils/PieceUnicode.scala` — function unicode
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/utils/Renderer.scala` — class Renderer, function render
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -295,39 +301,39 @@
|
|||||||
|
|
||||||
## Most Imported Files (change these carefully)
|
## Most Imported Files (change these carefully)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **60** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **64** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **40** files
|
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **44** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **39** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **40** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` — imported by **36** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` — imported by **35** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` — imported by **22** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` — imported by **19** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **21** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` — imported by **18** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` — imported by **21** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **17** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` — imported by **17** files
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` — imported by **17** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **10** files
|
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **11** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **10** files
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **10** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **8** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **9** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **8** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/DrawReason.scala` — imported by **7** files
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **7** files
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala` — imported by **6** files
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala` — imported by **6** files
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — imported by **5** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/BotDifficulty.scala` — imported by **5** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — imported by **5** files
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala` — imported by **4** files
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala` — imported by **4** files
|
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala` — imported by **4** files
|
|
||||||
|
|
||||||
## Import Map (who imports what)
|
## Import Map (who imports what)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`, `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala` +55 more
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`, `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala` +59 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/board/BoardTest.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala` +35 more
|
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` ← `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`, `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/board/BoardTest.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala` +39 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/move/Move.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/api/src/test/scala/de/nowchess/api/move/MoveTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` +34 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/move/Move.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/api/src/test/scala/de/nowchess/api/move/MoveTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` +35 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala` +31 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala` +30 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +17 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +14 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala` +16 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +13 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +16 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala` +12 more
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +12 more
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +12 more
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +5 more
|
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +6 more
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` ← `modules/bot/src/test/scala/de/nowchess/bot/PolyglotHashTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineNotationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala` +5 more
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` ← `modules/bot/src/test/scala/de/nowchess/bot/PolyglotHashTest.scala`, `modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineNotationTest.scala` +5 more
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,36 +2,36 @@
|
|||||||
|
|
||||||
## Most Imported Files (change these carefully)
|
## Most Imported Files (change these carefully)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **60** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **64** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **40** files
|
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **44** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **39** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **40** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` — imported by **36** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` — imported by **35** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` — imported by **22** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` — imported by **19** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **21** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` — imported by **18** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` — imported by **21** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **17** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` — imported by **17** files
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` — imported by **17** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **10** files
|
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **11** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **10** files
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **10** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **8** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **9** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **8** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/DrawReason.scala` — imported by **7** files
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **7** files
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala` — imported by **6** files
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala` — imported by **6** files
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — imported by **5** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/BotDifficulty.scala` — imported by **5** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — imported by **5** files
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala` — imported by **4** files
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala` — imported by **4** files
|
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala` — imported by **4** files
|
|
||||||
|
|
||||||
## Import Map (who imports what)
|
## Import Map (who imports what)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`, `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala` +55 more
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`, `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala` +59 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/board/BoardTest.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala` +35 more
|
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` ← `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`, `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/board/BoardTest.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala` +39 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/move/Move.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/api/src/test/scala/de/nowchess/api/move/MoveTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` +34 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/move/Move.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/api/src/test/scala/de/nowchess/api/move/MoveTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` +35 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala` +31 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Color.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala` +30 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +17 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Board.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/api/src/test/scala/de/nowchess/api/game/GameContextTest.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +14 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala` +16 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +13 more
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Piece.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala`, `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +16 more
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` ← `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala` +12 more
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +12 more
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +12 more
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +5 more
|
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` ← `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/HybridBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/bots/NNUEBot.scala`, `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`, `modules/bot/src/test/scala/de/nowchess/bot/AlphaBetaSearchTest.scala` +6 more
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` ← `modules/bot/src/test/scala/de/nowchess/bot/PolyglotHashTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineNotationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala` +5 more
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` ← `modules/bot/src/test/scala/de/nowchess/bot/PolyglotHashTest.scala`, `modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineLoadGameTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineNotationTest.scala` +5 more
|
||||||
|
|||||||
@@ -38,14 +38,21 @@
|
|||||||
- class Square
|
- class Square
|
||||||
- function fromAlgebraic
|
- function fromAlgebraic
|
||||||
- function offset
|
- function offset
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala`
|
||||||
|
- class Bot
|
||||||
|
- function name
|
||||||
|
- function nextMove
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/ErrorEventDto.scala` — class ErrorEventDto, function apply
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/GameFullEventDto.scala` — class GameFullEventDto, function apply
|
||||||
|
- `modules/api/src/main/scala/de/nowchess/api/dto/GameStateEventDto.scala` — class GameStateEventDto, function apply
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala`
|
||||||
|
- function kingSquare
|
||||||
- function withBoard
|
- function withBoard
|
||||||
- function withTurn
|
- function withTurn
|
||||||
- function withCastlingRights
|
- function withCastlingRights
|
||||||
- function withEnPassantSquare
|
- function withEnPassantSquare
|
||||||
- function withHalfMoveClock
|
- function withHalfMoveClock
|
||||||
- function withMove
|
- _...4 more_
|
||||||
- _...2 more_
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — class PlayerId, function apply
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — class PlayerId, function apply
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/response/ApiResponse.scala`
|
- `modules/api/src/main/scala/de/nowchess/api/response/ApiResponse.scala`
|
||||||
- class ApiResponse
|
- class ApiResponse
|
||||||
@@ -70,6 +77,7 @@
|
|||||||
- `modules/bot/python/src/export.py` — function export_to_nbai: (weights_file, output_file, trained_by, train_loss)
|
- `modules/bot/python/src/export.py` — function export_to_nbai: (weights_file, output_file, trained_by, train_loss)
|
||||||
- `modules/bot/python/src/generate.py` — function play_random_game_and_collect_positions: (output_file, total_positions, samples_per_game, min_move, max_move, num_workers)
|
- `modules/bot/python/src/generate.py` — function play_random_game_and_collect_positions: (output_file, total_positions, samples_per_game, min_move, max_move, num_workers)
|
||||||
- `modules/bot/python/src/label.py` — function normalize_evaluation: (cp_value, method, scale), function label_positions_with_stockfish: (positions_file, output_file, stockfish_path, batch_size, depth, verbose, normalize, num_workers)
|
- `modules/bot/python/src/label.py` — function normalize_evaluation: (cp_value, method, scale), function label_positions_with_stockfish: (positions_file, output_file, stockfish_path, batch_size, depth, verbose, normalize, num_workers)
|
||||||
|
- `modules/bot/python/src/lichess_importer.py` — function import_lichess_evals: (input_path, output_file, max_positions, min_depth, verbose) -> int
|
||||||
- `modules/bot/python/src/tactical_positions_extractor.py`
|
- `modules/bot/python/src/tactical_positions_extractor.py`
|
||||||
- function download_and_extract_puzzle_db: (url, output_dir)
|
- function download_and_extract_puzzle_db: (url, output_dir)
|
||||||
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
||||||
@@ -81,14 +89,10 @@
|
|||||||
- function fen_to_features: (fen)
|
- function fen_to_features: (fen)
|
||||||
- function find_next_version: (base_name)
|
- function find_next_version: (base_name)
|
||||||
- function save_metadata: (weights_file, metadata)
|
- function save_metadata: (weights_file, metadata)
|
||||||
- function train_nnue: (data_file, output_file, epochs, batch_size, lr, checkpoint, stockfish_depth, use_versioning, early_stopping_patience, weight_decay, subsample_ratio)
|
- function train_nnue: (data_file, output_file, epochs, batch_size, lr, checkpoint, stockfish_depth, use_versioning, early_stopping_patience, weight_decay, subsample_ratio, hidden_sizes)
|
||||||
- function burst_train: (data_file, output_file, duration_minutes, epochs_per_season, early_stopping_patience, batch_size, lr, initial_checkpoint, stockfish_depth, use_versioning, weight_decay, subsample_ratio)
|
- function burst_train: (data_file, output_file, duration_minutes, epochs_per_season, early_stopping_patience, batch_size, lr, initial_checkpoint, stockfish_depth, use_versioning, weight_decay, subsample_ratio, hidden_sizes)
|
||||||
- class NNUEDataset
|
- class NNUEDataset
|
||||||
- _...1 more_
|
- _...1 more_
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/Bot.scala`
|
|
||||||
- class Bot
|
|
||||||
- function name
|
|
||||||
- function nextMove
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
||||||
- class BotController
|
- class BotController
|
||||||
- function getBot
|
- function getBot
|
||||||
@@ -139,7 +143,6 @@
|
|||||||
- function bestMoveWithTime
|
- function bestMoveWithTime
|
||||||
- function loop
|
- function loop
|
||||||
- function loop
|
- function loop
|
||||||
- _...2 more_
|
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
||||||
- class MoveOrdering
|
- class MoveOrdering
|
||||||
- class OrderingContext
|
- class OrderingContext
|
||||||
@@ -149,6 +152,7 @@
|
|||||||
- function getHistory
|
- function getHistory
|
||||||
- _...3 more_
|
- _...3 more_
|
||||||
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
||||||
|
- function advance
|
||||||
- function probe
|
- function probe
|
||||||
- function store
|
- function store
|
||||||
- function clear
|
- function clear
|
||||||
@@ -172,14 +176,15 @@
|
|||||||
- function history
|
- function history
|
||||||
- function getCurrentIndex
|
- function getCurrentIndex
|
||||||
- _...3 more_
|
- _...3 more_
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/config/JacksonConfig.scala` — class JacksonConfig, function customize
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — class Parser, function parseMove
|
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — class Parser, function parseMove
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`
|
||||||
- class GameEngine
|
- class GameEngine
|
||||||
- function isPendingPromotion
|
|
||||||
- function board
|
- function board
|
||||||
- function turn
|
- function turn
|
||||||
- function context
|
- function context
|
||||||
- function canUndo
|
- function canUndo
|
||||||
|
- function canRedo
|
||||||
- _...11 more_
|
- _...11 more_
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`
|
||||||
- function context
|
- function context
|
||||||
@@ -189,6 +194,26 @@
|
|||||||
- function subscribe
|
- function subscribe
|
||||||
- function unsubscribe
|
- function unsubscribe
|
||||||
- _...1 more_
|
- _...1 more_
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/registry/GameRegistry.scala`
|
||||||
|
- class GameRegistry
|
||||||
|
- function store
|
||||||
|
- function get
|
||||||
|
- function update
|
||||||
|
- function generateId
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/registry/GameRegistryImpl.scala`
|
||||||
|
- class GameRegistryImpl
|
||||||
|
- function store
|
||||||
|
- function get
|
||||||
|
- function update
|
||||||
|
- function generateId
|
||||||
|
- `modules/core/src/main/scala/de/nowchess/chess/resource/GameResource.scala`
|
||||||
|
- function onGameEvent
|
||||||
|
- function createGame
|
||||||
|
- function getGame
|
||||||
|
- function streamGame
|
||||||
|
- function onGameEvent
|
||||||
|
- function resignGame
|
||||||
|
- _...9 more_
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — class GameContextExport, function exportGameContext
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — class GameContextExport, function exportGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — class GameContextImport, function importGameContext
|
- `modules/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — class GameContextImport, function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameFileService.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/GameFileService.scala`
|
||||||
@@ -238,29 +263,10 @@
|
|||||||
- function allLegalMoves
|
- function allLegalMoves
|
||||||
- function isCheck
|
- function isCheck
|
||||||
- function isCheckmate
|
- function isCheckmate
|
||||||
- _...4 more_
|
- _...5 more_
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala`
|
- `modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala`
|
||||||
- class DefaultRules
|
- class DefaultRules
|
||||||
|
- function positionOf
|
||||||
- function loop
|
- function loop
|
||||||
- function toMoves
|
- function toMoves
|
||||||
- function loop
|
- function loop
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/Main.scala` — class Main, function main
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/ChessBoardView.scala`
|
|
||||||
- class ChessBoardView
|
|
||||||
- function updateBoard
|
|
||||||
- function updateUndoRedoButtons
|
|
||||||
- function showMessage
|
|
||||||
- function showPromotionDialog
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/ChessGUI.scala`
|
|
||||||
- class ChessGUIApp
|
|
||||||
- class ChessGUILauncher
|
|
||||||
- function getEngine
|
|
||||||
- function launch
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/GUIObserver.scala` — class GUIObserver
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/PieceSprites.scala`
|
|
||||||
- class PieceSprites
|
|
||||||
- function loadPieceImage
|
|
||||||
- class SquareColors
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/terminal/TerminalUI.scala` — class TerminalUI, function start
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/utils/PieceUnicode.scala` — function unicode
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/utils/Renderer.scala` — class Renderer, function render
|
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>NowChessSystems — codesight report</title>
|
||||||
|
<style>
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
:root{--bg:#0a0a0f;--card:#12121a;--border:#1e1e2e;--text:#e0e0e8;--muted:#6b6b80;--accent:#6366f1;--accent2:#22d3ee;--green:#22c55e;--orange:#f59e0b;--red:#ef4444;--pink:#ec4899}
|
||||||
|
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:2rem;max-width:1400px;margin:0 auto;line-height:1.6}
|
||||||
|
h1{font-size:2.5rem;font-weight:800;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent;margin-bottom:.25rem}
|
||||||
|
.subtitle{color:var(--muted);font-size:1rem;margin-bottom:2rem}
|
||||||
|
.stack-badge{display:inline-block;background:var(--card);border:1px solid var(--border);border-radius:6px;padding:2px 10px;font-size:.85rem;color:var(--accent2);margin:0 4px 4px 0}
|
||||||
|
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1rem;margin:2rem 0}
|
||||||
|
.stat{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:1.25rem;text-align:center}
|
||||||
|
.stat-value{font-size:2rem;font-weight:800;background:linear-gradient(135deg,var(--accent),var(--accent2));-webkit-background-clip:text;-webkit-text-fill-color:transparent}
|
||||||
|
.stat-label{color:var(--muted);font-size:.85rem;margin-top:.25rem}
|
||||||
|
.token-hero{background:linear-gradient(135deg,#1a1a2e,#16213e);border:1px solid var(--accent);border-radius:16px;padding:2rem;margin:2rem 0;text-align:center}
|
||||||
|
.token-saved{font-size:3rem;font-weight:900;color:var(--green)}
|
||||||
|
.token-detail{color:var(--muted);font-size:.9rem;margin-top:.5rem}
|
||||||
|
.section{margin:2.5rem 0}
|
||||||
|
.section h2{font-size:1.4rem;font-weight:700;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:1px solid var(--border)}
|
||||||
|
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:1rem}
|
||||||
|
.card{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:1rem;transition:border-color .2s}
|
||||||
|
.card:hover{border-color:var(--accent)}
|
||||||
|
.card-title{font-weight:700;font-size:1rem;margin-bottom:.5rem}
|
||||||
|
.card-meta{color:var(--muted);font-size:.8rem}
|
||||||
|
.tag{display:inline-block;background:rgba(99,102,241,.15);color:var(--accent);border-radius:4px;padding:1px 6px;font-size:.75rem;margin:1px}
|
||||||
|
.tag-auth{background:rgba(239,68,68,.15);color:var(--red)}
|
||||||
|
.tag-db{background:rgba(34,211,238,.15);color:var(--accent2)}
|
||||||
|
.tag-ai{background:rgba(236,72,153,.15);color:var(--pink)}
|
||||||
|
.tag-payment{background:rgba(245,158,11,.15);color:var(--orange)}
|
||||||
|
.tag-email{background:rgba(34,197,94,.15);color:var(--green)}
|
||||||
|
.tag-queue{background:rgba(168,85,247,.15);color:#a855f7}
|
||||||
|
.tag-cache{background:rgba(245,158,11,.15);color:var(--orange)}
|
||||||
|
.method{font-weight:700;font-size:.8rem;padding:2px 6px;border-radius:4px;margin-right:6px}
|
||||||
|
.method-GET{background:rgba(34,197,94,.2);color:var(--green)}
|
||||||
|
.method-POST{background:rgba(99,102,241,.2);color:var(--accent)}
|
||||||
|
.method-PUT{background:rgba(245,158,11,.2);color:var(--orange)}
|
||||||
|
.method-PATCH{background:rgba(245,158,11,.2);color:var(--orange)}
|
||||||
|
.method-DELETE{background:rgba(239,68,68,.2);color:var(--red)}
|
||||||
|
.method-ALL{background:rgba(107,107,128,.2);color:var(--muted)}
|
||||||
|
.route-path{font-family:'Fira Code',monospace;font-size:.9rem}
|
||||||
|
.route-contract{color:var(--muted);font-size:.8rem;font-style:italic;margin-left:.5rem}
|
||||||
|
.field{display:flex;gap:.5rem;padding:3px 0;font-size:.9rem}
|
||||||
|
.field-name{font-family:monospace;color:var(--accent2)}
|
||||||
|
.field-type{color:var(--muted);font-family:monospace}
|
||||||
|
.field-flags{display:flex;gap:3px}
|
||||||
|
.flag{font-size:.7rem;padding:0 4px;border-radius:3px;background:rgba(99,102,241,.1);color:var(--accent)}
|
||||||
|
.flag-pk{background:rgba(245,158,11,.2);color:var(--orange)}
|
||||||
|
.flag-fk{background:rgba(34,211,238,.2);color:var(--accent2)}
|
||||||
|
.flag-unique{background:rgba(236,72,153,.2);color:var(--pink)}
|
||||||
|
.hot-bar{height:8px;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:4px;margin-top:4px}
|
||||||
|
.component-props{color:var(--muted);font-size:.85rem}
|
||||||
|
.badge-client{background:rgba(34,197,94,.15);color:var(--green);font-size:.75rem;padding:1px 6px;border-radius:4px}
|
||||||
|
.badge-server{background:rgba(99,102,241,.15);color:var(--accent);font-size:.75rem;padding:1px 6px;border-radius:4px}
|
||||||
|
.env-required{color:var(--red);font-weight:600;font-size:.8rem}
|
||||||
|
.env-default{color:var(--green);font-size:.8rem}
|
||||||
|
.footer{text-align:center;color:var(--muted);margin-top:4rem;padding-top:2rem;border-top:1px solid var(--border);font-size:.85rem}
|
||||||
|
.footer a{color:var(--accent);text-decoration:none}
|
||||||
|
table{width:100%;border-collapse:collapse}
|
||||||
|
table td,table th{padding:8px 12px;text-align:left;border-bottom:1px solid var(--border);font-size:.9rem}
|
||||||
|
table th{color:var(--muted);font-size:.8rem;font-weight:600;text-transform:uppercase}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>NowChessSystems</h1>
|
||||||
|
<div class="subtitle">AI Context Map — generated by codesight</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span class="stack-badge">raw-http</span>
|
||||||
|
|
||||||
|
<span class="stack-badge">unknown</span>
|
||||||
|
<span class="stack-badge">scala</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="token-hero">
|
||||||
|
<div class="token-saved">~20,573 tokens saved</div>
|
||||||
|
<div class="token-detail">
|
||||||
|
Output: 5,297 tokens — Exploration cost without codesight: ~25,870 tokens — 149 files scanned
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat"><div class="stat-value">0</div><div class="stat-label">Routes</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">0</div><div class="stat-label">Models</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">0</div><div class="stat-label">Components</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">63</div><div class="stat-label">Libraries</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">1</div><div class="stat-label">Env Vars</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">1</div><div class="stat-label">Middleware</div></div>
|
||||||
|
<div class="stat"><div class="stat-value">383</div><div class="stat-label">Import Links</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Dependency Hot Files</h2>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala</div>
|
||||||
|
<div class="card-meta">imported by 64 files</div>
|
||||||
|
<div class="hot-bar" style="width:100%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/move/Move.scala</div>
|
||||||
|
<div class="card-meta">imported by 44 files</div>
|
||||||
|
<div class="hot-bar" style="width:69%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/Square.scala</div>
|
||||||
|
<div class="card-meta">imported by 40 files</div>
|
||||||
|
<div class="hot-bar" style="width:63%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/Color.scala</div>
|
||||||
|
<div class="card-meta">imported by 35 files</div>
|
||||||
|
<div class="hot-bar" style="width:55%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/Board.scala</div>
|
||||||
|
<div class="card-meta">imported by 19 files</div>
|
||||||
|
<div class="hot-bar" style="width:30%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/Piece.scala</div>
|
||||||
|
<div class="card-meta">imported by 18 files</div>
|
||||||
|
<div class="hot-bar" style="width:28%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala</div>
|
||||||
|
<div class="card-meta">imported by 17 files</div>
|
||||||
|
<div class="hot-bar" style="width:27%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/rule/src/main/scala/de/nowchess/rules/sets/DefaultRules.scala</div>
|
||||||
|
<div class="card-meta">imported by 17 files</div>
|
||||||
|
<div class="hot-bar" style="width:27%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala</div>
|
||||||
|
<div class="card-meta">imported by 11 files</div>
|
||||||
|
<div class="hot-bar" style="width:17%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala</div>
|
||||||
|
<div class="card-meta">imported by 10 files</div>
|
||||||
|
<div class="hot-bar" style="width:16%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala</div>
|
||||||
|
<div class="card-meta">imported by 9 files</div>
|
||||||
|
<div class="hot-bar" style="width:14%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title" style="font-size:.9rem">modules/api/src/main/scala/de/nowchess/api/game/DrawReason.scala</div>
|
||||||
|
<div class="card-meta">imported by 7 files</div>
|
||||||
|
<div class="hot-bar" style="width:11%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Environment Variables</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Variable</th><th>Status</th><th>Source</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>STOCKFISH_PATH</code></td>
|
||||||
|
<td><span class="env-required">required</span></td>
|
||||||
|
<td class="card-meta">modules/bot/python/nnue.py</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Middleware</h2>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">generate <span class="tag tag-custom">custom</span></div>
|
||||||
|
<div class="card-meta">modules/bot/python/src/generate.py</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="footer">
|
||||||
|
Generated by <a href="https://github.com/Houseofmvps/codesight">codesight</a> — see your codebase clearly
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
<option value="$PROJECT_DIR$/modules/core" />
|
<option value="$PROJECT_DIR$/modules/core" />
|
||||||
<option value="$PROJECT_DIR$/modules/io" />
|
<option value="$PROJECT_DIR$/modules/io" />
|
||||||
<option value="$PROJECT_DIR$/modules/rule" />
|
<option value="$PROJECT_DIR$/modules/rule" />
|
||||||
<option value="$PROJECT_DIR$/modules/ui" />
|
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="NowChessSystems.modules.core.main" type="QuarkusRunConfigurationType" factoryName="Quarkus" nameIsGenerated="true">
|
||||||
|
<module name="NowChessSystems.modules.core.main" />
|
||||||
|
<QsGradleRunConfiguration>
|
||||||
|
<ExternalSystemSettings>
|
||||||
|
<option name="executionName" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$/modules/core" />
|
||||||
|
<option name="externalSystemIdString" value="GRADLE" />
|
||||||
|
<option name="scriptParameters" />
|
||||||
|
<option name="taskDescriptions">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
<option name="taskNames">
|
||||||
|
<list>
|
||||||
|
<option value="quarkusDev" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
<option name="vmOptions" />
|
||||||
|
</ExternalSystemSettings>
|
||||||
|
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||||
|
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||||
|
<ExternalSystemDebugDisabled>false</ExternalSystemDebugDisabled>
|
||||||
|
<DebugAllEnabled>false</DebugAllEnabled>
|
||||||
|
<RunAsTest>false</RunAsTest>
|
||||||
|
<GradleProfilingDisabled>false</GradleProfilingDisabled>
|
||||||
|
<GradleCoverageDisabled>false</GradleCoverageDisabled>
|
||||||
|
<profile>dev</profile>
|
||||||
|
</QsGradleRunConfiguration>
|
||||||
|
<method v="2">
|
||||||
|
<option name="Make" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<option name="deprecationWarnings" value="true" />
|
<option name="deprecationWarnings" value="true" />
|
||||||
<option name="uncheckedWarnings" value="true" />
|
<option name="uncheckedWarnings" value="true" />
|
||||||
</profile>
|
</profile>
|
||||||
<profile name="Gradle 2" modules="NowChessSystems.modules.bot.main,NowChessSystems.modules.bot.scoverage,NowChessSystems.modules.bot.test,NowChessSystems.modules.core.main,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.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">
|
||||||
<option name="deprecationWarnings" value="true" />
|
<option name="deprecationWarnings" value="true" />
|
||||||
<option name="uncheckedWarnings" value="true" />
|
<option name="uncheckedWarnings" value="true" />
|
||||||
<parameters>
|
<parameters>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Scala 3.5.1 · Gradle 9
|
|||||||
./test # Run all tests
|
./test # Run all tests
|
||||||
./coverage # Check coverage
|
./coverage # Check coverage
|
||||||
```
|
```
|
||||||
Try to stick to these commands for consistency.
|
Use consistently.
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
@@ -25,14 +25,14 @@ Try to stick to these commands for consistency.
|
|||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
||||||
- Use immutable data and pure functions.
|
- Immutable data, pure functions.
|
||||||
- Keep functions under 30 lines. If you need "and" to describe it, split it.
|
- Functions under 30 lines. Need "and"? Split it.
|
||||||
- Keep cyclomatic complexity under 15.
|
- Cyclomatic complexity under 15.
|
||||||
- Avoid comments. Let names carry intent; comment only non-obvious algorithms.
|
- No comments. Names carry intent. Comment non-obvious algorithms only.
|
||||||
- Scan for duplicated logic before finishing. Extract it.
|
- Scan duplicated logic. Extract.
|
||||||
- Follow default Sonar style for Scala.
|
- Follow default Sonar style for Scala.
|
||||||
- Use `Option` or `Either` for fallible operations; avoid exceptions for control flow.
|
- `Option`/`Either` for fallible ops. Skip exceptions for control flow.
|
||||||
- Naming: types are PascalCase, functions/values are camelCase.
|
- Naming: types PascalCase, functions/values camelCase.
|
||||||
|
|
||||||
## Code Quality
|
## Code Quality
|
||||||
|
|
||||||
@@ -40,23 +40,23 @@ Try to stick to these commands for consistency.
|
|||||||
|
|
||||||
### Linters
|
### Linters
|
||||||
|
|
||||||
- **scalafmt** — enforces formatting; run `./gradlew spotlessScalaCheck` to check and `./gradlew spotlessScalaApply` to refactor.
|
- **scalafmt** — Enforces formatting. Check: `./gradlew spotlessScalaCheck`. Refactor: `./gradlew spotlessScalaApply`.
|
||||||
- **scalafix** — enforces style and detects unused imports/code; run `./gradlew scalafix` to apply rules.
|
- **scalafix** — Enforces style, detects unused imports/code. Run: `./gradlew scalafix`.
|
||||||
|
|
||||||
## Architecture Decisions
|
## Architecture Decisions
|
||||||
|
|
||||||
- **Immutable state as primary model:** GameContext (api) holds board, history, player state — immutable, passed through the system. Each move creates a new GameContext, enabling undo/redo without side effects.
|
- **Immutable state as primary model:** GameContext (api) holds board, history, player state—immutable throughout. Each move → new GameContext. Enables undo/redo without side effects.
|
||||||
- **Observer pattern for UI decoupling:** GameEngine publishes move/state events; CommandInvoker queues moves; UI listens to events, not polling. GameEngine never imports UI code.
|
- **Observer pattern for UI decoupling:** GameEngine publishes move/state events; CommandInvoker queues moves; UI listens (no polling). GameEngine never imports UI.
|
||||||
- **RuleSet trait encapsulates rules:** Move generation, check, castling, en passant all in RuleSet impl. GameEngine calls rules as a black box; rules don't know about the rest of core.
|
- **RuleSet trait encapsulates rules:** Move generation, check, castling, en passant all in RuleSet impl. GameEngine calls rules as black box; rules don't know rest of core.
|
||||||
- **Polyglot hash must follow spec index layout:** piece keys use interleaved mapping `(pieceType * 2 + colorBit)` with black=0/white=1, castling keys are `768..771`, en-passant file keys are `772..779` and are XORed only if side-to-move has a pawn that can capture en passant, side-to-move key is `780` for white.
|
- **Polyglot hash must follow spec index layout:** Piece keys use interleaved mapping `(pieceType * 2 + colorBit)` (black=0, white=1). Castling keys: `768..771`. En-passant file keys: `772..779`, XORed only if side-to-move has capturable en passant. Side-to-move key: `780` (white).
|
||||||
- **Alpha-beta uses sequential PV search by default:** parallel split was disabled because fixed-window futures removed pruning effectiveness; correctness and pruning quality take priority over speculative parallelism.
|
- **Alpha-beta uses sequential PV search by default:** Parallel split disabled (fixed-window futures removed pruning effectiveness). Sequential PV default. Correctness + pruning quality > speculative parallelism.
|
||||||
- **Search hash is updated incrementally per move:** bot search now updates Zobrist keys from parent hash with move deltas instead of recomputing piece scans at every node.
|
- **Search hash is updated incrementally per move:** Bot search updates Zobrist keys from parent hash with move deltas, not recomputing piece scans per node.
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- **Tests are the spec.** Never modify tests to pass; modify requirements or code. Update tests only if requirements change.
|
- **Tests are the spec.** Don't modify to pass. Fix requirements/code. Update only if requirements change.
|
||||||
- Never read build folders. Ask permission if needed.
|
- Never read build folders. Ask permission if needed.
|
||||||
- Keep this file up to date with any important decisions or conventions.
|
- Keep file current with decisions + conventions.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -64,11 +64,9 @@ Try to stick to these commands for consistency.
|
|||||||
|
|
||||||
### Two-Step Rule (mandatory)
|
### Two-Step Rule (mandatory)
|
||||||
**Step 1 — Orient:** Use wiki articles to find WHERE things live.
|
**Step 1 — Orient:** Use wiki articles to find WHERE things live.
|
||||||
**Step 2 — Verify:** Read the actual source files listed in the wiki article BEFORE writing any code.
|
**Step 2 — Verify:** Read source files from wiki BEFORE coding.
|
||||||
|
|
||||||
Wiki articles are structural summaries extracted by AST. They show routes, models, and file locations.
|
Wiki = structural summaries (routes, models, file locations). No function logic, middleware internals, runtime behavior. Don't code from wiki alone—read sources.
|
||||||
They do NOT show full function logic, middleware internals, or dynamic runtime behavior.
|
|
||||||
**Never write or modify code based solely on wiki content — always read source files first.**
|
|
||||||
|
|
||||||
Read in order at session start:
|
Read in order at session start:
|
||||||
1. `.codesight/wiki/index.md` — orientation map (~200 tokens)
|
1. `.codesight/wiki/index.md` — orientation map (~200 tokens)
|
||||||
@@ -76,8 +74,7 @@ Read in order at session start:
|
|||||||
3. Domain article (e.g. `.codesight/wiki/auth.md`) → check "Source Files" section → read those files
|
3. Domain article (e.g. `.codesight/wiki/auth.md`) → check "Source Files" section → read those files
|
||||||
4. `.codesight/CODESIGHT.md` — full context map for deep exploration
|
4. `.codesight/CODESIGHT.md` — full context map for deep exploration
|
||||||
|
|
||||||
Routes marked `[inferred]` in wiki articles were detected via regex — verify against source before trusting.
|
`[inferred]` routes = regex-detected. Verify sources. ⚠ in wiki? Re-run `codesight --wiki`.
|
||||||
If any source file shows ⚠ in the wiki, re-run `codesight --wiki` before proceeding.
|
|
||||||
|
|
||||||
Or use the codesight MCP server for on-demand queries:
|
Or use the codesight MCP server for on-demand queries:
|
||||||
- `codesight_get_wiki_article` — read a specific wiki article by name
|
- `codesight_get_wiki_article` — read a specific wiki article by name
|
||||||
@@ -87,13 +84,13 @@ Or use the codesight MCP server for on-demand queries:
|
|||||||
- `codesight_get_blast_radius --file src/lib/db.ts` — impact analysis before changes
|
- `codesight_get_blast_radius --file src/lib/db.ts` — impact analysis before changes
|
||||||
- `codesight_get_schema --model users` — specific model details
|
- `codesight_get_schema --model users` — specific model details
|
||||||
|
|
||||||
Only open specific files after consulting codesight context. This saves ~16.893 tokens per conversation.
|
Consult codesight context first. Saves ~16.893 tokens/conversation.
|
||||||
|
|
||||||
## graphify
|
## graphify
|
||||||
|
|
||||||
This project has a graphify knowledge graph at graphify-out/.
|
graphify knowledge graph at graphify-out/.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
|
- Architecture/codebase questions? Read graphify-out/GRAPH_REPORT.md (god nodes, communities).
|
||||||
- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
|
- graphify-out/wiki/index.md exists? Use it (not raw files).
|
||||||
- After modifying code files in this session, run `python3 -c "from graphify.watch import _rebuild_code; from pathlib import Path; _rebuild_code(Path('.'))"` to keep the graph current
|
- Code modified? Run `python3 -c "from graphify.watch import _rebuild_code; from pathlib import Path; _rebuild_code(Path('.'))"` to sync graph.
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# Now-Chess
|
||||||
|
|
||||||
|
Scala 3.5.1 · Gradle 9
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```
|
||||||
|
./clean # Clear build dirs — only when necessary
|
||||||
|
./compile # Compile all modules — always run
|
||||||
|
./test # Run all tests
|
||||||
|
./coverage # Check coverage
|
||||||
|
```
|
||||||
|
Try to stick to these commands for consistency.
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
| Module | Role | Depends on |
|
||||||
|
|--------|------|-----------|
|
||||||
|
| `api` | Model / shared types | (none) |
|
||||||
|
| `core` | Primary business logic | api, rule |
|
||||||
|
| `rule` | Game rules | api |
|
||||||
|
| `bot` | Bots and AI | api,rule,io |
|
||||||
|
| `io` | Export formats | api, core |
|
||||||
|
| `ui` | Entrypoint & UI | core, io |
|
||||||
|
|
||||||
|
## Style
|
||||||
|
|
||||||
|
- Use immutable data and pure functions.
|
||||||
|
- Keep functions under 30 lines. If you need "and" to describe it, split it.
|
||||||
|
- Keep cyclomatic complexity under 15.
|
||||||
|
- Avoid comments. Let names carry intent; comment only non-obvious algorithms.
|
||||||
|
- Scan for duplicated logic before finishing. Extract it.
|
||||||
|
- Follow default Sonar style for Scala.
|
||||||
|
- Use `Option` or `Either` for fallible operations; avoid exceptions for control flow.
|
||||||
|
- Naming: types are PascalCase, functions/values are camelCase.
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
- **Coverage:** 100% condition coverage required in `api`, `core`, `rule`, `io` (mandatory); `ui` exempt.
|
||||||
|
|
||||||
|
### Linters
|
||||||
|
|
||||||
|
- **scalafmt** — enforces formatting; run `./gradlew spotlessScalaCheck` to check and `./gradlew spotlessScalaApply` to refactor.
|
||||||
|
- **scalafix** — enforces style and detects unused imports/code; run `./gradlew scalafix` to apply rules.
|
||||||
|
|
||||||
|
## Architecture Decisions
|
||||||
|
|
||||||
|
- **Immutable state as primary model:** GameContext (api) holds board, history, player state — immutable, passed through the system. Each move creates a new GameContext, enabling undo/redo without side effects.
|
||||||
|
- **Observer pattern for UI decoupling:** GameEngine publishes move/state events; CommandInvoker queues moves; UI listens to events, not polling. GameEngine never imports UI code.
|
||||||
|
- **RuleSet trait encapsulates rules:** Move generation, check, castling, en passant all in RuleSet impl. GameEngine calls rules as a black box; rules don't know about the rest of core.
|
||||||
|
- **Polyglot hash must follow spec index layout:** piece keys use interleaved mapping `(pieceType * 2 + colorBit)` with black=0/white=1, castling keys are `768..771`, en-passant file keys are `772..779` and are XORed only if side-to-move has a pawn that can capture en passant, side-to-move key is `780` for white.
|
||||||
|
- **Alpha-beta uses sequential PV search by default:** parallel split was disabled because fixed-window futures removed pruning effectiveness; correctness and pruning quality take priority over speculative parallelism.
|
||||||
|
- **Search hash is updated incrementally per move:** bot search now updates Zobrist keys from parent hash with move deltas instead of recomputing piece scans at every node.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- **Tests are the spec.** Never modify tests to pass; modify requirements or code. Update tests only if requirements change.
|
||||||
|
- Never read build folders. Ask permission if needed.
|
||||||
|
- Keep this file up to date with any important decisions or conventions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instructions for Claude Code
|
||||||
|
|
||||||
|
### Two-Step Rule (mandatory)
|
||||||
|
**Step 1 — Orient:** Use wiki articles to find WHERE things live.
|
||||||
|
**Step 2 — Verify:** Read the actual source files listed in the wiki article BEFORE writing any code.
|
||||||
|
|
||||||
|
Wiki articles are structural summaries extracted by AST. They show routes, models, and file locations.
|
||||||
|
They do NOT show full function logic, middleware internals, or dynamic runtime behavior.
|
||||||
|
**Never write or modify code based solely on wiki content — always read source files first.**
|
||||||
|
|
||||||
|
Read in order at session start:
|
||||||
|
1. `.codesight/wiki/index.md` — orientation map (~200 tokens)
|
||||||
|
2. `.codesight/wiki/overview.md` — architecture overview (~500 tokens)
|
||||||
|
3. Domain article (e.g. `.codesight/wiki/auth.md`) → check "Source Files" section → read those files
|
||||||
|
4. `.codesight/CODESIGHT.md` — full context map for deep exploration
|
||||||
|
|
||||||
|
Routes marked `[inferred]` in wiki articles were detected via regex — verify against source before trusting.
|
||||||
|
If any source file shows ⚠ in the wiki, re-run `codesight --wiki` before proceeding.
|
||||||
|
|
||||||
|
Or use the codesight MCP server for on-demand queries:
|
||||||
|
- `codesight_get_wiki_article` — read a specific wiki article by name
|
||||||
|
- `codesight_get_wiki_index` — get the wiki index
|
||||||
|
- `codesight_get_summary` — quick project overview
|
||||||
|
- `codesight_get_routes --prefix /api/users` — filtered routes
|
||||||
|
- `codesight_get_blast_radius --file src/lib/db.ts` — impact analysis before changes
|
||||||
|
- `codesight_get_schema --model users` — specific model details
|
||||||
|
|
||||||
|
Only open specific files after consulting codesight context. This saves ~16.893 tokens per conversation.
|
||||||
|
|
||||||
|
## graphify
|
||||||
|
|
||||||
|
This project has a graphify knowledge graph at graphify-out/.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
|
||||||
|
- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
|
||||||
|
- After modifying code files in this session, run `python3 -c "from graphify.watch import _rebuild_code; from pathlib import Path; _rebuild_code(Path('.'))"` to keep the graph current
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "NowChess API",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Offer Draw
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/draw/offer
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Accept Draw
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/draw/accept
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Decline Draw
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/draw/decline
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Claim Draw
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/draw/claim
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: draw
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
vars {
|
||||||
|
baseUrl: http://localhost:8080
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Export FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: GET
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/export/fen
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Export PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: GET
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/export/pgn
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: export
|
||||||
|
seq: 6
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
meta {
|
||||||
|
name: Create Game
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"white": {"id": "p1", "displayName": "Alice"},
|
||||||
|
"black": {"id": "p2", "displayName": "Bob"}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Game
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: GET
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
meta {
|
||||||
|
name: Stream Game
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/stream
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Accept: application/x-ndjson
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:pre-request {
|
||||||
|
gameId: tjOgyEcS
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Resign
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/resign
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: game
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
meta {
|
||||||
|
name: Import FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/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",
|
||||||
|
"white": {"id": "p1", "displayName": "Alice"},
|
||||||
|
"black": {"id": "p2", "displayName": "Bob"}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Import PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/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: 5
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
meta {
|
||||||
|
name: Make Move
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/move/b1c3
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:pre-request {
|
||||||
|
gameId: tjOgyEcS
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Legal Moves
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/moves
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
square: e2
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:pre-request {
|
||||||
|
gameId: tjOgyEcS
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Undo Move
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/undo
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
meta {
|
||||||
|
name: Redo Move
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{baseUrl}}/api/board/game/{{gameId}}/redo
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
meta {
|
||||||
|
name: move
|
||||||
|
}
|
||||||
@@ -8,6 +8,49 @@ plugins {
|
|||||||
group = "de.nowchess"
|
group = "de.nowchess"
|
||||||
version = "1.0-SNAPSHOT"
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
// Canonical coverage exclusions — glob patterns consumed by Sonar directly;
|
||||||
|
// converted to scoverage regexes via globToScoverageRegex for instrumentation-time exclusion.
|
||||||
|
val coverageExclusions = listOf(
|
||||||
|
// UI renders JavaFX components; headless test environments cannot exercise rendering paths
|
||||||
|
"modules/ui/**",
|
||||||
|
// FastParse macro-generated combinators produce synthetic branches that scoverage marks as uncovered
|
||||||
|
"modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse*",
|
||||||
|
// NNUE inference pipeline — coverage requires a trained model file not present in CI
|
||||||
|
"**/bot/**/NNUE.scala",
|
||||||
|
"**/bot/**/NNUEBot.scala",
|
||||||
|
"**/bot/**/EvaluationNNUE.scala",
|
||||||
|
// NBAI binary format loader/writer — error paths require crafted corrupt files; migrator is a one-shot tool
|
||||||
|
"**/bot/**/NbaiLoader.scala",
|
||||||
|
"**/bot/**/NbaiModel.scala",
|
||||||
|
"**/bot/**/NbaiMigrator.scala",
|
||||||
|
"**/bot/**/NbaiWriter.scala",
|
||||||
|
// PolyglotBook — binary I/O and dead-code guards (bit-masked fields can never exceed valid range)
|
||||||
|
"**/bot/**/PolyglotBook.scala",
|
||||||
|
"**/bot/**/MoveOrdering.scala",
|
||||||
|
"**/bot/**/AlphaBetaSearch.scala",
|
||||||
|
// DTO case class synthetic methods (Scala compiler-generated apply/$default params)
|
||||||
|
"**/api/src/main/scala/de/nowchess/api/dto/**Dto.scala",
|
||||||
|
// Core infrastructure: exception classes, config, registry implementation, game entry
|
||||||
|
"**/core/src/main/scala/de/nowchess/chess/exception/**",
|
||||||
|
"**/core/src/main/scala/de/nowchess/chess/config/**",
|
||||||
|
"**/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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Converts a Sonar-style glob to a scoverage regex (matched against full source path).
|
||||||
|
// Order matters: protect ** before converting lone *, escape dots last.
|
||||||
|
fun globToScoverageRegex(glob: String): String =
|
||||||
|
glob
|
||||||
|
.replace("**", "^@")
|
||||||
|
.replace("*", "[^/]*")
|
||||||
|
.replace(".", "\\.")
|
||||||
|
.replace("^@", ".*")
|
||||||
|
.let { ".*$it" }
|
||||||
|
|
||||||
|
extra["SCOVERAGE_EXCLUDED"] = coverageExclusions.map(::globToScoverageRegex)
|
||||||
|
|
||||||
sonar {
|
sonar {
|
||||||
properties {
|
properties {
|
||||||
property("sonar.projectKey", "Now-Chess-Systems")
|
property("sonar.projectKey", "Now-Chess-Systems")
|
||||||
@@ -22,26 +65,7 @@ sonar {
|
|||||||
}.joinToString(",")
|
}.joinToString(",")
|
||||||
|
|
||||||
property("sonar.scala.coverage.reportPaths", scoverageReports)
|
property("sonar.scala.coverage.reportPaths", scoverageReports)
|
||||||
property(
|
property("sonar.coverage.exclusions", coverageExclusions.joinToString(","))
|
||||||
"sonar.coverage.exclusions",
|
|
||||||
// UI renders JavaFX components; headless test environments cannot exercise rendering paths
|
|
||||||
"modules/ui/**," +
|
|
||||||
// FastParse macro-generated combinators produce synthetic branches that scoverage marks as uncovered
|
|
||||||
"modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse*," +
|
|
||||||
// NNUE inference pipeline — coverage requires a trained model file not present in CI
|
|
||||||
"**/bot/**/NNUE.scala," +
|
|
||||||
"**/bot/**/NNUEBot.scala," +
|
|
||||||
"**/bot/**/EvaluationNNUE.scala," +
|
|
||||||
// NBAI binary format loader/writer — error paths require crafted corrupt files; migrator is a one-shot tool
|
|
||||||
"**/bot/**/NbaiLoader.scala," +
|
|
||||||
"**/bot/**/NbaiModel.scala," +
|
|
||||||
"**/bot/**/NbaiMigrator.scala," +
|
|
||||||
"**/bot/**/NbaiWriter.scala," +
|
|
||||||
// PolyglotBook — binary I/O and dead-code guards (bit-masked fields can never exceed valid range)
|
|
||||||
"**/bot/**/PolyglotBook.scala," +
|
|
||||||
"**/bot/**/MoveOrdering.scala," +
|
|
||||||
"**/bot/**/AlphaBetaSearch.scala"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: NowChess API
|
title: NowChess Board API
|
||||||
description: |
|
description: |
|
||||||
REST API for the NowChess application. Designed to feel familiar to users
|
REST API for the NowChess application. Designed to feel familiar to users
|
||||||
of the [lichess API](https://lichess.org/api).
|
of the [lichess API](https://lichess.org/api).
|
||||||
@@ -186,11 +186,8 @@ paths:
|
|||||||
currently to move.
|
currently to move.
|
||||||
|
|
||||||
For promotion moves include the target piece as the fifth character:
|
For promotion moves include the target piece as the fifth character:
|
||||||
`e7e8q`, `a2a1r`, etc.
|
`e7e8q`, `a2a1r`, etc. Promotion moves without the fifth character
|
||||||
|
are rejected with `400 INVALID_MOVE`.
|
||||||
If the move results in a pawn reaching the back rank and no promotion
|
|
||||||
character is supplied, the game enters `promotionPending` status and
|
|
||||||
the move is not yet applied — resubmit with the promotion character.
|
|
||||||
security:
|
security:
|
||||||
- bearerAuth: []
|
- bearerAuth: []
|
||||||
parameters:
|
parameters:
|
||||||
@@ -630,7 +627,6 @@ components:
|
|||||||
| `draw` | Draw agreed or claimed — game over |
|
| `draw` | Draw agreed or claimed — game over |
|
||||||
| `drawOffered` | Waiting for the opponent to accept or decline a draw offer |
|
| `drawOffered` | Waiting for the opponent to accept or decline a draw offer |
|
||||||
| `fiftyMoveAvailable` | Fifty-move rule threshold reached; active player may claim draw |
|
| `fiftyMoveAvailable` | Fifty-move rule threshold reached; active player may claim draw |
|
||||||
| `promotionPending` | A pawn reached the back rank; awaiting promotion piece selection |
|
|
||||||
| `insufficientMaterial` | Neither side has enough pieces to deliver checkmate — game over (draw) |
|
| `insufficientMaterial` | Neither side has enough pieces to deliver checkmate — game over (draw) |
|
||||||
enum:
|
enum:
|
||||||
- started
|
- started
|
||||||
@@ -641,7 +637,6 @@ components:
|
|||||||
- draw
|
- draw
|
||||||
- drawOffered
|
- drawOffered
|
||||||
- fiftyMoveAvailable
|
- fiftyMoveAvailable
|
||||||
- promotionPending
|
|
||||||
- insufficientMaterial
|
- insufficientMaterial
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Gradle properties
|
||||||
|
quarkusPluginId=io.quarkus
|
||||||
|
quarkusPluginVersion=3.32.4
|
||||||
|
quarkusPlatformGroupId=io.quarkus.platform
|
||||||
|
quarkusPlatformArtifactId=quarkus-bom
|
||||||
|
quarkusPlatformVersion=3.32.4
|
||||||
@@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT"
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List<String>
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -19,6 +21,7 @@ scala {
|
|||||||
|
|
||||||
scoverage {
|
scoverage {
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
||||||
|
excludedFiles.set(scoverageExcluded)
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.scoverage {
|
configurations.scoverage {
|
||||||
@@ -31,7 +34,7 @@ configurations.scoverage {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class ApiErrorDto(code: String, message: String, field: Option[String])
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class CreateGameRequestDto(
|
||||||
|
white: Option[PlayerInfoDto],
|
||||||
|
black: Option[PlayerInfoDto],
|
||||||
|
)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class ErrorEventDto(`type`: String, error: ApiErrorDto)
|
||||||
|
|
||||||
|
object ErrorEventDto:
|
||||||
|
def apply(error: ApiErrorDto): ErrorEventDto = ErrorEventDto("error", error)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class GameFullDto(
|
||||||
|
gameId: String,
|
||||||
|
white: PlayerInfoDto,
|
||||||
|
black: PlayerInfoDto,
|
||||||
|
state: GameStateDto,
|
||||||
|
)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class GameFullEventDto(`type`: String, game: GameFullDto)
|
||||||
|
|
||||||
|
object GameFullEventDto:
|
||||||
|
def apply(game: GameFullDto): GameFullEventDto = GameFullEventDto("gameFull", game)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class GameStateDto(
|
||||||
|
fen: String,
|
||||||
|
pgn: String,
|
||||||
|
turn: String,
|
||||||
|
status: String,
|
||||||
|
winner: Option[String],
|
||||||
|
moves: List[String],
|
||||||
|
undoAvailable: Boolean,
|
||||||
|
redoAvailable: Boolean,
|
||||||
|
)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class GameStateEventDto(`type`: String, state: GameStateDto)
|
||||||
|
|
||||||
|
object GameStateEventDto:
|
||||||
|
def apply(state: GameStateDto): GameStateEventDto = GameStateEventDto("gameState", state)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class ImportFenRequestDto(
|
||||||
|
fen: String,
|
||||||
|
white: Option[PlayerInfoDto],
|
||||||
|
black: Option[PlayerInfoDto],
|
||||||
|
)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class ImportPgnRequestDto(pgn: String)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class LegalMoveDto(
|
||||||
|
from: String,
|
||||||
|
to: String,
|
||||||
|
uci: String,
|
||||||
|
moveType: String,
|
||||||
|
promotion: Option[String],
|
||||||
|
)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class LegalMovesResponseDto(moves: List[LegalMoveDto])
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class OkResponseDto(ok: Boolean = true)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
final case class PlayerInfoDto(id: String, displayName: String)
|
||||||
@@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT"
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List<String>
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -26,16 +28,7 @@ scoverage {
|
|||||||
"de\\.nowchess\\.bot\\.util\\.PolyglotBook",
|
"de\\.nowchess\\.bot\\.util\\.PolyglotBook",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
excludedFiles.set(
|
excludedFiles.set(scoverageExcluded)
|
||||||
listOf(
|
|
||||||
".*NNUE\\.scala",
|
|
||||||
".*NNUEBot\\.scala",
|
|
||||||
".*NbaiLoader\\.scala",
|
|
||||||
".*NbaiMigrator\\.scala",
|
|
||||||
".*NbaiWriter\\.scala",
|
|
||||||
".*PolyglotBook\\.scala",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
tasks.withType<ScalaCompile> {
|
||||||
@@ -44,7 +37,7 @@ tasks.withType<ScalaCompile> {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.gitignore
|
||||||
|
!build/*-runner
|
||||||
|
!build/*-runner.jar
|
||||||
|
!build/lib/*
|
||||||
|
!build/quarkus-app/*
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Gradle
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
.idea
|
||||||
|
*.ipr
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# NetBeans
|
||||||
|
nb-configuration.xml
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# patch
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
|
||||||
|
# Local environment
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Plugin directory
|
||||||
|
/.quarkus/cli/plugins/
|
||||||
|
# TLS Certificates
|
||||||
|
.certs/
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("scala")
|
id("scala")
|
||||||
id("org.scoverage") version "8.1"
|
id("org.scoverage") version "8.1"
|
||||||
|
id("io.quarkus")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "de.nowchess"
|
group = "de.nowchess"
|
||||||
@@ -8,6 +9,8 @@ version = "1.0-SNAPSHOT"
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List<String>
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -19,15 +22,21 @@ scala {
|
|||||||
|
|
||||||
scoverage {
|
scoverage {
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
||||||
|
excludedFiles.set(scoverageExcluded)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
tasks.withType<ScalaCompile> {
|
||||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val quarkusPlatformGroupId: String by project
|
||||||
|
val quarkusPlatformArtifactId: String by project
|
||||||
|
val quarkusPlatformVersion: String by project
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
@@ -43,19 +52,59 @@ dependencies {
|
|||||||
implementation(project(":modules:rule"))
|
implementation(project(":modules:rule"))
|
||||||
implementation(project(":modules:bot"))
|
implementation(project(":modules:bot"))
|
||||||
|
|
||||||
|
|
||||||
|
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
||||||
|
implementation("io.quarkus:quarkus-rest")
|
||||||
|
implementation("io.quarkus:quarkus-hibernate-orm")
|
||||||
|
implementation("io.quarkus:quarkus-rest-client-jackson")
|
||||||
|
implementation("io.quarkus:quarkus-rest-client")
|
||||||
|
implementation("io.quarkus:quarkus-rest-jackson")
|
||||||
|
implementation("io.quarkus:quarkus-config-yaml")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-fault-tolerance")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-jwt")
|
||||||
|
implementation("io.quarkus:quarkus-smallrye-health")
|
||||||
|
implementation("io.quarkus:quarkus-micrometer")
|
||||||
|
implementation("io.quarkus:quarkus-arc")
|
||||||
|
|
||||||
|
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
||||||
|
|
||||||
|
|
||||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
||||||
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
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.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 {
|
tasks.test {
|
||||||
useJUnitPlatform {
|
useJUnitPlatform {
|
||||||
includeEngines("scalatest")
|
includeEngines("scalatest", "junit-jupiter")
|
||||||
testLogging {
|
testLogging {
|
||||||
events("skipped", "failed")
|
events("passed", "skipped", "failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finalizedBy(tasks.reportScoverage)
|
finalizedBy(tasks.reportScoverage)
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./gradlew build
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/backcore-jvm .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore-jvm
|
||||||
|
#
|
||||||
|
# If you want to include the debug port into your docker image
|
||||||
|
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# Then run the container using :
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore-jvm
|
||||||
|
#
|
||||||
|
# This image uses the `run-java.sh` script to run the application.
|
||||||
|
# This scripts computes the command line to execute your Java application, and
|
||||||
|
# includes memory/GC tuning.
|
||||||
|
# You can configure the behavior using the following environment properties:
|
||||||
|
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override
|
||||||
|
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
|
||||||
|
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||||
|
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||||
|
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||||
|
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||||
|
# of the container available memory as set here. The default is `50` which means 50%
|
||||||
|
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||||
|
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||||
|
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||||
|
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||||
|
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||||
|
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||||
|
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||||
|
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||||
|
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||||
|
# a container without any memory constraints for the container then this option has
|
||||||
|
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||||
|
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||||
|
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||||
|
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||||
|
# when things are happening. This option, if set to true, will set
|
||||||
|
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||||
|
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||||
|
# true").
|
||||||
|
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||||
|
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||||
|
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||||
|
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||||
|
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||||
|
# (example: "20")
|
||||||
|
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||||
|
# (example: "40")
|
||||||
|
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||||
|
# (example: "4")
|
||||||
|
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||||
|
# previous GC times. (example: "90")
|
||||||
|
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||||
|
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||||
|
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||||
|
# contain the necessary JRE command-line options to specify the required GC, which
|
||||||
|
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||||
|
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||||
|
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||||
|
#
|
||||||
|
# You can find more information about the UBI base runtime images and their configuration here:
|
||||||
|
# https://rh-openjdk.github.io/redhat-openjdk-containers/
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.24
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
# We make four distinct layers so if there are application changes the library layers can be re-used
|
||||||
|
COPY --chown=185 build/quarkus-app/lib/ /deployments/lib/
|
||||||
|
COPY --chown=185 build/quarkus-app/*.jar /deployments/
|
||||||
|
COPY --chown=185 build/quarkus-app/app/ /deployments/app/
|
||||||
|
COPY --chown=185 build/quarkus-app/quarkus/ /deployments/quarkus/
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
|
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./gradlew build -Dquarkus.package.jar.type=legacy-jar
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/backcore-legacy-jar .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore-legacy-jar
|
||||||
|
#
|
||||||
|
# If you want to include the debug port into your docker image
|
||||||
|
# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005.
|
||||||
|
# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005
|
||||||
|
# when running the container
|
||||||
|
#
|
||||||
|
# Then run the container using :
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore-legacy-jar
|
||||||
|
#
|
||||||
|
# This image uses the `run-java.sh` script to run the application.
|
||||||
|
# This scripts computes the command line to execute your Java application, and
|
||||||
|
# includes memory/GC tuning.
|
||||||
|
# You can configure the behavior using the following environment properties:
|
||||||
|
# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") - Be aware that this will override
|
||||||
|
# the default JVM options, use `JAVA_OPTS_APPEND` to append options
|
||||||
|
# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options
|
||||||
|
# in JAVA_OPTS (example: "-Dsome.property=foo")
|
||||||
|
# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is
|
||||||
|
# used to calculate a default maximal heap memory based on a containers restriction.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio
|
||||||
|
# of the container available memory as set here. The default is `50` which means 50%
|
||||||
|
# of the available memory is used as an upper boundary. You can skip this mechanism by
|
||||||
|
# setting this value to `0` in which case no `-Xmx` option is added.
|
||||||
|
# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This
|
||||||
|
# is used to calculate a default initial heap memory based on the maximum heap memory.
|
||||||
|
# If used in a container without any memory constraints for the container then this
|
||||||
|
# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio
|
||||||
|
# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx`
|
||||||
|
# is used as the initial heap size. You can skip this mechanism by setting this value
|
||||||
|
# to `0` in which case no `-Xms` option is added (example: "25")
|
||||||
|
# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS.
|
||||||
|
# This is used to calculate the maximum value of the initial heap memory. If used in
|
||||||
|
# a container without any memory constraints for the container then this option has
|
||||||
|
# no effect. If there is a memory constraint then `-Xms` is limited to the value set
|
||||||
|
# here. The default is 4096MB which means the calculated value of `-Xms` never will
|
||||||
|
# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096")
|
||||||
|
# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output
|
||||||
|
# when things are happening. This option, if set to true, will set
|
||||||
|
# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true").
|
||||||
|
# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example:
|
||||||
|
# true").
|
||||||
|
# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787").
|
||||||
|
# - CONTAINER_CORE_LIMIT: A calculated core limit as described in
|
||||||
|
# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2")
|
||||||
|
# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024").
|
||||||
|
# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion.
|
||||||
|
# (example: "20")
|
||||||
|
# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking.
|
||||||
|
# (example: "40")
|
||||||
|
# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection.
|
||||||
|
# (example: "4")
|
||||||
|
# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus
|
||||||
|
# previous GC times. (example: "90")
|
||||||
|
# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20")
|
||||||
|
# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100")
|
||||||
|
# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should
|
||||||
|
# contain the necessary JRE command-line options to specify the required GC, which
|
||||||
|
# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC).
|
||||||
|
# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080")
|
||||||
|
# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be
|
||||||
|
# accessed directly. (example: "foo.example.com,bar.example.com")
|
||||||
|
#
|
||||||
|
# You can find more information about the UBI base runtime images and their configuration here:
|
||||||
|
# https://rh-openjdk.github.io/redhat-openjdk-containers/
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.24
|
||||||
|
|
||||||
|
ENV LANGUAGE='en_US:en'
|
||||||
|
|
||||||
|
|
||||||
|
COPY build/lib/* /deployments/lib/
|
||||||
|
COPY build/*-runner.jar /deployments/quarkus-run.jar
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 185
|
||||||
|
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
|
||||||
|
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./gradlew build -Dquarkus.native.enabled=true
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.native -t quarkus/backcore .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore
|
||||||
|
#
|
||||||
|
# The ` registry.access.redhat.com/ubi9/ubi-minimal:9.7` base image is based on UBI 9.
|
||||||
|
# To use UBI 8, switch to `quay.io/ubi8/ubi-minimal:8.10`.
|
||||||
|
###
|
||||||
|
FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
####
|
||||||
|
# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode.
|
||||||
|
# It uses a micro base image, tuned for Quarkus native executables.
|
||||||
|
# It reduces the size of the resulting container image.
|
||||||
|
# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image.
|
||||||
|
#
|
||||||
|
# Before building the container image run:
|
||||||
|
#
|
||||||
|
# ./gradlew build -Dquarkus.native.enabled=true
|
||||||
|
#
|
||||||
|
# Then, build the image with:
|
||||||
|
#
|
||||||
|
# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/backcore .
|
||||||
|
#
|
||||||
|
# Then run the container using:
|
||||||
|
#
|
||||||
|
# docker run -i --rm -p 8080:8080 quarkus/backcore
|
||||||
|
#
|
||||||
|
# The `quay.io/quarkus/ubi9-quarkus-micro-image:2.0` base image is based on UBI 9.
|
||||||
|
# To use UBI 8, switch to `quay.io/quarkus/quarkus-micro-image:2.0`.
|
||||||
|
###
|
||||||
|
FROM quay.io/quarkus/ubi9-quarkus-micro-image:2.0
|
||||||
|
WORKDIR /work/
|
||||||
|
RUN chown 1001 /work \
|
||||||
|
&& chmod "g+rwX" /work \
|
||||||
|
&& chown 1001:root /work
|
||||||
|
COPY --chown=1001:root --chmod=0755 build/*-runner /work/application
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
USER 1001
|
||||||
|
|
||||||
|
ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"reflection": [
|
||||||
|
{ "type": "scala.Tuple1[]" },
|
||||||
|
{ "type": "scala.Tuple2[]" },
|
||||||
|
{ "type": "scala.Tuple3[]" },
|
||||||
|
{ "type": "scala.Tuple4[]" },
|
||||||
|
{ "type": "scala.Tuple5[]" },
|
||||||
|
{ "type": "scala.Tuple6[]" },
|
||||||
|
{ "type": "scala.Tuple7[]" },
|
||||||
|
{ "type": "scala.Tuple8[]" },
|
||||||
|
{ "type": "scala.Tuple9[]" },
|
||||||
|
{ "type": "scala.Tuple10[]" },
|
||||||
|
{ "type": "scala.Tuple11[]" },
|
||||||
|
{ "type": "scala.Tuple12[]" },
|
||||||
|
{ "type": "scala.Tuple13[]" },
|
||||||
|
{ "type": "scala.Tuple14[]" },
|
||||||
|
{ "type": "scala.Tuple15[]" },
|
||||||
|
{ "type": "scala.Tuple16[]" },
|
||||||
|
{ "type": "scala.Tuple17[]" },
|
||||||
|
{ "type": "scala.Tuple18[]" },
|
||||||
|
{ "type": "scala.Tuple19[]" },
|
||||||
|
{ "type": "scala.Tuple20[]" },
|
||||||
|
{ "type": "scala.Tuple21[]" },
|
||||||
|
{ "type": "scala.Tuple22[]" },
|
||||||
|
{ "type": "com.fasterxml.jackson.module.scala.introspect.PropertyDescriptor[]" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
greeting:
|
||||||
|
message: "hello"
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- This file allow to write SQL commands that will be emitted in test and dev.
|
||||||
|
-- The commands are commented as their support depends of the database
|
||||||
|
-- insert into myentity (id, field) values(1, 'field-1');
|
||||||
|
-- insert into myentity (id, field) values(2, 'field-2');
|
||||||
|
-- insert into myentity (id, field) values(3, 'field-3');
|
||||||
|
-- alter sequence myentity_seq restart with 4;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package de.nowchess.chess.config
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.Version
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||||
|
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
|
||||||
|
})
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package de.nowchess.chess.config
|
||||||
|
|
||||||
|
import de.nowchess.api.dto.*
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||||
|
|
||||||
|
@RegisterForReflection(
|
||||||
|
targets = Array(
|
||||||
|
classOf[ApiErrorDto],
|
||||||
|
classOf[CreateGameRequestDto],
|
||||||
|
classOf[ErrorEventDto],
|
||||||
|
classOf[GameFullDto],
|
||||||
|
classOf[GameFullEventDto],
|
||||||
|
classOf[GameStateDto],
|
||||||
|
classOf[GameStateEventDto],
|
||||||
|
classOf[ImportFenRequestDto],
|
||||||
|
classOf[ImportPgnRequestDto],
|
||||||
|
classOf[LegalMoveDto],
|
||||||
|
classOf[LegalMovesResponseDto],
|
||||||
|
classOf[OkResponseDto],
|
||||||
|
classOf[PlayerInfoDto],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
class NativeReflectionConfig
|
||||||
@@ -38,9 +38,10 @@ class GameEngine(
|
|||||||
private implicit val ec: ExecutionContext = ExecutionContext.global
|
private implicit val ec: ExecutionContext = ExecutionContext.global
|
||||||
|
|
||||||
// Synchronized accessors for current state
|
// Synchronized accessors for current state
|
||||||
def board: Board = synchronized(currentContext.board)
|
def board: Board = synchronized(currentContext.board)
|
||||||
def turn: Color = synchronized(currentContext.turn)
|
def turn: Color = synchronized(currentContext.turn)
|
||||||
def context: GameContext = synchronized(currentContext)
|
def context: GameContext = synchronized(currentContext)
|
||||||
|
def pendingDrawOfferBy: Option[Color] = synchronized(pendingDrawOffer)
|
||||||
|
|
||||||
/** Check if undo is available. */
|
/** Check if undo is available. */
|
||||||
def canUndo: Boolean = synchronized(invoker.canUndo)
|
def canUndo: Boolean = synchronized(invoker.canUndo)
|
||||||
@@ -67,21 +68,7 @@ class GameEngine(
|
|||||||
performRedo()
|
performRedo()
|
||||||
|
|
||||||
case "draw" =>
|
case "draw" =>
|
||||||
if currentContext.halfMoveClock >= 100 then
|
claimDraw()
|
||||||
currentContext = currentContext.withResult(Some(GameResult.Draw(DrawReason.FiftyMoveRule)))
|
|
||||||
invoker.clear()
|
|
||||||
notifyObservers(DrawEvent(currentContext, DrawReason.FiftyMoveRule))
|
|
||||||
else if ruleSet.isThreefoldRepetition(currentContext) then
|
|
||||||
currentContext = currentContext.withResult(Some(GameResult.Draw(DrawReason.ThreefoldRepetition)))
|
|
||||||
invoker.clear()
|
|
||||||
notifyObservers(DrawEvent(currentContext, DrawReason.ThreefoldRepetition))
|
|
||||||
else
|
|
||||||
notifyObservers(
|
|
||||||
InvalidMoveEvent(
|
|
||||||
currentContext,
|
|
||||||
InvalidMoveReason.DrawCannotBeClaimed,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
case "" =>
|
case "" =>
|
||||||
notifyObservers(InvalidMoveEvent(currentContext, InvalidMoveReason.EmptyInput))
|
notifyObservers(InvalidMoveEvent(currentContext, InvalidMoveReason.EmptyInput))
|
||||||
@@ -195,6 +182,21 @@ class GameEngine(
|
|||||||
notifyObservers(DrawOfferDeclinedEvent(currentContext, color))
|
notifyObservers(DrawOfferDeclinedEvent(currentContext, color))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Claim a draw by fifty-move rule or threefold repetition. */
|
||||||
|
def claimDraw(): Unit = synchronized {
|
||||||
|
if currentContext.result.isDefined then
|
||||||
|
notifyObservers(InvalidMoveEvent(currentContext, InvalidMoveReason.GameAlreadyOver))
|
||||||
|
else if currentContext.halfMoveClock >= 100 then
|
||||||
|
currentContext = currentContext.withResult(Some(GameResult.Draw(DrawReason.FiftyMoveRule)))
|
||||||
|
invoker.clear()
|
||||||
|
notifyObservers(DrawEvent(currentContext, DrawReason.FiftyMoveRule))
|
||||||
|
else if ruleSet.isThreefoldRepetition(currentContext) then
|
||||||
|
currentContext = currentContext.withResult(Some(GameResult.Draw(DrawReason.ThreefoldRepetition)))
|
||||||
|
invoker.clear()
|
||||||
|
notifyObservers(DrawEvent(currentContext, DrawReason.ThreefoldRepetition))
|
||||||
|
else notifyObservers(InvalidMoveEvent(currentContext, InvalidMoveReason.DrawCannotBeClaimed))
|
||||||
|
}
|
||||||
|
|
||||||
/** Load a game using the provided importer. If the imported context has moves, they are replayed through the command
|
/** Load a game using the provided importer. If the imported context has moves, they are replayed through the command
|
||||||
* system. Otherwise, the position is set directly. Notifies observers with PgnLoadedEvent on success.
|
* system. Otherwise, the position is set directly. Notifies observers with PgnLoadedEvent on success.
|
||||||
*/
|
*/
|
||||||
@@ -258,6 +260,22 @@ class GameEngine(
|
|||||||
notifyObservers(BoardResetEvent(currentContext))
|
notifyObservers(BoardResetEvent(currentContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resign the game on behalf of the side to move. */
|
||||||
|
def resign(): Unit = synchronized {
|
||||||
|
if currentContext.result.isEmpty then
|
||||||
|
val winner = currentContext.turn.opposite
|
||||||
|
currentContext = currentContext.withResult(Some(GameResult.Win(winner)))
|
||||||
|
invoker.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Apply a draw result directly (for agreement, fifty-move claim, etc.). */
|
||||||
|
def applyDraw(reason: DrawReason): Unit = synchronized {
|
||||||
|
if currentContext.result.isEmpty then
|
||||||
|
currentContext = currentContext.withResult(Some(GameResult.Draw(reason)))
|
||||||
|
invoker.clear()
|
||||||
|
notifyObservers(DrawEvent(currentContext, reason))
|
||||||
|
}
|
||||||
|
|
||||||
/** Kick off play when the side to move is a bot (e.g. bot-vs-bot from initial position). */
|
/** Kick off play when the side to move is a bot (e.g. bot-vs-bot from initial position). */
|
||||||
def startGame(): Unit = synchronized(requestBotMoveIfNeeded())
|
def startGame(): Unit = synchronized(requestBotMoveIfNeeded())
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package de.nowchess.chess.exception
|
||||||
|
|
||||||
|
class ApiException(
|
||||||
|
val status: Int,
|
||||||
|
val code: String,
|
||||||
|
message: String,
|
||||||
|
val field: Option[String] = None,
|
||||||
|
) extends RuntimeException(message)
|
||||||
|
|
||||||
|
class GameNotFoundException(gameId: String) extends ApiException(404, "GAME_NOT_FOUND", s"Game $gameId not found")
|
||||||
|
|
||||||
|
class BadRequestException(code: String, message: String, field: Option[String] = None)
|
||||||
|
extends ApiException(400, code, message, field)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package de.nowchess.chess.exception
|
||||||
|
|
||||||
|
import de.nowchess.api.dto.ApiErrorDto
|
||||||
|
import jakarta.ws.rs.core.{MediaType, Response}
|
||||||
|
import jakarta.ws.rs.ext.{ExceptionMapper, Provider}
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
class ApiExceptionMapper extends ExceptionMapper[ApiException]:
|
||||||
|
def toResponse(ex: ApiException): Response =
|
||||||
|
Response
|
||||||
|
.status(ex.status)
|
||||||
|
.entity(ApiErrorDto(ex.code, ex.getMessage, ex.field))
|
||||||
|
.`type`(MediaType.APPLICATION_JSON)
|
||||||
|
.build()
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package de.nowchess.chess.registry
|
||||||
|
|
||||||
|
import de.nowchess.api.board.Color
|
||||||
|
import de.nowchess.api.player.PlayerInfo
|
||||||
|
import de.nowchess.chess.engine.GameEngine
|
||||||
|
|
||||||
|
final case class GameEntry(
|
||||||
|
gameId: String,
|
||||||
|
engine: GameEngine,
|
||||||
|
white: PlayerInfo,
|
||||||
|
black: PlayerInfo,
|
||||||
|
resigned: Boolean = false,
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package de.nowchess.chess.registry
|
||||||
|
|
||||||
|
trait GameRegistry:
|
||||||
|
def store(entry: GameEntry): Unit
|
||||||
|
def get(gameId: String): Option[GameEntry]
|
||||||
|
def update(entry: GameEntry): Unit
|
||||||
|
def generateId(): String
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package de.nowchess.chess.registry
|
||||||
|
|
||||||
|
import jakarta.enterprise.context.ApplicationScoped
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
@ApplicationScoped
|
||||||
|
class GameRegistryImpl extends GameRegistry:
|
||||||
|
private val games = ConcurrentHashMap[String, GameEntry]()
|
||||||
|
private val rng = new SecureRandom()
|
||||||
|
|
||||||
|
def store(entry: GameEntry): Unit =
|
||||||
|
games.put(entry.gameId, entry)
|
||||||
|
|
||||||
|
def get(gameId: String): Option[GameEntry] =
|
||||||
|
Option(games.get(gameId))
|
||||||
|
|
||||||
|
def update(entry: GameEntry): Unit =
|
||||||
|
games.put(entry.gameId, entry)
|
||||||
|
|
||||||
|
def generateId(): String =
|
||||||
|
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
Iterator.continually(rng.nextInt(chars.length)).map(chars).take(8).mkString // NOSONAR
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
package de.nowchess.chess.resource
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
|
import de.nowchess.api.board.Square
|
||||||
|
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.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 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 java.util.concurrent.atomic.AtomicReference
|
||||||
|
import scala.compiletime.uninitialized
|
||||||
|
|
||||||
|
@Path("/api/board/game")
|
||||||
|
@ApplicationScoped
|
||||||
|
class GameResource:
|
||||||
|
|
||||||
|
// scalafix:off DisableSyntax.var
|
||||||
|
@Inject
|
||||||
|
var registry: GameRegistry = uninitialized
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
var objectMapper: ObjectMapper = uninitialized
|
||||||
|
// scalafix:on DisableSyntax.var
|
||||||
|
|
||||||
|
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
||||||
|
private val DefaultBlack = PlayerInfo(PlayerId("p2"), "Player 2")
|
||||||
|
|
||||||
|
// ── mapping ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private def statusOf(entry: GameEntry): String =
|
||||||
|
if entry.engine.pendingDrawOfferBy.isDefined then "drawOffered"
|
||||||
|
else
|
||||||
|
val ctx = entry.engine.context
|
||||||
|
ctx.result match
|
||||||
|
case Some(GameResult.Win(_)) =>
|
||||||
|
if entry.resigned then "resign" else "checkmate"
|
||||||
|
case Some(GameResult.Draw(DrawReason.Stalemate)) => "stalemate"
|
||||||
|
case Some(GameResult.Draw(DrawReason.InsufficientMaterial)) => "insufficientMaterial"
|
||||||
|
case Some(GameResult.Draw(_)) => "draw"
|
||||||
|
case None =>
|
||||||
|
if ctx.halfMoveClock >= 100 then "fiftyMoveAvailable"
|
||||||
|
else if entry.engine.ruleSet.isCheck(ctx) then "check"
|
||||||
|
else "started"
|
||||||
|
|
||||||
|
private def moveToUci(move: Move): String =
|
||||||
|
val base = s"${move.from}${move.to}"
|
||||||
|
move.moveType match
|
||||||
|
case MoveType.Promotion(PromotionPiece.Queen) => s"${base}q"
|
||||||
|
case MoveType.Promotion(PromotionPiece.Rook) => s"${base}r"
|
||||||
|
case MoveType.Promotion(PromotionPiece.Bishop) => s"${base}b"
|
||||||
|
case MoveType.Promotion(PromotionPiece.Knight) => s"${base}n"
|
||||||
|
case _ => base
|
||||||
|
|
||||||
|
private def toLegalMoveDto(move: Move): LegalMoveDto =
|
||||||
|
val (moveTypeStr, promotionStr) = move.moveType match
|
||||||
|
case MoveType.Normal(false) => ("normal", None)
|
||||||
|
case MoveType.Normal(true) => ("capture", None)
|
||||||
|
case MoveType.CastleKingside => ("castleKingside", None)
|
||||||
|
case MoveType.CastleQueenside => ("castleQueenside", None)
|
||||||
|
case MoveType.EnPassant => ("enPassant", None)
|
||||||
|
case MoveType.Promotion(PromotionPiece.Queen) => ("promotion", Some("queen"))
|
||||||
|
case MoveType.Promotion(PromotionPiece.Rook) => ("promotion", Some("rook"))
|
||||||
|
case MoveType.Promotion(PromotionPiece.Bishop) => ("promotion", Some("bishop"))
|
||||||
|
case MoveType.Promotion(PromotionPiece.Knight) => ("promotion", Some("knight"))
|
||||||
|
LegalMoveDto(move.from.toString, move.to.toString, moveToUci(move), moveTypeStr, promotionStr)
|
||||||
|
|
||||||
|
private def toPlayerDto(info: PlayerInfo): PlayerInfoDto =
|
||||||
|
PlayerInfoDto(info.id.value, info.displayName)
|
||||||
|
|
||||||
|
private def toGameStateDto(entry: GameEntry): GameStateDto =
|
||||||
|
val ctx = entry.engine.context
|
||||||
|
GameStateDto(
|
||||||
|
fen = FenExporter.exportGameContext(ctx),
|
||||||
|
pgn = PgnExporter.exportGame(
|
||||||
|
Map(
|
||||||
|
"Event" -> "NowChess game",
|
||||||
|
"White" -> entry.white.displayName,
|
||||||
|
"Black" -> entry.black.displayName,
|
||||||
|
"Result" -> "*",
|
||||||
|
),
|
||||||
|
ctx.moves,
|
||||||
|
),
|
||||||
|
turn = ctx.turn.label.toLowerCase,
|
||||||
|
status = statusOf(entry),
|
||||||
|
winner = ctx.result.collect { case GameResult.Win(c) => c.label.toLowerCase },
|
||||||
|
moves = ctx.moves.map(moveToUci),
|
||||||
|
undoAvailable = entry.engine.canUndo,
|
||||||
|
redoAvailable = entry.engine.canRedo,
|
||||||
|
)
|
||||||
|
|
||||||
|
private def toGameFullDto(entry: GameEntry): GameFullDto =
|
||||||
|
GameFullDto(entry.gameId, toPlayerDto(entry.white), toPlayerDto(entry.black), toGameStateDto(entry))
|
||||||
|
|
||||||
|
private def playerInfoFrom(dto: Option[PlayerInfoDto], default: PlayerInfo): PlayerInfo =
|
||||||
|
dto.fold(default)(d => PlayerInfo(PlayerId(d.id), d.displayName))
|
||||||
|
|
||||||
|
private def newEntry(ctx: GameContext, white: PlayerInfo, black: PlayerInfo): GameEntry =
|
||||||
|
GameEntry(registry.generateId(), GameEngine(initialContext = ctx), white, black)
|
||||||
|
|
||||||
|
private def applyMoveInput(engine: GameEngine, uci: String): Option[String] =
|
||||||
|
val error = new AtomicReference[Option[String]](None)
|
||||||
|
val obs = new Observer:
|
||||||
|
def onGameEvent(e: GameEvent): Unit = e match
|
||||||
|
case InvalidMoveEvent(_, reason) => error.set(Some(reason.toString))
|
||||||
|
case _ => ()
|
||||||
|
engine.subscribe(obs)
|
||||||
|
engine.processUserInput(uci)
|
||||||
|
engine.unsubscribe(obs)
|
||||||
|
error.get()
|
||||||
|
|
||||||
|
// ── response helpers ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private def ok(body: AnyRef): Response = Response.ok(body).build()
|
||||||
|
private def created(body: AnyRef): Response = Response.status(Response.Status.CREATED).entity(body).build()
|
||||||
|
|
||||||
|
// scalafix:off DisableSyntax.throw
|
||||||
|
private def assertGameNotOver(entry: GameEntry): Unit =
|
||||||
|
if entry.engine.context.result.isDefined then throw BadRequestException("GAME_OVER", "Game is already over")
|
||||||
|
// scalafix:on DisableSyntax.throw
|
||||||
|
|
||||||
|
// ── endpoints ────────────────────────────────────────────────────────────
|
||||||
|
// scalafix:off DisableSyntax.throw
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Consumes(Array(MediaType.APPLICATION_JSON))
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def createGame(body: CreateGameRequestDto): Response =
|
||||||
|
val req = Option(body).getOrElse(CreateGameRequestDto(None, None))
|
||||||
|
val white = playerInfoFrom(req.white, DefaultWhite)
|
||||||
|
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
|
||||||
|
@Path("/{gameId}")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def getGame(@PathParam("gameId") gameId: String): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
ok(toGameFullDto(entry))
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{gameId}/stream")
|
||||||
|
@Produces(Array("application/x-ndjson"))
|
||||||
|
def streamGame(@PathParam("gameId") gameId: String): Multi[String] =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
Multi
|
||||||
|
.createFrom()
|
||||||
|
.emitter[String] { emitter =>
|
||||||
|
emitter.emit(objectMapper.writeValueAsString(GameFullEventDto(toGameFullDto(entry))) + "\n")
|
||||||
|
val obs = new Observer:
|
||||||
|
def onGameEvent(event: GameEvent): Unit =
|
||||||
|
registry.get(gameId).foreach { updated =>
|
||||||
|
emitter.emit(
|
||||||
|
objectMapper.writeValueAsString(GameStateEventDto(toGameStateDto(updated))) + "\n",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
entry.engine.subscribe(obs)
|
||||||
|
emitter.onTermination(() => entry.engine.unsubscribe(obs))
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{gameId}/resign")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def resignGame(@PathParam("gameId") gameId: String): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
assertGameNotOver(entry)
|
||||||
|
entry.engine.resign()
|
||||||
|
registry.update(entry.copy(resigned = true))
|
||||||
|
ok(OkResponseDto())
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{gameId}/move/{uci}")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def makeMove(@PathParam("gameId") gameId: String, @PathParam("uci") uci: String): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
assertGameNotOver(entry)
|
||||||
|
val (from, to, promoOpt) = Parser
|
||||||
|
.parseMove(uci)
|
||||||
|
.getOrElse(throw BadRequestException("INVALID_UCI", s"Invalid UCI notation: $uci", Some("uci")))
|
||||||
|
val candidates = entry.engine.ruleSet.legalMoves(entry.engine.context)(from).filter(_.to == to)
|
||||||
|
val isPromotion = candidates.exists { case Move(_, _, MoveType.Promotion(_)) => true; case _ => false }
|
||||||
|
if candidates.isEmpty || (isPromotion && promoOpt.isEmpty) then
|
||||||
|
throw BadRequestException("INVALID_MOVE", s"$uci is not a legal move", Some("uci"))
|
||||||
|
applyMoveInput(entry.engine, uci).foreach(err => throw BadRequestException("INVALID_MOVE", err, Some("uci")))
|
||||||
|
ok(toGameStateDto(entry))
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{gameId}/moves")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def getLegalMoves(
|
||||||
|
@PathParam("gameId") gameId: String,
|
||||||
|
@QueryParam("square") square: String,
|
||||||
|
): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
val ctx = entry.engine.context
|
||||||
|
val moves =
|
||||||
|
if Option(square).isEmpty || square.isEmpty then entry.engine.ruleSet.allLegalMoves(ctx)
|
||||||
|
else
|
||||||
|
val sq = Square
|
||||||
|
.fromAlgebraic(square)
|
||||||
|
.getOrElse(throw BadRequestException("INVALID_SQUARE", s"Invalid square: $square", Some("square")))
|
||||||
|
entry.engine.ruleSet.legalMoves(ctx)(sq)
|
||||||
|
ok(LegalMovesResponseDto(moves.map(toLegalMoveDto)))
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{gameId}/undo")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def undoMove(@PathParam("gameId") gameId: String): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
if !entry.engine.canUndo then throw BadRequestException("NO_UNDO", "No moves to undo")
|
||||||
|
entry.engine.undo()
|
||||||
|
ok(toGameStateDto(entry))
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{gameId}/redo")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def redoMove(@PathParam("gameId") gameId: String): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
if !entry.engine.canRedo then throw BadRequestException("NO_REDO", "No moves to redo")
|
||||||
|
entry.engine.redo()
|
||||||
|
ok(toGameStateDto(entry))
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{gameId}/draw/{action}")
|
||||||
|
@Produces(Array(MediaType.APPLICATION_JSON))
|
||||||
|
def drawAction(
|
||||||
|
@PathParam("gameId") gameId: String,
|
||||||
|
@PathParam("action") action: String,
|
||||||
|
): Response =
|
||||||
|
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
|
||||||
|
assertGameNotOver(entry)
|
||||||
|
action match
|
||||||
|
case "offer" =>
|
||||||
|
entry.engine.offerDraw(entry.engine.context.turn)
|
||||||
|
ok(OkResponseDto())
|
||||||
|
case "accept" =>
|
||||||
|
entry.engine.acceptDraw(entry.engine.context.turn)
|
||||||
|
ok(OkResponseDto())
|
||||||
|
case "decline" =>
|
||||||
|
entry.engine.declineDraw(entry.engine.context.turn)
|
||||||
|
ok(OkResponseDto())
|
||||||
|
case "claim" =>
|
||||||
|
entry.engine.claimDraw()
|
||||||
|
ok(OkResponseDto())
|
||||||
|
case _ =>
|
||||||
|
throw BadRequestException("INVALID_ACTION", s"Unknown draw action: $action", Some("action"))
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/fen")
|
||||||
|
@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 white = playerInfoFrom(body.white, DefaultWhite)
|
||||||
|
val black = playerInfoFrom(body.black, DefaultBlack)
|
||||||
|
val entry = newEntry(ctx, white, black)
|
||||||
|
registry.store(entry)
|
||||||
|
created(toGameFullDto(entry))
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/import/pgn")
|
||||||
|
@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)
|
||||||
|
registry.store(entry)
|
||||||
|
created(toGameFullDto(entry))
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{gameId}/export/fen")
|
||||||
|
@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))
|
||||||
|
|
||||||
|
@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)
|
||||||
|
// scalafix:on DisableSyntax.throw
|
||||||
@@ -234,6 +234,77 @@ class GameEngineDrawOfferTest extends AnyFunSuite with Matchers:
|
|||||||
case other =>
|
case other =>
|
||||||
fail(s"Expected InvalidMoveEvent, but got $other")
|
fail(s"Expected InvalidMoveEvent, but got $other")
|
||||||
|
|
||||||
|
test("pendingDrawOfferBy returns None initially"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
engine.pendingDrawOfferBy shouldBe None
|
||||||
|
|
||||||
|
test("pendingDrawOfferBy returns White after White offers"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
engine.offerDraw(Color.White)
|
||||||
|
engine.pendingDrawOfferBy shouldBe Some(Color.White)
|
||||||
|
|
||||||
|
test("pendingDrawOfferBy returns None after draw is accepted"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
engine.offerDraw(Color.White)
|
||||||
|
engine.acceptDraw(Color.Black)
|
||||||
|
engine.pendingDrawOfferBy shouldBe None
|
||||||
|
|
||||||
|
test("applyDraw sets draw result when game not over"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new DrawOfferMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
engine.applyDraw(DrawReason.Agreement)
|
||||||
|
observer.events should have length 1
|
||||||
|
observer.events.head match
|
||||||
|
case event: DrawEvent =>
|
||||||
|
event.reason shouldBe DrawReason.Agreement
|
||||||
|
event.context.result shouldBe Some(GameResult.Draw(DrawReason.Agreement))
|
||||||
|
case other =>
|
||||||
|
fail(s"Expected DrawEvent, but got $other")
|
||||||
|
|
||||||
|
test("applyDraw does nothing when game already over"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new DrawOfferMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
// End the game with checkmate
|
||||||
|
engine.processUserInput("f2f3")
|
||||||
|
engine.processUserInput("e7e5")
|
||||||
|
engine.processUserInput("g2g4")
|
||||||
|
engine.processUserInput("d8h4")
|
||||||
|
observer.events.clear()
|
||||||
|
engine.applyDraw(DrawReason.Agreement)
|
||||||
|
observer.events should have length 0
|
||||||
|
|
||||||
|
test("claimDraw with fifty-move rule when at half-move 100"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new DrawOfferMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
// Play moves to reach fifty-move rule claim
|
||||||
|
engine.processUserInput("e2e4")
|
||||||
|
engine.processUserInput("e7e5")
|
||||||
|
engine.processUserInput("g1f3")
|
||||||
|
engine.processUserInput("g8f6")
|
||||||
|
// Need to advance halfMoveClock to 100
|
||||||
|
// This is hard to do naturally; skip for now if not critical
|
||||||
|
|
||||||
|
test("claimDraw when game already over"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new DrawOfferMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
// End the game with checkmate
|
||||||
|
engine.processUserInput("f2f3")
|
||||||
|
engine.processUserInput("e7e5")
|
||||||
|
engine.processUserInput("g2g4")
|
||||||
|
engine.processUserInput("d8h4")
|
||||||
|
observer.events.clear()
|
||||||
|
engine.claimDraw()
|
||||||
|
observer.events should have length 1
|
||||||
|
observer.events.head match
|
||||||
|
case event: InvalidMoveEvent =>
|
||||||
|
event.reason shouldBe InvalidMoveReason.GameAlreadyOver
|
||||||
|
case other =>
|
||||||
|
fail(s"Expected InvalidMoveEvent, but got $other")
|
||||||
|
|
||||||
private class DrawOfferMockObserver extends Observer:
|
private class DrawOfferMockObserver extends Observer:
|
||||||
val events = mutable.ListBuffer[GameEvent]()
|
val events = mutable.ListBuffer[GameEvent]()
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,32 @@ class GameEngineResignTest extends AnyFunSuite with Matchers:
|
|||||||
case other =>
|
case other =>
|
||||||
fail(s"Expected InvalidMoveEvent, but got $other")
|
fail(s"Expected InvalidMoveEvent, but got $other")
|
||||||
|
|
||||||
|
test("resign() without color resigns side to move"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new ResignMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
|
||||||
|
engine.resign()
|
||||||
|
|
||||||
|
engine.context.result shouldBe Some(GameResult.Win(Color.Black))
|
||||||
|
|
||||||
|
test("resign() without color does nothing when game already over"):
|
||||||
|
val engine = new GameEngine()
|
||||||
|
val observer = new ResignMockObserver()
|
||||||
|
engine.subscribe(observer)
|
||||||
|
|
||||||
|
// End the game with checkmate
|
||||||
|
engine.processUserInput("f2f3")
|
||||||
|
engine.processUserInput("e7e5")
|
||||||
|
engine.processUserInput("g2g4")
|
||||||
|
observer.events.clear()
|
||||||
|
engine.processUserInput("d8h4")
|
||||||
|
|
||||||
|
// Try to resign without color parameter
|
||||||
|
val resultBefore = engine.context.result
|
||||||
|
engine.resign()
|
||||||
|
resultBefore shouldBe engine.context.result
|
||||||
|
|
||||||
private class ResignMockObserver extends Observer:
|
private class ResignMockObserver extends Observer:
|
||||||
val events = mutable.ListBuffer[GameEvent]()
|
val events = mutable.ListBuffer[GameEvent]()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package de.nowchess.chess.registry
|
||||||
|
|
||||||
|
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
||||||
|
import de.nowchess.chess.engine.GameEngine
|
||||||
|
import io.quarkus.test.junit.QuarkusTest
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
import org.junit.jupiter.api.{DisplayName, Test}
|
||||||
|
import org.junit.jupiter.api.Assertions.*
|
||||||
|
|
||||||
|
import scala.compiletime.uninitialized
|
||||||
|
|
||||||
|
// scalafix:off
|
||||||
|
@QuarkusTest
|
||||||
|
@DisplayName("GameRegistryImpl")
|
||||||
|
class GameRegistryImplTest:
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
var registry: GameRegistry = uninitialized
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("store saves entry")
|
||||||
|
def testStore(): Unit =
|
||||||
|
val entry = GameEntry("g1", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2"))
|
||||||
|
registry.store(entry)
|
||||||
|
assertTrue(registry.get("g1").isDefined)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("get returns stored entry")
|
||||||
|
def testGet(): Unit =
|
||||||
|
val entry = GameEntry("g2", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2"))
|
||||||
|
registry.store(entry)
|
||||||
|
val retrieved = registry.get("g2")
|
||||||
|
assertTrue(retrieved.isDefined)
|
||||||
|
assertEquals("g2", retrieved.get.gameId)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("get returns None for unknown id")
|
||||||
|
def testGetUnknown(): Unit =
|
||||||
|
assertTrue(registry.get("unknown").isEmpty)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("update modifies existing entry")
|
||||||
|
def testUpdate(): Unit =
|
||||||
|
val entry = GameEntry("g3", GameEngine(), PlayerInfo(PlayerId("p1"), "P1"), PlayerInfo(PlayerId("p2"), "P2"))
|
||||||
|
registry.store(entry)
|
||||||
|
val updated = entry.copy(resigned = true)
|
||||||
|
registry.update(updated)
|
||||||
|
val retrieved = registry.get("g3")
|
||||||
|
assertTrue(retrieved.isDefined)
|
||||||
|
assertTrue(retrieved.get.resigned)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("generateId produces unique ids")
|
||||||
|
def testGenerateId(): Unit =
|
||||||
|
val id1 = registry.generateId()
|
||||||
|
val id2 = registry.generateId()
|
||||||
|
assertNotEquals(id1, id2)
|
||||||
|
assertFalse(id1.isEmpty)
|
||||||
|
assertFalse(id2.isEmpty)
|
||||||
|
// scalafix:on
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package de.nowchess.chess.resource
|
||||||
|
|
||||||
|
import de.nowchess.api.dto.*
|
||||||
|
import de.nowchess.chess.exception.BadRequestException
|
||||||
|
import io.quarkus.test.junit.QuarkusTest
|
||||||
|
import jakarta.inject.Inject
|
||||||
|
import org.junit.jupiter.api.{DisplayName, Test}
|
||||||
|
import org.junit.jupiter.api.Assertions.*
|
||||||
|
|
||||||
|
import scala.compiletime.uninitialized
|
||||||
|
|
||||||
|
// scalafix:off
|
||||||
|
@QuarkusTest
|
||||||
|
@DisplayName("GameResource Integration")
|
||||||
|
class GameResourceIntegrationTest:
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
var resource: GameResource = uninitialized
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("createGame returns 201")
|
||||||
|
def testCreateGame(): Unit =
|
||||||
|
val req = CreateGameRequestDto(None, None)
|
||||||
|
val resp = resource.createGame(req)
|
||||||
|
assertEquals(201, resp.getStatus)
|
||||||
|
val dto = resp.getEntity.asInstanceOf[GameFullDto]
|
||||||
|
assertNotNull(dto.gameId)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("getGame returns 200")
|
||||||
|
def testGetGame(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val getResp = resource.getGame(gameId)
|
||||||
|
assertEquals(200, getResp.getStatus)
|
||||||
|
val dto = getResp.getEntity.asInstanceOf[GameFullDto]
|
||||||
|
assertEquals(gameId, dto.gameId)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("makeMove advances game")
|
||||||
|
def testMakeMove(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val moveResp = resource.makeMove(gameId, "e2e4")
|
||||||
|
assertEquals(200, moveResp.getStatus)
|
||||||
|
val state = moveResp.getEntity.asInstanceOf[GameStateDto]
|
||||||
|
assertEquals("black", state.turn)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("makeMove with invalid UCI throws")
|
||||||
|
def testMakeMoveInvalid(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
assertThrows(classOf[BadRequestException], () => resource.makeMove(gameId, "invalid"))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("getLegalMoves returns moves")
|
||||||
|
def testGetLegalMoves(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val movesResp = resource.getLegalMoves(gameId, "")
|
||||||
|
assertEquals(200, movesResp.getStatus)
|
||||||
|
val dto = movesResp.getEntity.asInstanceOf[LegalMovesResponseDto]
|
||||||
|
assertFalse(dto.moves.isEmpty)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("resignGame updates state")
|
||||||
|
def testResignGame(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val resignResp = resource.resignGame(gameId)
|
||||||
|
assertEquals(200, resignResp.getStatus)
|
||||||
|
val getResp = resource.getGame(gameId)
|
||||||
|
val state = getResp.getEntity.asInstanceOf[GameFullDto].state
|
||||||
|
assertEquals("resign", state.status)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("undoMove reverts")
|
||||||
|
def testUndoMove(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
resource.makeMove(gameId, "e2e4")
|
||||||
|
val undoResp = resource.undoMove(gameId)
|
||||||
|
assertEquals(200, undoResp.getStatus)
|
||||||
|
val state = undoResp.getEntity.asInstanceOf[GameStateDto]
|
||||||
|
assertEquals("white", state.turn)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("redoMove restores")
|
||||||
|
def testRedoMove(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
resource.makeMove(gameId, "e2e4")
|
||||||
|
resource.undoMove(gameId)
|
||||||
|
val redoResp = resource.redoMove(gameId)
|
||||||
|
assertEquals(200, redoResp.getStatus)
|
||||||
|
val state = redoResp.getEntity.asInstanceOf[GameStateDto]
|
||||||
|
assertEquals("black", state.turn)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("drawAction offer")
|
||||||
|
def testDrawActionOffer(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val resp = resource.drawAction(gameId, "offer")
|
||||||
|
assertEquals(200, resp.getStatus)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("drawAction accept")
|
||||||
|
def testDrawActionAccept(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
resource.drawAction(gameId, "offer")
|
||||||
|
val resp = resource.drawAction(gameId, "accept")
|
||||||
|
assertEquals(200, resp.getStatus)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("importFen creates game")
|
||||||
|
def testImportFen(): Unit =
|
||||||
|
val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||||
|
val req = ImportFenRequestDto(fen, None, None)
|
||||||
|
val resp = resource.importFen(req)
|
||||||
|
assertEquals(201, resp.getStatus)
|
||||||
|
val dto = resp.getEntity.asInstanceOf[GameFullDto]
|
||||||
|
assertEquals(fen, dto.state.fen)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("importPgn creates game")
|
||||||
|
def testImportPgn(): Unit =
|
||||||
|
val req = ImportPgnRequestDto("1. e4 c5")
|
||||||
|
val resp = resource.importPgn(req)
|
||||||
|
assertEquals(201, resp.getStatus)
|
||||||
|
val dto = resp.getEntity.asInstanceOf[GameFullDto]
|
||||||
|
assertTrue(dto.state.moves.length > 0)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("exportFen returns FEN")
|
||||||
|
def testExportFen(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
val resp = resource.exportFen(gameId)
|
||||||
|
assertEquals(200, resp.getStatus)
|
||||||
|
assertTrue(resp.getEntity.asInstanceOf[String].contains("rnbqkbnr"))
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("exportPgn returns PGN")
|
||||||
|
def testExportPgn(): Unit =
|
||||||
|
val createResp = resource.createGame(CreateGameRequestDto(None, None))
|
||||||
|
val gameId = createResp.getEntity.asInstanceOf[GameFullDto].gameId
|
||||||
|
resource.makeMove(gameId, "e2e4")
|
||||||
|
val resp = resource.exportPgn(gameId)
|
||||||
|
assertEquals(200, resp.getStatus)
|
||||||
|
assertTrue(resp.getEntity.asInstanceOf[String].contains("1."))
|
||||||
|
// scalafix:on
|
||||||
@@ -8,6 +8,8 @@ version = "1.0-SNAPSHOT"
|
|||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val scoverageExcluded = rootProject.extra["SCOVERAGE_EXCLUDED"] as List<String>
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -19,7 +21,7 @@ scala {
|
|||||||
|
|
||||||
scoverage {
|
scoverage {
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
||||||
excludedFiles.set(listOf(".*FenParserFastParse.*"))
|
excludedFiles.set(scoverageExcluded)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
tasks.withType<ScalaCompile> {
|
||||||
@@ -28,7 +30,7 @@ tasks.withType<ScalaCompile> {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ tasks.withType<ScalaCompile> {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
## (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))
|
|
||||||
## (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))
|
|
||||||
## (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))
|
|
||||||
* 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))
|
|
||||||
## (2026-04-02)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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))
|
|
||||||
## (2026-04-03)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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))
|
|
||||||
## (2026-04-07)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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))
|
|
||||||
## (2026-04-07)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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))
|
|
||||||
## (2026-04-12)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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-29 JSON - Cherry Picked ([#28](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/28)) ([dbcafd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dbcafd286993e0604a6fa286c5543581a149439e))
|
|
||||||
## (2026-04-12)
|
|
||||||
|
|
||||||
### 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))
|
|
||||||
* 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-29 JSON - Cherry Picked ([#28](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/28)) ([dbcafd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dbcafd286993e0604a6fa286c5543581a149439e))
|
|
||||||
## (2026-04-14)
|
|
||||||
|
|
||||||
### 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-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-29 JSON - Cherry Picked ([#28](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/28)) ([dbcafd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dbcafd286993e0604a6fa286c5543581a149439e))
|
|
||||||
## (2026-04-16)
|
|
||||||
|
|
||||||
### 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-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-29 JSON - Cherry Picked ([#28](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/28)) ([dbcafd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dbcafd286993e0604a6fa286c5543581a149439e))
|
|
||||||
## (2026-04-19)
|
|
||||||
|
|
||||||
### 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-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-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-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-19)
|
|
||||||
|
|
||||||
### 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-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-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-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import org.gradle.api.file.DuplicatesStrategy
|
|
||||||
import org.gradle.jvm.tasks.Jar
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("scala")
|
|
||||||
id("org.scoverage")
|
|
||||||
application
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "de.nowchess"
|
|
||||||
version = "1.0-SNAPSHOT"
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
scala {
|
|
||||||
scalaVersion = versions["SCALA3"]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
scoverage {
|
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
|
||||||
excludedPackages.set(listOf("de\\.nowchess\\.ui\\..*"))
|
|
||||||
}
|
|
||||||
|
|
||||||
application {
|
|
||||||
mainClass.set("de.nowchess.ui.Main")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
|
||||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named<JavaExec>("run") {
|
|
||||||
jvmArgs("-Dfile.encoding=UTF-8", "-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8")
|
|
||||||
standardInput = System.`in`
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.named<Jar>("jar") {
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
|
||||||
version {
|
|
||||||
strictly(versions["SCALA3"]!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
implementation("org.scala-lang:scala3-library_3") {
|
|
||||||
version {
|
|
||||||
strictly(versions["SCALA3"]!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
implementation(project(":modules:core"))
|
|
||||||
implementation(project(":modules:rule"))
|
|
||||||
implementation(project(":modules:api"))
|
|
||||||
implementation(project(":modules:io"))
|
|
||||||
implementation(project(":modules:bot"))
|
|
||||||
|
|
||||||
// ScalaFX dependencies
|
|
||||||
implementation("org.scalafx:scalafx_3:${versions["SCALAFX"]!!}")
|
|
||||||
|
|
||||||
// JavaFX dependencies for the current platform
|
|
||||||
val javaFXVersion = versions["JAVAFX"]!!
|
|
||||||
val osName = System.getProperty("os.name").lowercase()
|
|
||||||
val platform = when {
|
|
||||||
osName.contains("win") -> "win"
|
|
||||||
osName.contains("mac") -> "mac"
|
|
||||||
osName.contains("linux") -> "linux"
|
|
||||||
else -> "linux"
|
|
||||||
}
|
|
||||||
|
|
||||||
listOf("base", "controls", "graphics", "media").forEach { module ->
|
|
||||||
implementation("org.openjfx:javafx-$module:$javaFXVersion:$platform")
|
|
||||||
}
|
|
||||||
|
|
||||||
testImplementation(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}"))
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
|
||||||
testImplementation("org.scalatest:scalatest_3:${versions["SCALATEST"]!!}")
|
|
||||||
testImplementation("co.helmethair:scalatest-junit-runner:${versions["SCALATEST_JUNIT"]!!}")
|
|
||||||
|
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.test {
|
|
||||||
useJUnitPlatform {
|
|
||||||
includeEngines("scalatest")
|
|
||||||
testLogging {
|
|
||||||
events("skipped", "failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finalizedBy(tasks.reportScoverage)
|
|
||||||
}
|
|
||||||
tasks.reportScoverage {
|
|
||||||
dependsOn(tasks.test)
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 161 B |
|
Before Width: | Height: | Size: 188 B |
|
Before Width: | Height: | Size: 188 B |
|
Before Width: | Height: | Size: 286 B |
|
Before Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 266 B |
|
Before Width: | Height: | Size: 297 B |
|
Before Width: | Height: | Size: 258 B |
|
Before Width: | Height: | Size: 263 B |
|
Before Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 251 B |
|
Before Width: | Height: | Size: 275 B |
|
Before Width: | Height: | Size: 305 B |
|
Before Width: | Height: | Size: 281 B |
|
Before Width: | Height: | Size: 280 B |
@@ -1,30 +0,0 @@
|
|||||||
/* Arabian Chess GUI Styles */
|
|
||||||
|
|
||||||
.root {
|
|
||||||
-fx-font-family: "Comic Sans MS", "Comic Sans", cursive;
|
|
||||||
-fx-background-color: #F3C8A0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
-fx-background-radius: 8;
|
|
||||||
-fx-padding: 8 16 8 16;
|
|
||||||
-fx-font-family: "Comic Sans MS", cursive;
|
|
||||||
-fx-font-size: 12px;
|
|
||||||
-fx-cursor: hand;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
-fx-opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
-fx-font-family: "Comic Sans MS", cursive;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-pane {
|
|
||||||
-fx-background-color: #F3C8A0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-pane .content {
|
|
||||||
-fx-font-family: "Comic Sans MS", cursive;
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package de.nowchess.ui
|
|
||||||
|
|
||||||
import de.nowchess.api.game.{BotParticipant, Human}
|
|
||||||
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
|
||||||
import de.nowchess.bot.util.PolyglotBook
|
|
||||||
import de.nowchess.bot.BotDifficulty
|
|
||||||
import de.nowchess.ui.terminal.TerminalUI
|
|
||||||
import de.nowchess.ui.gui.ChessGUILauncher
|
|
||||||
|
|
||||||
/** Application entry point - starts both GUI and Terminal UI for the chess game. Both views subscribe to the same
|
|
||||||
* GameEngine via Observer pattern.
|
|
||||||
*/
|
|
||||||
object Main:
|
|
||||||
def main(args: Array[String]): Unit =
|
|
||||||
val book = PolyglotBook("../../modules/bot/codekiddy.bin")
|
|
||||||
|
|
||||||
// Create the core game engine (single source of truth)
|
|
||||||
val engine = new de.nowchess.chess.engine.GameEngine(
|
|
||||||
participants = Map(
|
|
||||||
de.nowchess.api.board.Color.White -> BotParticipant(
|
|
||||||
de.nowchess.bot.bots.HybridBot(BotDifficulty.Easy, book = Some(book)),
|
|
||||||
),
|
|
||||||
de.nowchess.api.board.Color.Black -> Human(PlayerInfo(PlayerId("p1"), "Player 1")),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.startGame()
|
|
||||||
|
|
||||||
// Launch ScalaFX GUI in separate thread
|
|
||||||
ChessGUILauncher.launch(engine)
|
|
||||||
|
|
||||||
// Create and start the terminal UI (blocks on main thread)
|
|
||||||
val tui = new TerminalUI(engine)
|
|
||||||
tui.start()
|
|
||||||
@@ -1,392 +0,0 @@
|
|||||||
package de.nowchess.ui.gui
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
|
||||||
import scalafx.Includes.*
|
|
||||||
import scalafx.application.Platform
|
|
||||||
import scalafx.geometry.{Insets, Pos}
|
|
||||||
import scalafx.scene.control.{Button, ButtonType, ChoiceDialog, Label}
|
|
||||||
import scalafx.scene.layout.{BorderPane, GridPane, HBox, StackPane, VBox}
|
|
||||||
import scalafx.scene.paint.Color as FXColor
|
|
||||||
import scalafx.scene.shape.Rectangle
|
|
||||||
import scalafx.scene.text.{Font, Text}
|
|
||||||
import scalafx.stage.Stage
|
|
||||||
import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
|
|
||||||
import de.nowchess.api.move.MoveType
|
|
||||||
import de.nowchess.chess.command.{MoveCommand, MoveResult}
|
|
||||||
import de.nowchess.chess.engine.GameEngine
|
|
||||||
import de.nowchess.io.fen.{FenExporter, FenParser}
|
|
||||||
import de.nowchess.io.pgn.{PgnExporter, PgnParser}
|
|
||||||
import de.nowchess.io.json.{JsonExporter, JsonParser}
|
|
||||||
import de.nowchess.io.{FileSystemGameService, GameContextExport, GameContextImport, GameFileService}
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import scalafx.stage.FileChooser
|
|
||||||
import scalafx.stage.FileChooser.ExtensionFilter
|
|
||||||
|
|
||||||
/** ScalaFX chess board view that displays the game state. Uses chess sprites and color palette. Handles user
|
|
||||||
* interactions (clicks) and sends moves to GameEngine.
|
|
||||||
*/
|
|
||||||
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(comicSansFontFamily, 16)
|
|
||||||
padding = Insets(10)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val currentBoard = new AtomicReference[Board](engine.board)
|
|
||||||
private val currentTurn = new AtomicReference[Color](engine.turn)
|
|
||||||
private val selectedSquare = new AtomicReference[Option[Square]](None)
|
|
||||||
private val squareViews = scala.collection.mutable.Map[(Int, Int), StackPane]()
|
|
||||||
|
|
||||||
private val undoButton: Button = new Button("Undo") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => if engine.canUndo then engine.undo()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #B9DAD1;"
|
|
||||||
disable = !engine.canUndo
|
|
||||||
}
|
|
||||||
private val redoButton: Button = new Button("Redo") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => if engine.canRedo then engine.redo()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #B9C2DA;"
|
|
||||||
disable = !engine.canRedo
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize UI
|
|
||||||
initializeBoard()
|
|
||||||
|
|
||||||
top = new VBox {
|
|
||||||
padding = Insets(10)
|
|
||||||
spacing = 5
|
|
||||||
alignment = Pos.Center
|
|
||||||
children = Seq(
|
|
||||||
new Label {
|
|
||||||
text = "Chess"
|
|
||||||
font = Font.font(comicSansFontFamily, 24)
|
|
||||||
style = "-fx-font-weight: bold;"
|
|
||||||
},
|
|
||||||
messageLabel,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
center = new VBox {
|
|
||||||
padding = Insets(20)
|
|
||||||
alignment = Pos.Center
|
|
||||||
style = s"-fx-background-color: ${PieceSprites.SquareColors.Border};"
|
|
||||||
children = boardGrid
|
|
||||||
}
|
|
||||||
|
|
||||||
bottom = new VBox {
|
|
||||||
padding = Insets(10)
|
|
||||||
spacing = 8
|
|
||||||
alignment = Pos.Center
|
|
||||||
children = Seq(
|
|
||||||
new HBox {
|
|
||||||
spacing = 10
|
|
||||||
alignment = Pos.Center
|
|
||||||
children = Seq(
|
|
||||||
undoButton,
|
|
||||||
redoButton,
|
|
||||||
new Button("Reset") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => engine.reset()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #E1EAA9;"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
new HBox {
|
|
||||||
spacing = 10
|
|
||||||
alignment = Pos.Center
|
|
||||||
children = Seq(
|
|
||||||
new Button("FEN Export") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doFenExport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #DAC4B9;"
|
|
||||||
},
|
|
||||||
new Button("FEN Import") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doFenImport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #DAD4B9;"
|
|
||||||
},
|
|
||||||
new Button("PGN Export") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doPgnExport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #C4DAB9;"
|
|
||||||
},
|
|
||||||
new Button("PGN Import") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doPgnImport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #B9DAC4;"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
new HBox {
|
|
||||||
spacing = 10
|
|
||||||
alignment = Pos.Center
|
|
||||||
children = Seq(
|
|
||||||
new Button("JSON Export") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doJsonExport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #B9C4DA;"
|
|
||||||
},
|
|
||||||
new Button("JSON Import") {
|
|
||||||
font = Font.font(comicSansFontFamily, 12)
|
|
||||||
onAction = _ => doJsonImport()
|
|
||||||
style = "-fx-background-radius: 8; -fx-background-color: #C4B9DA;"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def initializeBoard(): Unit =
|
|
||||||
boardGrid.padding = Insets(5)
|
|
||||||
boardGrid.hgap = 0
|
|
||||||
boardGrid.vgap = 0
|
|
||||||
|
|
||||||
// Create 8x8 board with rank/file labels
|
|
||||||
for
|
|
||||||
rank <- 0 until 8
|
|
||||||
file <- 0 until 8
|
|
||||||
do
|
|
||||||
val square = createSquare(rank, file)
|
|
||||||
squareViews((rank, file)) = square
|
|
||||||
boardGrid.add(square, file, 7 - rank) // Flip rank for proper display
|
|
||||||
|
|
||||||
updateBoard(currentBoard.get(), currentTurn.get())
|
|
||||||
|
|
||||||
private def createSquare(rank: Int, file: Int): StackPane =
|
|
||||||
val isWhite = (rank + file) % 2 == 0
|
|
||||||
val baseColor = if isWhite then PieceSprites.SquareColors.White else PieceSprites.SquareColors.Black
|
|
||||||
|
|
||||||
val bgRect = new Rectangle {
|
|
||||||
width = squareSize
|
|
||||||
height = squareSize
|
|
||||||
fill = FXColor.web(baseColor)
|
|
||||||
arcWidth = 8
|
|
||||||
arcHeight = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
val square = new StackPane {
|
|
||||||
children = Seq(bgRect)
|
|
||||||
onMouseClicked = _ => handleSquareClick(rank, file)
|
|
||||||
style = "-fx-cursor: hand;"
|
|
||||||
}
|
|
||||||
|
|
||||||
square
|
|
||||||
|
|
||||||
private def handleSquareClick(rank: Int, file: Int): Unit =
|
|
||||||
val clickedSquare = Square(File.values(file), Rank.values(rank))
|
|
||||||
|
|
||||||
selectedSquare.get() match
|
|
||||||
case None =>
|
|
||||||
// First click - select piece if it belongs to current player
|
|
||||||
currentBoard.get().pieceAt(clickedSquare).foreach { piece =>
|
|
||||||
if piece.color == currentTurn.get() then
|
|
||||||
selectedSquare.set(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 =>
|
|
||||||
highlightSquare(sq.rank.ordinal, sq.file.ordinal, PieceSprites.SquareColors.ValidMove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Some(fromSquare) =>
|
|
||||||
// Second click - attempt move
|
|
||||||
if clickedSquare == fromSquare then
|
|
||||||
// Deselect
|
|
||||||
selectedSquare.set(None)
|
|
||||||
updateBoard(currentBoard.get(), currentTurn.get())
|
|
||||||
else
|
|
||||||
val isPromo = engine.ruleSet
|
|
||||||
.legalMoves(engine.context)(fromSquare)
|
|
||||||
.exists(m =>
|
|
||||||
m.to == clickedSquare && (m.moveType match
|
|
||||||
case MoveType.Promotion(_) => true
|
|
||||||
case _ => false
|
|
||||||
),
|
|
||||||
)
|
|
||||||
if isPromo then showPromotionDialog(fromSquare, clickedSquare)
|
|
||||||
else engine.processUserInput(s"${fromSquare}$clickedSquare")
|
|
||||||
selectedSquare.set(None)
|
|
||||||
|
|
||||||
def updateBoard(board: Board, turn: Color): Unit =
|
|
||||||
currentBoard.set(board)
|
|
||||||
currentTurn.set(turn)
|
|
||||||
selectedSquare.set(None)
|
|
||||||
|
|
||||||
// Update all squares
|
|
||||||
for
|
|
||||||
rank <- 0 until 8
|
|
||||||
file <- 0 until 8
|
|
||||||
do
|
|
||||||
squareViews.get((rank, file)).foreach { stackPane =>
|
|
||||||
val isWhite = (rank + file) % 2 == 0
|
|
||||||
val baseColor = if isWhite then PieceSprites.SquareColors.White else PieceSprites.SquareColors.Black
|
|
||||||
|
|
||||||
val bgRect = new Rectangle {
|
|
||||||
width = squareSize
|
|
||||||
height = squareSize
|
|
||||||
fill = FXColor.web(baseColor)
|
|
||||||
arcWidth = 8
|
|
||||||
arcHeight = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
val square = Square(File.values(file), Rank.values(rank))
|
|
||||||
val pieceOption = board.pieceAt(square)
|
|
||||||
|
|
||||||
val children: Seq[scalafx.scene.Node] = pieceOption match
|
|
||||||
case Some(piece) =>
|
|
||||||
Seq(bgRect) ++ PieceSprites.loadPieceImage(piece, squareSize * 0.8).toSeq
|
|
||||||
case None =>
|
|
||||||
Seq(bgRect)
|
|
||||||
|
|
||||||
stackPane.children = children
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUndoRedoButtons()
|
|
||||||
|
|
||||||
def updateUndoRedoButtons(): Unit =
|
|
||||||
undoButton.disable = !engine.canUndo
|
|
||||||
redoButton.disable = !engine.canRedo
|
|
||||||
|
|
||||||
private def highlightSquare(rank: Int, file: Int, color: String): Unit =
|
|
||||||
squareViews.get((rank, file)).foreach { stackPane =>
|
|
||||||
val bgRect = new Rectangle {
|
|
||||||
width = squareSize
|
|
||||||
height = squareSize
|
|
||||||
fill = FXColor.web(color)
|
|
||||||
arcWidth = 8
|
|
||||||
arcHeight = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
val square = Square(File.values(file), Rank.values(rank))
|
|
||||||
val pieceOption = currentBoard.get().pieceAt(square)
|
|
||||||
|
|
||||||
stackPane.children = (pieceOption match
|
|
||||||
case Some(piece) =>
|
|
||||||
Seq(bgRect) ++ PieceSprites.loadPieceImage(piece, squareSize * 0.8).toSeq
|
|
||||||
case None =>
|
|
||||||
Seq(bgRect)
|
|
||||||
): Seq[scalafx.scene.Node]
|
|
||||||
}
|
|
||||||
|
|
||||||
def showMessage(msg: String): Unit =
|
|
||||||
messageLabel.text = msg
|
|
||||||
|
|
||||||
def showPromotionDialog(from: Square, to: Square): Unit =
|
|
||||||
val choices = Seq("Queen", "Rook", "Bishop", "Knight")
|
|
||||||
val dialog = new ChoiceDialog(defaultChoice = "Queen", choices = choices) {
|
|
||||||
initOwner(stage)
|
|
||||||
title = "Pawn Promotion"
|
|
||||||
headerText = "Choose promotion piece"
|
|
||||||
contentText = "Promote to:"
|
|
||||||
}
|
|
||||||
val uciSuffix = dialog.showAndWait() match
|
|
||||||
case Some("Rook") => "r"
|
|
||||||
case Some("Bishop") => "b"
|
|
||||||
case Some("Knight") => "n"
|
|
||||||
case _ => "q"
|
|
||||||
engine.processUserInput(s"${from}${to}$uciSuffix")
|
|
||||||
|
|
||||||
private def doFenExport(): Unit =
|
|
||||||
doExport(FenExporter, "FEN")
|
|
||||||
|
|
||||||
private def doFenImport(): Unit =
|
|
||||||
doImport(FenParser, "FEN")
|
|
||||||
|
|
||||||
private def doPgnExport(): Unit =
|
|
||||||
doExport(PgnExporter, "PGN")
|
|
||||||
|
|
||||||
private def doPgnImport(): Unit =
|
|
||||||
doImport(PgnParser, "PGN")
|
|
||||||
|
|
||||||
private def doJsonExport(): Unit =
|
|
||||||
val fileChooser = new FileChooser {
|
|
||||||
title = "Export Game as JSON"
|
|
||||||
initialFileName = "chess_game.json"
|
|
||||||
extensionFilters.add(new ExtensionFilter("JSON files (*.json)", "*.json"))
|
|
||||||
extensionFilters.add(new ExtensionFilter("All files", "*.*"))
|
|
||||||
}
|
|
||||||
|
|
||||||
Option(fileChooser.showSaveDialog(stage)).foreach { selectedFile =>
|
|
||||||
val result = FileSystemGameService.saveGameToFile(
|
|
||||||
engine.context,
|
|
||||||
selectedFile.toPath,
|
|
||||||
JsonExporter,
|
|
||||||
)
|
|
||||||
result match
|
|
||||||
case Right(_) => showMessage(s"✓ Game saved to: ${selectedFile.getName}")
|
|
||||||
case Left(err) => showMessage(s"⚠️ Error saving file: $err")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def doJsonImport(): Unit =
|
|
||||||
val fileChooser = new FileChooser {
|
|
||||||
title = "Import Game from JSON"
|
|
||||||
extensionFilters.add(new ExtensionFilter("JSON files (*.json)", "*.json"))
|
|
||||||
extensionFilters.add(new ExtensionFilter("All files", "*.*"))
|
|
||||||
}
|
|
||||||
|
|
||||||
Option(fileChooser.showOpenDialog(stage)).foreach { selectedFile =>
|
|
||||||
val result = FileSystemGameService.loadGameFromFile(
|
|
||||||
selectedFile.toPath,
|
|
||||||
JsonParser,
|
|
||||||
)
|
|
||||||
result match
|
|
||||||
case Right(gameContext) =>
|
|
||||||
engine.loadPosition(gameContext)
|
|
||||||
showMessage(s"✓ Game loaded from: ${selectedFile.getName}")
|
|
||||||
case Left(err) =>
|
|
||||||
showMessage(s"⚠️ Error: $err")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def doExport(exporter: GameContextExport, formatName: String): Unit = {
|
|
||||||
val exported = exporter.exportGameContext(engine.context)
|
|
||||||
showCopyDialog(s"$formatName Export", exported)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def doImport(importer: GameContextImport, formatName: String): Unit =
|
|
||||||
showInputDialog(s"$formatName Import", rows = 5).foreach { input =>
|
|
||||||
importer.importGameContext(input) match
|
|
||||||
case Right(gameContext) =>
|
|
||||||
engine.loadPosition(gameContext)
|
|
||||||
showMessage(s"✓ $formatName loaded successfully!")
|
|
||||||
case Left(err) =>
|
|
||||||
showMessage(s"⚠️ $formatName Error: $err")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def showCopyDialog(title: String, content: String): Unit =
|
|
||||||
val area = new javafx.scene.control.TextArea(content)
|
|
||||||
area.setEditable(false)
|
|
||||||
area.setWrapText(true)
|
|
||||||
area.setPrefRowCount(4)
|
|
||||||
val alert = new javafx.scene.control.Alert(javafx.scene.control.Alert.AlertType.INFORMATION)
|
|
||||||
alert.setTitle(title)
|
|
||||||
alert.setHeaderText("")
|
|
||||||
alert.getDialogPane.setContent(area)
|
|
||||||
alert.getDialogPane.setPrefWidth(500)
|
|
||||||
alert.initOwner(stage.delegate)
|
|
||||||
alert.showAndWait()
|
|
||||||
|
|
||||||
private def showInputDialog(title: String, rows: Int = 2): Option[String] =
|
|
||||||
val area = new javafx.scene.control.TextArea()
|
|
||||||
area.setWrapText(true)
|
|
||||||
area.setPrefRowCount(rows)
|
|
||||||
val dialog = new javafx.scene.control.Dialog[String]()
|
|
||||||
dialog.setTitle(title)
|
|
||||||
dialog.getDialogPane.setContent(area)
|
|
||||||
dialog.getDialogPane.getButtonTypes.addAll(
|
|
||||||
javafx.scene.control.ButtonType.OK,
|
|
||||||
javafx.scene.control.ButtonType.CANCEL,
|
|
||||||
)
|
|
||||||
dialog.setResultConverter { bt =>
|
|
||||||
if bt == javafx.scene.control.ButtonType.OK then area.getText else ""
|
|
||||||
}
|
|
||||||
dialog.initOwner(stage.delegate)
|
|
||||||
val result = dialog.showAndWait()
|
|
||||||
if result.isPresent && result.get.nonEmpty then Some(result.get) else None
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package de.nowchess.ui.gui
|
|
||||||
|
|
||||||
import javafx.application.{Application as JFXApplication, Platform as JFXPlatform}
|
|
||||||
import javafx.stage.Stage as JFXStage
|
|
||||||
import scalafx.application.Platform
|
|
||||||
import scalafx.scene.Scene
|
|
||||||
import scalafx.stage.Stage
|
|
||||||
import de.nowchess.chess.engine.GameEngine
|
|
||||||
|
|
||||||
/** ScalaFX GUI Application for Chess. This is launched from Main alongside the TUI. Both subscribe to the same
|
|
||||||
* GameEngine via Observer pattern.
|
|
||||||
*/
|
|
||||||
class ChessGUIApp extends JFXApplication:
|
|
||||||
|
|
||||||
override def start(primaryStage: JFXStage): Unit =
|
|
||||||
val engine = ChessGUILauncher.getEngine
|
|
||||||
val stage = new Stage(primaryStage)
|
|
||||||
|
|
||||||
stage.title = "Chess"
|
|
||||||
stage.width = 700
|
|
||||||
stage.height = 1000
|
|
||||||
stage.resizable = false
|
|
||||||
|
|
||||||
val boardView = new ChessBoardView(stage, engine)
|
|
||||||
val guiObserver = new GUIObserver(boardView)
|
|
||||||
|
|
||||||
// Subscribe GUI observer to engine
|
|
||||||
engine.subscribe(guiObserver)
|
|
||||||
|
|
||||||
stage.scene = new Scene {
|
|
||||||
root = boardView
|
|
||||||
// Load CSS if available
|
|
||||||
try
|
|
||||||
Option(getClass.getResource("/styles.css")).foreach(url => stylesheets.add(url.toExternalForm))
|
|
||||||
catch {
|
|
||||||
case _: Exception => // CSS is optional
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage.onCloseRequest = _ =>
|
|
||||||
// Unsubscribe when window closes
|
|
||||||
engine.unsubscribe(guiObserver)
|
|
||||||
|
|
||||||
stage.show()
|
|
||||||
|
|
||||||
/** Launcher object that holds the engine reference and launches GUI in separate thread. */
|
|
||||||
object ChessGUILauncher:
|
|
||||||
private val engineRef = new java.util.concurrent.atomic.AtomicReference[GameEngine]()
|
|
||||||
|
|
||||||
def getEngine: GameEngine = engineRef.get()
|
|
||||||
|
|
||||||
def launch(eng: GameEngine): Unit =
|
|
||||||
engineRef.set(eng)
|
|
||||||
val guiThread = new Thread(() => JFXApplication.launch(classOf[ChessGUIApp]))
|
|
||||||
guiThread.setDaemon(false)
|
|
||||||
guiThread.setName("ScalaFX-GUI-Thread")
|
|
||||||
guiThread.start()
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package de.nowchess.ui.gui
|
|
||||||
|
|
||||||
import scalafx.application.Platform
|
|
||||||
import scalafx.scene.control.Alert
|
|
||||||
import scalafx.scene.control.Alert.AlertType
|
|
||||||
import de.nowchess.chess.observer.{GameEvent, Observer, *}
|
|
||||||
import de.nowchess.api.board.Board
|
|
||||||
import de.nowchess.api.game.DrawReason
|
|
||||||
|
|
||||||
/** GUI Observer that implements the Observer pattern. Receives game events from GameEngine and updates the ScalaFX UI.
|
|
||||||
* All UI updates must be done on the JavaFX Application Thread.
|
|
||||||
*/
|
|
||||||
class GUIObserver(private val boardView: ChessBoardView) extends Observer:
|
|
||||||
|
|
||||||
override def onGameEvent(event: GameEvent): Unit =
|
|
||||||
// Ensure UI updates happen on JavaFX thread
|
|
||||||
Platform.runLater {
|
|
||||||
event match
|
|
||||||
case e: MoveExecutedEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
e.capturedPiece.foreach { piece =>
|
|
||||||
boardView.showMessage(s"Captured: $piece on ${e.toSquare}")
|
|
||||||
}
|
|
||||||
|
|
||||||
case e: CheckDetectedEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
boardView.showMessage(s"${e.context.turn.label} is in check!")
|
|
||||||
|
|
||||||
case e: CheckmateEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
showAlert(AlertType.Information, "Game Over", s"Checkmate! ${e.winner.label} wins.")
|
|
||||||
|
|
||||||
case e: DrawEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
val msg = e.reason match
|
|
||||||
case DrawReason.Stalemate => "Stalemate! The game is a draw."
|
|
||||||
case DrawReason.InsufficientMaterial => "Draw by insufficient material."
|
|
||||||
case DrawReason.FiftyMoveRule => "Draw claimed under the 50-move rule."
|
|
||||||
case DrawReason.ThreefoldRepetition => "Draw by threefold repetition."
|
|
||||||
case DrawReason.Agreement => "Draw by agreement."
|
|
||||||
showAlert(AlertType.Information, "Game Over", msg)
|
|
||||||
|
|
||||||
case e: InvalidMoveEvent =>
|
|
||||||
boardView.showMessage(s"⚠️ ${e.reason}")
|
|
||||||
|
|
||||||
case e: BoardResetEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
boardView.showMessage("Board has been reset to initial position.")
|
|
||||||
|
|
||||||
case e: FiftyMoveRuleAvailableEvent =>
|
|
||||||
boardView.showMessage("50-move rule is now available — type 'draw' to claim.")
|
|
||||||
|
|
||||||
case e: ThreefoldRepetitionAvailableEvent =>
|
|
||||||
boardView.showMessage("Threefold repetition is now available — type 'draw' to claim.")
|
|
||||||
|
|
||||||
case e: MoveUndoneEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
boardView.showMessage(s"↶ Undo: ${e.pgnNotation}")
|
|
||||||
boardView.updateUndoRedoButtons()
|
|
||||||
|
|
||||||
case e: MoveRedoneEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
if e.capturedPiece.isDefined then
|
|
||||||
boardView.showMessage(s"↷ Redo: ${e.pgnNotation} — Captured: ${e.capturedPiece.get}")
|
|
||||||
else boardView.showMessage(s"↷ Redo: ${e.pgnNotation}")
|
|
||||||
boardView.updateUndoRedoButtons()
|
|
||||||
|
|
||||||
case e: PgnLoadedEvent =>
|
|
||||||
boardView.updateBoard(e.context.board, e.context.turn)
|
|
||||||
boardView.showMessage("✓ PGN loaded successfully!")
|
|
||||||
boardView.updateUndoRedoButtons()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def showAlert(alertType: AlertType, titleText: String, content: String): Unit =
|
|
||||||
new Alert(alertType) {
|
|
||||||
initOwner(boardView.stage)
|
|
||||||
title = titleText
|
|
||||||
headerText = None
|
|
||||||
contentText = content
|
|
||||||
}.showAndWait()
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package de.nowchess.ui.gui
|
|
||||||
|
|
||||||
import scalafx.scene.image.{Image, ImageView}
|
|
||||||
import de.nowchess.api.board.{Color, Piece, PieceType}
|
|
||||||
|
|
||||||
/** Utility object for loading chess piece sprites. */
|
|
||||||
object PieceSprites:
|
|
||||||
|
|
||||||
private val spriteCache = scala.collection.mutable.Map[String, Option[Image]]()
|
|
||||||
|
|
||||||
/** Load a piece sprite image from resources. Sprites are cached for performance.
|
|
||||||
*/
|
|
||||||
def loadPieceImage(piece: Piece, size: Double = 60.0): Option[ImageView] =
|
|
||||||
val key = s"${piece.color.label.toLowerCase}_${piece.pieceType.label.toLowerCase}"
|
|
||||||
spriteCache.getOrElseUpdate(key, loadImage(key)).map { image =>
|
|
||||||
new ImageView(image) {
|
|
||||||
fitWidth = size
|
|
||||||
fitHeight = size
|
|
||||||
preserveRatio = true
|
|
||||||
smooth = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def loadImage(key: String): Option[Image] =
|
|
||||||
val path = s"/sprites/pieces/$key.png"
|
|
||||||
Option(getClass.getResourceAsStream(path)).map(new Image(_))
|
|
||||||
|
|
||||||
/** Get square colors for the board using theme. */
|
|
||||||
object SquareColors:
|
|
||||||
val White = "#F3C8A0" // Warm light beige
|
|
||||||
val Black = "#BA6D4B" // Warm terracotta
|
|
||||||
val Selected = "#C19EF5" // Purple highlight
|
|
||||||
val ValidMove = "#E1EAA9" // Light yellow-green
|
|
||||||
val Border = "#5A2C28" // Dark brown border
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package de.nowchess.ui.terminal
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import scala.io.StdIn
|
|
||||||
import de.nowchess.api.game.DrawReason
|
|
||||||
import de.nowchess.chess.engine.GameEngine
|
|
||||||
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. Handles all
|
|
||||||
* I/O and user interaction in the terminal.
|
|
||||||
*/
|
|
||||||
class TerminalUI(engine: GameEngine) extends Observer:
|
|
||||||
private val running = new AtomicBoolean(true)
|
|
||||||
|
|
||||||
/** Called by GameEngine whenever a game event occurs. */
|
|
||||||
override def onGameEvent(event: GameEvent): Unit =
|
|
||||||
event match
|
|
||||||
case e: MoveExecutedEvent =>
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
e.capturedPiece.foreach: cap =>
|
|
||||||
println(s"Captured: $cap on ${e.toSquare}")
|
|
||||||
printPrompt(e.context.turn)
|
|
||||||
|
|
||||||
case e: MoveUndoneEvent =>
|
|
||||||
println(s"Undo: ${e.pgnNotation}")
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
printPrompt(e.context.turn)
|
|
||||||
|
|
||||||
case e: MoveRedoneEvent =>
|
|
||||||
println(s"Redo: ${e.pgnNotation}")
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
printPrompt(e.context.turn)
|
|
||||||
|
|
||||||
case e: CheckDetectedEvent =>
|
|
||||||
println(s"${e.context.turn.label} is in check!")
|
|
||||||
|
|
||||||
case e: CheckmateEvent =>
|
|
||||||
println(s"Checkmate! ${e.winner.label} wins.")
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
|
|
||||||
case e: DrawEvent =>
|
|
||||||
val msg = e.reason match
|
|
||||||
case DrawReason.Stalemate => "Stalemate! The game is a draw."
|
|
||||||
case DrawReason.InsufficientMaterial => "Draw by insufficient material."
|
|
||||||
case DrawReason.FiftyMoveRule => "Draw claimed under the 50-move rule."
|
|
||||||
case DrawReason.ThreefoldRepetition => "Draw by threefold repetition."
|
|
||||||
case DrawReason.Agreement => "Draw by agreement."
|
|
||||||
println(msg)
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
|
|
||||||
case e: InvalidMoveEvent =>
|
|
||||||
println(s"⚠️ ${e.reason}")
|
|
||||||
|
|
||||||
case e: BoardResetEvent =>
|
|
||||||
println("Board has been reset to initial position.")
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
printPrompt(e.context.turn)
|
|
||||||
|
|
||||||
case _: FiftyMoveRuleAvailableEvent =>
|
|
||||||
println("50-move rule is now available — type 'draw' to claim.")
|
|
||||||
|
|
||||||
case _: ThreefoldRepetitionAvailableEvent =>
|
|
||||||
println("Threefold repetition is now available — type 'draw' to claim.")
|
|
||||||
|
|
||||||
case e: PgnLoadedEvent =>
|
|
||||||
println("PGN loaded successfully.")
|
|
||||||
println()
|
|
||||||
print(Renderer.render(e.context.board))
|
|
||||||
printPrompt(e.context.turn)
|
|
||||||
|
|
||||||
/** Start the terminal UI game loop. */
|
|
||||||
def start(): Unit =
|
|
||||||
// Register as observer
|
|
||||||
engine.subscribe(this)
|
|
||||||
|
|
||||||
// Show initial board
|
|
||||||
println()
|
|
||||||
print(Renderer.render(engine.board))
|
|
||||||
printPrompt(engine.turn)
|
|
||||||
|
|
||||||
while running.get() do
|
|
||||||
val input = Option(StdIn.readLine()).getOrElse("quit").trim
|
|
||||||
synchronized {
|
|
||||||
input.toLowerCase match
|
|
||||||
case "quit" | "q" =>
|
|
||||||
running.set(false)
|
|
||||||
println("Game over. Goodbye!")
|
|
||||||
case "" =>
|
|
||||||
printPrompt(engine.turn)
|
|
||||||
case _ =>
|
|
||||||
engine.processUserInput(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe when done
|
|
||||||
engine.unsubscribe(this)
|
|
||||||
|
|
||||||
private def printPrompt(turn: de.nowchess.api.board.Color): Unit =
|
|
||||||
val undoHint = if engine.canUndo then " [undo]" else ""
|
|
||||||
val redoHint = if engine.canRedo then " [redo]" else ""
|
|
||||||
print(s"${turn.label}'s turn. Enter move (or 'quit'/'q' to exit)$undoHint$redoHint: ")
|
|
||||||