Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52b171c7af | |||
| a386f57c21 | |||
| 3ca2afbb4b | |||
| b5a2966ada | |||
| 74a4fce0ca | |||
| 8fc97bde02 | |||
| 2d75b2e80e | |||
| f088c4e9ff | |||
| 8a1cf909d4 | |||
| 33e785d22a | |||
| d16cec176b | |||
| 8744bee2dd | |||
| 5f4d33f3ca | |||
| 767d3051a7 |
+193
-56
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
> **Stack:** raw-http | none | unknown | scala
|
> **Stack:** raw-http | none | unknown | scala
|
||||||
|
|
||||||
> 0 routes | 0 models | 0 components | 35 lib files | 0 env vars | 0 middleware
|
> 0 routes | 0 models | 0 components | 63 lib files | 1 env vars | 1 middleware
|
||||||
> **Token savings:** this file is ~3.700 tokens. Without it, AI exploration would cost ~18.200 tokens. **Saves ~14.500 tokens per conversation.**
|
> **Token savings:** this file is ~0 tokens. Without it, AI exploration would cost ~0 tokens. **Saves ~0 tokens per conversation.**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -47,19 +47,130 @@
|
|||||||
- 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
|
||||||
- function error
|
- function error
|
||||||
- function totalPages
|
- function totalPages
|
||||||
|
- `modules/bot/python/nnue.py`
|
||||||
|
- function get_weights_dir: ()
|
||||||
|
- function get_data_dir: ()
|
||||||
|
- function list_checkpoints: ()
|
||||||
|
- function migrate_legacy_data: ()
|
||||||
|
- function show_header: ()
|
||||||
|
- function show_checkpoints_table: ()
|
||||||
|
- _...10 more_
|
||||||
|
- `modules/bot/python/src/dataset.py`
|
||||||
|
- function get_datasets_dir: () -> Path
|
||||||
|
- function next_dataset_version: () -> int
|
||||||
|
- function list_datasets: () -> List[Tuple[int, Dict]]
|
||||||
|
- function load_dataset_metadata: (version) -> Optional[Dict]
|
||||||
|
- function save_dataset_metadata: (version, metadata) -> None
|
||||||
|
- function create_dataset: (version, labeled_jsonl_path, sources, stockfish_depth) -> Path
|
||||||
|
- _...4 more_
|
||||||
|
- `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/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`
|
||||||
|
- function download_and_extract_puzzle_db: (url, output_dir)
|
||||||
|
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
||||||
|
- function load_positions_from_file: (file_path) -> Set[str]
|
||||||
|
- function merge_positions: (tactical, other, output_file)
|
||||||
|
- function extract_tactical_only: (puzzle_csv, output_file, max_puzzles) -> int
|
||||||
|
- function interactive_merge_positions: (puzzle_csv, output_file, max_puzzles)
|
||||||
|
- `modules/bot/python/src/train.py`
|
||||||
|
- function fen_to_features: (fen)
|
||||||
|
- function find_next_version: (base_name)
|
||||||
|
- 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, 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, hidden_sizes)
|
||||||
|
- class NNUEDataset
|
||||||
|
- _...1 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
||||||
|
- class BotController
|
||||||
|
- function getBot
|
||||||
|
- function listBots
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`
|
||||||
|
- class BotMoveRepetition
|
||||||
|
- function blockedMoves
|
||||||
|
- function repeatedMove
|
||||||
|
- function filterAllowed
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/Config.scala` — class Config
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`
|
||||||
|
- class Evaluation
|
||||||
|
- class CHECKMATE_SCORE
|
||||||
|
- class DRAW_SCORE
|
||||||
|
- function evaluate
|
||||||
|
- function initAccumulator
|
||||||
|
- function copyAccumulator
|
||||||
|
- _...2 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`
|
||||||
|
- class EvaluationClassic
|
||||||
|
- function evaluate
|
||||||
|
- function countRay
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/EvaluationNNUE.scala` — class EvaluationNNUE, function evaluate
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`
|
||||||
|
- class NNUE
|
||||||
|
- function initAccumulator
|
||||||
|
- function pushAccumulator
|
||||||
|
- function copyAccumulator
|
||||||
|
- function recomputeAccumulator
|
||||||
|
- function validateAccumulator
|
||||||
|
- _...4 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiLoader.scala`
|
||||||
|
- class NbaiLoader
|
||||||
|
- function load
|
||||||
|
- function loadDefault
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiMigrator.scala` — class NbaiMigrator, function migrateFromBin
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiModel.scala`
|
||||||
|
- function toJson
|
||||||
|
- class NbaiMetadata
|
||||||
|
- function fromJson
|
||||||
|
- function str
|
||||||
|
- function num
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiWriter.scala` — class NbaiWriter, function write
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`
|
||||||
|
- function bestMove
|
||||||
|
- function bestMove
|
||||||
|
- function bestMoveWithTime
|
||||||
|
- function bestMoveWithTime
|
||||||
|
- function loop
|
||||||
|
- function loop
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
||||||
|
- class MoveOrdering
|
||||||
|
- class OrderingContext
|
||||||
|
- function addKillerMove
|
||||||
|
- function getKillerMoves
|
||||||
|
- function addHistory
|
||||||
|
- function getHistory
|
||||||
|
- _...3 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
||||||
|
- function advance
|
||||||
|
- function probe
|
||||||
|
- function store
|
||||||
|
- function clear
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — function probe, function select
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala` — class PolyglotHash, function hash
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`
|
||||||
|
- class ZobristHash
|
||||||
|
- function hash
|
||||||
|
- function nextHash
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`
|
||||||
- class Command
|
- class Command
|
||||||
- function execute
|
- function execute
|
||||||
@@ -74,15 +185,16 @@
|
|||||||
- 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
|
||||||
- _...10 more_
|
- function canRedo
|
||||||
|
- _...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
|
||||||
- class Observer
|
- class Observer
|
||||||
@@ -91,8 +203,35 @@
|
|||||||
- 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`
|
||||||
|
- class GameFileService
|
||||||
|
- function saveGameToFile
|
||||||
|
- function loadGameFromFile
|
||||||
|
- class FileSystemGameService
|
||||||
|
- function saveGameToFile
|
||||||
|
- function loadGameFromFile
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala`
|
||||||
- class FenExporter
|
- class FenExporter
|
||||||
- function boardToFen
|
- function boardToFen
|
||||||
@@ -114,6 +253,8 @@
|
|||||||
- function parseBoard
|
- function parseBoard
|
||||||
- function importGameContext
|
- function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — function buildSquares
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — function buildSquares
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala` — class JsonExporter, function exportGameContext
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala` — class JsonParser, function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala`
|
||||||
- class PgnExporter
|
- class PgnExporter
|
||||||
- function exportGameContext
|
- function exportGameContext
|
||||||
@@ -131,32 +272,28 @@
|
|||||||
- 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
|
# Config
|
||||||
- function updateUndoRedoButtons
|
|
||||||
- function showMessage
|
## Environment Variables
|
||||||
- function showPromotionDialog
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/ChessGUI.scala`
|
- `STOCKFISH_PATH` **required** — modules/bot/python/nnue.py
|
||||||
- class ChessGUIApp
|
|
||||||
- class ChessGUILauncher
|
---
|
||||||
- function getEngine
|
|
||||||
- function launch
|
# Middleware
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/GUIObserver.scala` — class GUIObserver
|
|
||||||
- `modules/ui/src/main/scala/de/nowchess/ui/gui/PieceSprites.scala`
|
## custom
|
||||||
- class PieceSprites
|
- generate — `modules/bot/python/src/generate.py`
|
||||||
- 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
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -164,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 **28** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **64** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **21** 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/Color.scala` — imported by **19** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **40** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **14** 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 **13** 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/Piece.scala` — imported by **10** 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/PieceType.scala` — imported by **9** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **17** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **9** 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 **8** 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/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **9** 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/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **7** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala` — imported by **6** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala` — imported by **6** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — imported by **5** files
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala` — imported by **2** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala` — imported by **2** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala` — imported by **2** files
|
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — imported by **2** files
|
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — imported by **1** files
|
|
||||||
|
|
||||||
## Import Map (who imports what)
|
## Import Map (who imports what)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`, `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`, `modules/core/src/test/scala/de/nowchess/chess/command/CommandInvokerBranchTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/command/CommandInvokerTest.scala` +23 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/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/core/src/main/scala/de/nowchess/chess/command/Command.scala` +16 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/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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala` +14 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/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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala` +9 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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineGameEndingTest.scala` +8 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/Piece.scala` ← `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`, `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineScenarioTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesStateTransitionsTest.scala` +5 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/PieceType.scala` ← `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesStateTransitionsTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesTest.scala` +4 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/io/src/main/scala/de/nowchess/io/fen/FenParser.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`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineScenarioTest.scala` +4 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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala` +3 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/GameContextImport.scala` ← `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala` +2 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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Config
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
- `STOCKFISH_PATH` **required** — modules/bot/python/nnue.py
|
||||||
+29
-29
@@ -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 **28** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` — imported by **64** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **21** 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/Color.scala` — imported by **19** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/Square.scala` — imported by **40** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/move/Move.scala` — imported by **14** 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 **13** 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/Piece.scala` — imported by **10** 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/PieceType.scala` — imported by **9** files
|
- `modules/api/src/main/scala/de/nowchess/api/board/PieceType.scala` — imported by **17** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala` — imported by **9** 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 **8** 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/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **9** 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/io/src/main/scala/de/nowchess/io/GameContextImport.scala` — imported by **7** files
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/board/CastlingRights.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/bot/Bot.scala` — imported by **6** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/GameContextExport.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala` — imported by **6** files
|
||||||
- `modules/rule/src/main/scala/de/nowchess/rules/RuleSet.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/player/PlayerInfo.scala` — imported by **5** files
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala` — imported by **4** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — imported by **5** files
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala` — imported by **4** files
|
- `modules/api/src/main/scala/de/nowchess/api/game/GameResult.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnParser.scala` — imported by **2** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/ClassicalBot.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala` — imported by **2** files
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala` — imported by **4** files
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala` — imported by **2** files
|
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — imported by **2** files
|
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/controller/Parser.scala` — imported by **1** files
|
|
||||||
|
|
||||||
## Import Map (who imports what)
|
## Import Map (who imports what)
|
||||||
|
|
||||||
- `modules/api/src/main/scala/de/nowchess/api/game/GameContext.scala` ← `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`, `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`, `modules/core/src/test/scala/de/nowchess/chess/command/CommandInvokerBranchTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/command/CommandInvokerTest.scala` +23 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/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/core/src/main/scala/de/nowchess/chess/command/Command.scala` +16 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/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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/main/scala/de/nowchess/chess/observer/Observer.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala` +14 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/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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala` +9 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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineGameEndingTest.scala` +8 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/Piece.scala` ← `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`, `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineScenarioTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesStateTransitionsTest.scala` +5 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/PieceType.scala` ← `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesStateTransitionsTest.scala`, `modules/rule/src/test/scala/de/nowchess/rule/DefaultRulesTest.scala` +4 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/io/src/main/scala/de/nowchess/io/fen/FenParser.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`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineScenarioTest.scala` +4 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/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/EngineTestHelpers.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEnginePromotionTest.scala`, `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala` +3 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/GameContextImport.scala` ← `modules/core/src/main/scala/de/nowchess/chess/engine/GameEngine.scala`, `modules/core/src/test/scala/de/nowchess/chess/engine/GameEngineIntegrationTest.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParser.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParserCombinators.scala`, `modules/io/src/main/scala/de/nowchess/io/fen/FenParserFastParse.scala` +2 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
|
||||||
|
|||||||
+147
-25
@@ -38,19 +38,130 @@
|
|||||||
- 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
|
||||||
- function error
|
- function error
|
||||||
- function totalPages
|
- function totalPages
|
||||||
|
- `modules/bot/python/nnue.py`
|
||||||
|
- function get_weights_dir: ()
|
||||||
|
- function get_data_dir: ()
|
||||||
|
- function list_checkpoints: ()
|
||||||
|
- function migrate_legacy_data: ()
|
||||||
|
- function show_header: ()
|
||||||
|
- function show_checkpoints_table: ()
|
||||||
|
- _...10 more_
|
||||||
|
- `modules/bot/python/src/dataset.py`
|
||||||
|
- function get_datasets_dir: () -> Path
|
||||||
|
- function next_dataset_version: () -> int
|
||||||
|
- function list_datasets: () -> List[Tuple[int, Dict]]
|
||||||
|
- function load_dataset_metadata: (version) -> Optional[Dict]
|
||||||
|
- function save_dataset_metadata: (version, metadata) -> None
|
||||||
|
- function create_dataset: (version, labeled_jsonl_path, sources, stockfish_depth) -> Path
|
||||||
|
- _...4 more_
|
||||||
|
- `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/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`
|
||||||
|
- function download_and_extract_puzzle_db: (url, output_dir)
|
||||||
|
- function extract_puzzle_positions: (puzzle_csv, max_puzzles) -> Set[str]
|
||||||
|
- function load_positions_from_file: (file_path) -> Set[str]
|
||||||
|
- function merge_positions: (tactical, other, output_file)
|
||||||
|
- function extract_tactical_only: (puzzle_csv, output_file, max_puzzles) -> int
|
||||||
|
- function interactive_merge_positions: (puzzle_csv, output_file, max_puzzles)
|
||||||
|
- `modules/bot/python/src/train.py`
|
||||||
|
- function fen_to_features: (fen)
|
||||||
|
- function find_next_version: (base_name)
|
||||||
|
- 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, 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, hidden_sizes)
|
||||||
|
- class NNUEDataset
|
||||||
|
- _...1 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotController.scala`
|
||||||
|
- class BotController
|
||||||
|
- function getBot
|
||||||
|
- function listBots
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/BotMoveRepetition.scala`
|
||||||
|
- class BotMoveRepetition
|
||||||
|
- function blockedMoves
|
||||||
|
- function repeatedMove
|
||||||
|
- function filterAllowed
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/Config.scala` — class Config
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/ai/Evaluation.scala`
|
||||||
|
- class Evaluation
|
||||||
|
- class CHECKMATE_SCORE
|
||||||
|
- class DRAW_SCORE
|
||||||
|
- function evaluate
|
||||||
|
- function initAccumulator
|
||||||
|
- function copyAccumulator
|
||||||
|
- _...2 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/classic/EvaluationClassic.scala`
|
||||||
|
- class EvaluationClassic
|
||||||
|
- function evaluate
|
||||||
|
- function countRay
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/EvaluationNNUE.scala` — class EvaluationNNUE, function evaluate
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NNUE.scala`
|
||||||
|
- class NNUE
|
||||||
|
- function initAccumulator
|
||||||
|
- function pushAccumulator
|
||||||
|
- function copyAccumulator
|
||||||
|
- function recomputeAccumulator
|
||||||
|
- function validateAccumulator
|
||||||
|
- _...4 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiLoader.scala`
|
||||||
|
- class NbaiLoader
|
||||||
|
- function load
|
||||||
|
- function loadDefault
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiMigrator.scala` — class NbaiMigrator, function migrateFromBin
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiModel.scala`
|
||||||
|
- function toJson
|
||||||
|
- class NbaiMetadata
|
||||||
|
- function fromJson
|
||||||
|
- function str
|
||||||
|
- function num
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/bots/nnue/NbaiWriter.scala` — class NbaiWriter, function write
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/AlphaBetaSearch.scala`
|
||||||
|
- function bestMove
|
||||||
|
- function bestMove
|
||||||
|
- function bestMoveWithTime
|
||||||
|
- function bestMoveWithTime
|
||||||
|
- function loop
|
||||||
|
- function loop
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/MoveOrdering.scala`
|
||||||
|
- class MoveOrdering
|
||||||
|
- class OrderingContext
|
||||||
|
- function addKillerMove
|
||||||
|
- function getKillerMoves
|
||||||
|
- function addHistory
|
||||||
|
- function getHistory
|
||||||
|
- _...3 more_
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/logic/TranspositionTable.scala`
|
||||||
|
- function advance
|
||||||
|
- function probe
|
||||||
|
- function store
|
||||||
|
- function clear
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotBook.scala` — function probe, function select
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/PolyglotHash.scala` — class PolyglotHash, function hash
|
||||||
|
- `modules/bot/src/main/scala/de/nowchess/bot/util/ZobristHash.scala`
|
||||||
|
- class ZobristHash
|
||||||
|
- function hash
|
||||||
|
- function nextHash
|
||||||
- `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`
|
- `modules/core/src/main/scala/de/nowchess/chess/command/Command.scala`
|
||||||
- class Command
|
- class Command
|
||||||
- function execute
|
- function execute
|
||||||
@@ -65,15 +176,16 @@
|
|||||||
- 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
|
||||||
- _...10 more_
|
- function canRedo
|
||||||
|
- _...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
|
||||||
- class Observer
|
- class Observer
|
||||||
@@ -82,8 +194,35 @@
|
|||||||
- 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`
|
||||||
|
- class GameFileService
|
||||||
|
- function saveGameToFile
|
||||||
|
- function loadGameFromFile
|
||||||
|
- class FileSystemGameService
|
||||||
|
- function saveGameToFile
|
||||||
|
- function loadGameFromFile
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenExporter.scala`
|
||||||
- class FenExporter
|
- class FenExporter
|
||||||
- function boardToFen
|
- function boardToFen
|
||||||
@@ -105,6 +244,8 @@
|
|||||||
- function parseBoard
|
- function parseBoard
|
||||||
- function importGameContext
|
- function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — function buildSquares
|
- `modules/io/src/main/scala/de/nowchess/io/fen/FenParserSupport.scala` — function buildSquares
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/json/JsonExporter.scala` — class JsonExporter, function exportGameContext
|
||||||
|
- `modules/io/src/main/scala/de/nowchess/io/json/JsonParser.scala` — class JsonParser, function importGameContext
|
||||||
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala`
|
- `modules/io/src/main/scala/de/nowchess/io/pgn/PgnExporter.scala`
|
||||||
- class PgnExporter
|
- class PgnExporter
|
||||||
- function exportGameContext
|
- function exportGameContext
|
||||||
@@ -122,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,4 @@
|
|||||||
|
# Middleware
|
||||||
|
|
||||||
|
## custom
|
||||||
|
- generate — `modules/bot/python/src/generate.py`
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
name: Build & Push Native Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-actor:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
allowed: ${{ steps.check.outputs.allowed }}
|
||||||
|
steps:
|
||||||
|
- id: check
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" || "${{ github.actor }}" == "TeamCity" ]]; then
|
||||||
|
echo "allowed=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "allowed=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build-and-push:
|
||||||
|
needs: check-actor
|
||||||
|
if: needs.check-actor.outputs.allowed == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
module:
|
||||||
|
- core
|
||||||
|
- io
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up GraalVM
|
||||||
|
uses: graalvm/setup-graalvm@v1
|
||||||
|
with:
|
||||||
|
java-version: '21'
|
||||||
|
distribution: 'graalvm-community'
|
||||||
|
native-image-job-reports: 'true'
|
||||||
|
|
||||||
|
- name: Cache Gradle packages
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: gradle-${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Build native binary
|
||||||
|
run: ./gradlew :modules:${{ matrix.module }}:build -Dquarkus.native.enabled=true --no-daemon
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ghcr.io/now-chess/now-chess-systems/${{ matrix.module }}
|
||||||
|
tags: |
|
||||||
|
type=sha,prefix=,format=short
|
||||||
|
type=raw,value=latest
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: modules/${{ matrix.module }}/src/main/docker/Dockerfile.native
|
||||||
|
push: true
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha,scope=${{ matrix.module }}
|
||||||
|
cache-to: type=gha,mode=max,scope=${{ matrix.module }}
|
||||||
Generated
+2
@@ -8,3 +8,5 @@
|
|||||||
/dataSources.local.xml
|
/dataSources.local.xml
|
||||||
# Editor-based HTTP Client requests
|
# Editor-based HTTP Client requests
|
||||||
/httpRequests/
|
/httpRequests/
|
||||||
|
|
||||||
|
sonarlint.xml
|
||||||
|
|||||||
Generated
+133
@@ -0,0 +1,133 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<AndroidXmlCodeStyleSettings>
|
||||||
|
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||||
|
</AndroidXmlCodeStyleSettings>
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<ScalaCodeStyleSettings>
|
||||||
|
<option name="FORMATTER" value="1" />
|
||||||
|
</ScalaCodeStyleSettings>
|
||||||
|
<XML>
|
||||||
|
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||||
|
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||||
|
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</XML>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
Generated
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<state>
|
<state>
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
</state>
|
</state>
|
||||||
</component>
|
</component>
|
||||||
Generated
+1
-2
@@ -11,11 +11,10 @@
|
|||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/modules" />
|
<option value="$PROJECT_DIR$/modules" />
|
||||||
<option value="$PROJECT_DIR$/modules/api" />
|
<option value="$PROJECT_DIR$/modules/api" />
|
||||||
<option value="$PROJECT_DIR$/modules/backcore" />
|
<option value="$PROJECT_DIR$/modules/bot" />
|
||||||
<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>
|
||||||
Generated
+1
-1
@@ -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.backcore.integrationTest,NowChessSystems.modules.backcore.main,NowChessSystems.modules.backcore.native-test,NowChessSystems.modules.backcore.quarkus-generated-sources,NowChessSystems.modules.backcore.quarkus-test-generated-sources,NowChessSystems.modules.backcore.scoverage,NowChessSystems.modules.backcore.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.integrationTest,NowChessSystems.modules.io.main,NowChessSystems.modules.io.native-test,NowChessSystems.modules.io.quarkus-generated-sources,NowChessSystems.modules.io.quarkus-test-generated-sources,NowChessSystems.modules.io.scoverage,NowChessSystems.modules.io.test,NowChessSystems.modules.rule.main,NowChessSystems.modules.rule.scoverage,NowChessSystems.modules.rule.test,NowChessSystems.modules.ui.main,NowChessSystems.modules.ui.scoverage,NowChessSystems.modules.ui.test">
|
||||||
<option name="deprecationWarnings" value="true" />
|
<option name="deprecationWarnings" value="true" />
|
||||||
<option name="uncheckedWarnings" value="true" />
|
<option name="uncheckedWarnings" value="true" />
|
||||||
<parameters>
|
<parameters>
|
||||||
|
|||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ScalaProjectSettings">
|
|
||||||
<option name="scala3DisclaimerShown" value="true" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -9,8 +9,9 @@ Scala 3.5.1 · Gradle 9
|
|||||||
./compile # Compile all modules — always run
|
./compile # Compile all modules — always run
|
||||||
./test # Run all tests
|
./test # Run all tests
|
||||||
./coverage # Check coverage
|
./coverage # Check coverage
|
||||||
|
./lint # Run linters
|
||||||
```
|
```
|
||||||
Try to stick to these commands for consistency.
|
Use consistently.
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
@@ -19,19 +20,20 @@ Try to stick to these commands for consistency.
|
|||||||
| `api` | Model / shared types | (none) |
|
| `api` | Model / shared types | (none) |
|
||||||
| `core` | Primary business logic | api, rule |
|
| `core` | Primary business logic | api, rule |
|
||||||
| `rule` | Game rules | api |
|
| `rule` | Game rules | api |
|
||||||
|
| `bot` | Bots and AI | api,rule,io |
|
||||||
| `io` | Export formats | api, core |
|
| `io` | Export formats | api, core |
|
||||||
| `ui` | Entrypoint & UI | core, io |
|
| `ui` | Entrypoint & UI | core, io |
|
||||||
|
|
||||||
## 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
|
||||||
|
|
||||||
@@ -39,20 +41,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)` (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 disabled (fixed-window futures removed pruning effectiveness). Sequential PV default. Correctness + pruning quality > speculative parallelism.
|
||||||
|
- **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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -60,11 +65,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)
|
||||||
@@ -72,8 +75,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
|
||||||
@@ -83,13 +85,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,100 @@
|
|||||||
|
# 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
|
||||||
|
./lint # Run linters
|
||||||
|
```
|
||||||
|
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,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,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
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "NowChess API",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
vars {
|
||||||
|
baseUrl: http://localhost:8080
|
||||||
|
ioBaseUrl: http://localhost:8081
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
meta {
|
||||||
|
name: Export FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/export/fen
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
},
|
||||||
|
"turn": "White",
|
||||||
|
"castlingRights": {
|
||||||
|
"whiteKingSide": true,
|
||||||
|
"whiteQueenSide": true,
|
||||||
|
"blackKingSide": true,
|
||||||
|
"blackQueenSide": true
|
||||||
|
},
|
||||||
|
"enPassantSquare": null,
|
||||||
|
"halfMoveClock": 0,
|
||||||
|
"moves": [],
|
||||||
|
"result": null,
|
||||||
|
"initialBoard": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
meta {
|
||||||
|
name: Export PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/export/pgn
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
},
|
||||||
|
"turn": "White",
|
||||||
|
"castlingRights": {
|
||||||
|
"whiteKingSide": true,
|
||||||
|
"whiteQueenSide": true,
|
||||||
|
"blackKingSide": true,
|
||||||
|
"blackQueenSide": true
|
||||||
|
},
|
||||||
|
"enPassantSquare": null,
|
||||||
|
"halfMoveClock": 0,
|
||||||
|
"moves": [],
|
||||||
|
"result": null,
|
||||||
|
"initialBoard": {
|
||||||
|
"a1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"b1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"c1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"d1": {"color": "White", "pieceType": "Queen"},
|
||||||
|
"e1": {"color": "White", "pieceType": "King"},
|
||||||
|
"f1": {"color": "White", "pieceType": "Bishop"},
|
||||||
|
"g1": {"color": "White", "pieceType": "Knight"},
|
||||||
|
"h1": {"color": "White", "pieceType": "Rook"},
|
||||||
|
"a2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"b2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"c2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"d2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"e2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"f2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"g2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"h2": {"color": "White", "pieceType": "Pawn"},
|
||||||
|
"a7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"b7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"c7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"d7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"e7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"f7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"g7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"h7": {"color": "Black", "pieceType": "Pawn"},
|
||||||
|
"a8": {"color": "Black", "pieceType": "Rook"},
|
||||||
|
"b8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"c8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"d8": {"color": "Black", "pieceType": "Queen"},
|
||||||
|
"e8": {"color": "Black", "pieceType": "King"},
|
||||||
|
"f8": {"color": "Black", "pieceType": "Bishop"},
|
||||||
|
"g8": {"color": "Black", "pieceType": "Knight"},
|
||||||
|
"h8": {"color": "Black", "pieceType": "Rook"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: export
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Import FEN
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/import/fen
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"fen": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
meta {
|
||||||
|
name: Import PGN
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
method: POST
|
||||||
|
url: {{ioBaseUrl}}/io/import/pgn
|
||||||
|
body: json
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
headers {
|
||||||
|
Content-Type: application/json
|
||||||
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"pgn": "1. e4 e5 2. Nf3 Nc6 *"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
meta {
|
||||||
|
name: import
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
+50
-9
@@ -8,6 +8,53 @@ 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",
|
||||||
|
// IoResource — same rationale as GameResource; @QuarkusTest not instrumented by Scoverage
|
||||||
|
"**/io/src/main/scala/de/nowchess/io/service/resource/IoResource.scala",
|
||||||
|
// JacksonConfig — Quarkus lifecycle hook, no testable logic beyond ObjectMapper registration
|
||||||
|
"**/io/src/main/scala/de/nowchess/io/service/config/JacksonConfig.scala",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Converts a Sonar-style glob to a scoverage regex (matched against full source path).
|
||||||
|
// 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")
|
||||||
@@ -21,28 +68,22 @@ sonar {
|
|||||||
if (report.exists()) report.absolutePath else null
|
if (report.exists()) report.absolutePath else null
|
||||||
}.joinToString(",")
|
}.joinToString(",")
|
||||||
|
|
||||||
val jacocoReports = subprojects.mapNotNull { subproject ->
|
|
||||||
val report = subproject.file("build/reports/jacoco/test/jacocoTestReport.xml")
|
|
||||||
if (report.exists()) report.absolutePath else null
|
|
||||||
}.joinToString(",")
|
|
||||||
|
|
||||||
property("sonar.scala.coverage.reportPaths", scoverageReports)
|
property("sonar.scala.coverage.reportPaths", scoverageReports)
|
||||||
if (jacocoReports.isNotEmpty()) {
|
property("sonar.coverage.exclusions", coverageExclusions.joinToString(","))
|
||||||
property("sonar.coverage.jacoco.xmlReportPaths", jacocoReports)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val versions = mapOf(
|
val versions = mapOf(
|
||||||
"QUARKUS_SCALA3" to "1.0.0",
|
"QUARKUS_SCALA3" to "1.0.0",
|
||||||
"SCALA3" to "3.5.1",
|
"SCALA3" to "3.5.1",
|
||||||
"SCALA_LIBRARY" to "2.13.18",
|
"SCALA_LIBRARY" to "2.13.16",
|
||||||
"SCALATEST" to "3.2.19",
|
"SCALATEST" to "3.2.19",
|
||||||
"SCALATEST_JUNIT" to "0.1.11",
|
"SCALATEST_JUNIT" to "0.1.11",
|
||||||
"SCOVERAGE" to "2.1.1",
|
"SCOVERAGE" to "2.1.1",
|
||||||
"SCALAFX" to "21.0.0-R32",
|
"SCALAFX" to "21.0.0-R32",
|
||||||
"JAVAFX" to "21.0.1",
|
"JAVAFX" to "21.0.1",
|
||||||
"JUNIT_BOM" to "5.13.4",
|
"JUNIT_BOM" to "5.13.4",
|
||||||
|
"ONNXRUNTIME" to "1.19.2",
|
||||||
"SCALA_PARSER_COMBINATORS" to "2.4.0",
|
"SCALA_PARSER_COMBINATORS" to "2.4.0",
|
||||||
"FASTPARSE" to "3.0.2",
|
"FASTPARSE" to "3.0.2",
|
||||||
"JACKSON" to "2.17.2",
|
"JACKSON" to "2.17.2",
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# Gradle properties
|
||||||
quarkusPluginId=io.quarkus
|
quarkusPluginId=io.quarkus
|
||||||
quarkusPluginVersion=3.32.4
|
quarkusPluginVersion=3.32.4
|
||||||
quarkusPlatformGroupId=io.quarkus.platform
|
quarkusPlatformGroupId=io.quarkus.platform
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import glob,re
|
import glob,re
|
||||||
mods=['api','core','io','rule','ui']
|
mods=['api','core','io','rule','ui', 'bot']
|
||||||
tot=0
|
tot=0
|
||||||
for m in mods:
|
for m in mods:
|
||||||
s=0
|
s=0
|
||||||
|
|||||||
@@ -34,3 +34,63 @@
|
|||||||
* 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-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
* NCS-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-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))
|
||||||
|
## (2026-04-16)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
## (2026-04-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
* NCS-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-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||||
|
## (2026-04-21)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||||
|
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||||
|
## (2026-04-21)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||||
|
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||||
|
## (2026-04-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* NCS-13 Implement Threefold Repetition ([#31](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/31)) ([767d305](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/767d3051a76c266050b6335774d66e2db2273c16))
|
||||||
|
* NCS-14 implemented insufficient moves rule ([#30](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/30)) ([b0399a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0399a4e489950083066c9538df9a84dcc7a4613))
|
||||||
|
* NCS-21 Write Scripts to automate certain tasks ([#15](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/15)) ([8051871](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/80518719d536a087d339fe02530825dc07f8b388))
|
||||||
|
* NCS-25 Add linters to keep quality up ([#27](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/27)) ([fd4e67d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fd4e67d4f782a7e955822d90cb909d0a81676fb2))
|
||||||
|
* NCS-37 Quarkus integration ([#35](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/35)) ([5ad5efb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5ad5efb41e9df9e3dccb48f96a69f06217ab98e1))
|
||||||
|
* NCS-41 Bot Platform ([#33](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/33)) ([dceab08](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/dceab0875e6d15f7d3958633cf5dd5b29a851b1d))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* IO microservice ([#38](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/38)) ([fb5c61d](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fb5c61de63292e5d70c06304cba2193686aa1607))
|
||||||
|
|||||||
@@ -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,11 @@
|
|||||||
|
package de.nowchess.api.bot
|
||||||
|
|
||||||
|
import de.nowchess.api.game.GameContext
|
||||||
|
import de.nowchess.api.move.Move
|
||||||
|
|
||||||
|
trait Bot {
|
||||||
|
|
||||||
|
def name: String
|
||||||
|
def nextMove(context: GameContext): Option[Move]
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,3 @@
|
|||||||
|
package de.nowchess.api.dto
|
||||||
|
|
||||||
|
case class ImportFenRequest(fen: String)
|
||||||
@@ -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
|
||||||
|
|
||||||
|
case class ImportPgnRequest(pgn: String)
|
||||||
@@ -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)
|
||||||
@@ -5,4 +5,5 @@ enum DrawReason:
|
|||||||
case Stalemate
|
case Stalemate
|
||||||
case InsufficientMaterial
|
case InsufficientMaterial
|
||||||
case FiftyMoveRule
|
case FiftyMoveRule
|
||||||
|
case ThreefoldRepetition
|
||||||
case Agreement
|
case Agreement
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.nowchess.api.game
|
package de.nowchess.api.game
|
||||||
|
|
||||||
import de.nowchess.api.board.{Board, CastlingRights, Color, Square}
|
import de.nowchess.api.board.{Board, CastlingRights, Color, PieceType, Square}
|
||||||
import de.nowchess.api.move.Move
|
import de.nowchess.api.move.Move
|
||||||
|
|
||||||
/** Immutable bundle of complete game state. All state changes produce new GameContext instances.
|
/** Immutable bundle of complete game state. All state changes produce new GameContext instances.
|
||||||
@@ -13,7 +13,15 @@ case class GameContext(
|
|||||||
halfMoveClock: Int,
|
halfMoveClock: Int,
|
||||||
moves: List[Move],
|
moves: List[Move],
|
||||||
result: Option[GameResult] = None,
|
result: Option[GameResult] = None,
|
||||||
|
initialBoard: Board = Board.initial,
|
||||||
):
|
):
|
||||||
|
private lazy val whiteKingSquare: Option[Square] =
|
||||||
|
board.pieces.find((_, p) => p.color == Color.White && p.pieceType == PieceType.King).map(_._1)
|
||||||
|
private lazy val blackKingSquare: Option[Square] =
|
||||||
|
board.pieces.find((_, p) => p.color == Color.Black && p.pieceType == PieceType.King).map(_._1)
|
||||||
|
def kingSquare(color: Color): Option[Square] =
|
||||||
|
if color == Color.White then whiteKingSquare else blackKingSquare
|
||||||
|
|
||||||
/** Create new context with updated board. */
|
/** Create new context with updated board. */
|
||||||
def withBoard(newBoard: Board): GameContext = copy(board = newBoard)
|
def withBoard(newBoard: Board): GameContext = copy(board = newBoard)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.nowchess.api.game
|
||||||
|
|
||||||
|
import de.nowchess.api.bot.Bot
|
||||||
|
import de.nowchess.api.player.PlayerInfo
|
||||||
|
|
||||||
|
sealed trait Participant
|
||||||
|
final case class Human(playerInfo: PlayerInfo) extends Participant
|
||||||
|
final case class BotParticipant(bot: Bot) extends Participant
|
||||||
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
package de.nowchess.io
|
package de.nowchess.api.io
|
||||||
|
|
||||||
import de.nowchess.api.game.GameContext
|
import de.nowchess.api.game.GameContext
|
||||||
|
|
||||||
trait GameContextExport:
|
trait GameContextExport:
|
||||||
|
|
||||||
def exportGameContext(context: GameContext): String
|
def exportGameContext(context: GameContext): String
|
||||||
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
package de.nowchess.io
|
package de.nowchess.api.io
|
||||||
|
|
||||||
import de.nowchess.api.game.GameContext
|
import de.nowchess.api.game.GameContext
|
||||||
|
|
||||||
trait GameContextImport:
|
trait GameContextImport:
|
||||||
|
|
||||||
def importGameContext(input: String): Either[String, GameContext]
|
def importGameContext(input: String): Either[String, GameContext]
|
||||||
@@ -71,3 +71,9 @@ class GameContextTest extends AnyFunSuite with Matchers:
|
|||||||
test("withResult clears result"):
|
test("withResult clears result"):
|
||||||
val ctx = GameContext.initial.withResult(Some(GameResult.Win(Color.Black)))
|
val ctx = GameContext.initial.withResult(Some(GameResult.Win(Color.Black)))
|
||||||
ctx.withResult(None).result shouldBe None
|
ctx.withResult(None).result shouldBe None
|
||||||
|
|
||||||
|
test("kingSquare returns white king position"):
|
||||||
|
GameContext.initial.kingSquare(Color.White) shouldBe Some(Square(File.E, Rank.R1))
|
||||||
|
|
||||||
|
test("kingSquare returns black king position"):
|
||||||
|
GameContext.initial.kingSquare(Color.Black) shouldBe Some(Square(File.E, Rank.R8))
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=0
|
MAJOR=0
|
||||||
MINOR=5
|
MINOR=11
|
||||||
PATCH=0
|
PATCH=0
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("scala")
|
|
||||||
id("org.scoverage") version "8.1"
|
|
||||||
id("io.quarkus")
|
|
||||||
id("jacoco")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val versions = rootProject.extra["VERSIONS"] as Map<String, String>
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
mavenLocal()
|
|
||||||
}
|
|
||||||
|
|
||||||
scala {
|
|
||||||
scalaVersion = versions["SCALA3"]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
scoverage {
|
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
|
||||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
|
||||||
}
|
|
||||||
|
|
||||||
val quarkusPlatformGroupId: String by project
|
|
||||||
val quarkusPlatformArtifactId: String by project
|
|
||||||
val quarkusPlatformVersion: String by project
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(":modules:api"))
|
|
||||||
implementation(project(":modules:core"))
|
|
||||||
implementation(project(":modules:io"))
|
|
||||||
implementation(project(":modules:rule"))
|
|
||||||
|
|
||||||
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
|
|
||||||
implementation("io.quarkus:quarkus-rest")
|
|
||||||
implementation("io.quarkus:quarkus-rest-jackson")
|
|
||||||
implementation("io.quarkus:quarkus-config-yaml")
|
|
||||||
implementation("io.quarkus:quarkus-arc")
|
|
||||||
|
|
||||||
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
|
||||||
|
|
||||||
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"]!!}")
|
|
||||||
testImplementation("io.quarkus:quarkus-junit5")
|
|
||||||
testImplementation("io.quarkus:quarkus-jacoco")
|
|
||||||
testImplementation("io.rest-assured:rest-assured")
|
|
||||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "de.nowchess"
|
|
||||||
version = "1.0-SNAPSHOT"
|
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
options.compilerArgs.add("-parameters")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<Jar>().configureEach {
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.test {
|
|
||||||
useJUnitPlatform {
|
|
||||||
includeEngines("scalatest", "junit-jupiter")
|
|
||||||
}
|
|
||||||
testLogging {
|
|
||||||
events("passed", "skipped", "failed")
|
|
||||||
}
|
|
||||||
finalizedBy(tasks.named("jacocoTestReport"))
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.jacocoTestReport {
|
|
||||||
dependsOn(tasks.test)
|
|
||||||
executionData.setFrom(layout.buildDirectory.file("jacoco-quarkus.exec"))
|
|
||||||
sourceDirectories.setFrom(files("src/main/scala"))
|
|
||||||
classDirectories.setFrom(files(layout.buildDirectory.dir("classes/scala/main")))
|
|
||||||
reports {
|
|
||||||
xml.required.set(true)
|
|
||||||
xml.outputLocation.set(
|
|
||||||
layout.buildDirectory.file("reports/jacoco/test/jacocoTestReport.xml")
|
|
||||||
)
|
|
||||||
html.required.set(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
quarkus:
|
|
||||||
http:
|
|
||||||
port: 8080
|
|
||||||
jacoco:
|
|
||||||
data-file: ${user.dir}/build/jacoco-quarkus.exec
|
|
||||||
report: false
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package de.nowchess.backcore.config
|
|
||||||
|
|
||||||
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(DefaultScalaModule)
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package de.nowchess.backcore.dto
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude.Include
|
|
||||||
|
|
||||||
case class PlayerInfoDto(id: String, displayName: String)
|
|
||||||
|
|
||||||
case class GameStateResponse(
|
|
||||||
fen: String,
|
|
||||||
pgn: String,
|
|
||||||
turn: String,
|
|
||||||
status: String,
|
|
||||||
@JsonInclude(Include.NON_ABSENT) winner: Option[String],
|
|
||||||
moves: List[String],
|
|
||||||
undoAvailable: Boolean,
|
|
||||||
redoAvailable: Boolean,
|
|
||||||
)
|
|
||||||
|
|
||||||
case class GameFullResponse(
|
|
||||||
gameId: String,
|
|
||||||
white: PlayerInfoDto,
|
|
||||||
black: PlayerInfoDto,
|
|
||||||
state: GameStateResponse,
|
|
||||||
)
|
|
||||||
|
|
||||||
case class OkResponse(ok: Boolean = true)
|
|
||||||
|
|
||||||
@JsonInclude(Include.NON_ABSENT)
|
|
||||||
case class ApiErrorResponse(
|
|
||||||
code: String,
|
|
||||||
message: String,
|
|
||||||
field: Option[String] = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Requests
|
|
||||||
case class CreateGameRequest(
|
|
||||||
white: Option[PlayerInfoDto] = None,
|
|
||||||
black: Option[PlayerInfoDto] = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
case class ImportFenRequest(
|
|
||||||
fen: String = "",
|
|
||||||
white: Option[PlayerInfoDto] = None,
|
|
||||||
black: Option[PlayerInfoDto] = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
case class ImportPgnRequest(pgn: String = "")
|
|
||||||
|
|
||||||
case class LegalMoveDto(
|
|
||||||
from: String,
|
|
||||||
to: String,
|
|
||||||
uci: String,
|
|
||||||
moveType: String,
|
|
||||||
@JsonInclude(Include.NON_ABSENT) promotion: Option[String] = None,
|
|
||||||
)
|
|
||||||
|
|
||||||
case class LegalMovesResponse(moves: List[LegalMoveDto])
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package de.nowchess.backcore.game
|
|
||||||
|
|
||||||
import java.security.SecureRandom
|
|
||||||
|
|
||||||
object GameId:
|
|
||||||
private val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
private val random = SecureRandom()
|
|
||||||
|
|
||||||
def generate(): String =
|
|
||||||
(1 to 8).map(_ => chars(random.nextInt(chars.length))).mkString
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package de.nowchess.backcore.game
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
|
||||||
import de.nowchess.api.board.Color
|
|
||||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
|
||||||
import de.nowchess.backcore.dto.*
|
|
||||||
import de.nowchess.io.fen.FenExporter
|
|
||||||
import de.nowchess.io.pgn.PgnExporter
|
|
||||||
import de.nowchess.rules.sets.DefaultRules
|
|
||||||
|
|
||||||
object GameMapper:
|
|
||||||
private val mapper = new ObjectMapper().registerModule(DefaultScalaModule)
|
|
||||||
|
|
||||||
def toGameFullJson(session: GameSession): String =
|
|
||||||
mapper.writeValueAsString(toGameFull(session))
|
|
||||||
|
|
||||||
def toGameFull(session: GameSession): GameFullResponse =
|
|
||||||
GameFullResponse(
|
|
||||||
gameId = session.gameId,
|
|
||||||
white = toPlayerInfo(session.white),
|
|
||||||
black = toPlayerInfo(session.black),
|
|
||||||
state = toGameState(session),
|
|
||||||
)
|
|
||||||
|
|
||||||
def toGameState(session: GameSession): GameStateResponse =
|
|
||||||
val (status, winner) = computeStatus(session)
|
|
||||||
GameStateResponse(
|
|
||||||
fen = FenExporter.exportGameContext(session.context),
|
|
||||||
pgn = buildPgn(session.context.moves),
|
|
||||||
turn = if session.context.turn == Color.White then "white" else "black",
|
|
||||||
status = status,
|
|
||||||
winner = winner,
|
|
||||||
moves = session.context.moves.map(moveToUci),
|
|
||||||
undoAvailable = session.invoker.canUndo,
|
|
||||||
redoAvailable = session.invoker.canRedo,
|
|
||||||
)
|
|
||||||
|
|
||||||
private def toPlayerInfo(p: de.nowchess.api.player.PlayerInfo): PlayerInfoDto =
|
|
||||||
PlayerInfoDto(id = p.id.value, displayName = p.displayName)
|
|
||||||
|
|
||||||
private def computeStatus(session: GameSession): (String, Option[String]) =
|
|
||||||
session.result match
|
|
||||||
case Some(GameResult.Checkmate(winner)) =>
|
|
||||||
val w = if winner == Color.White then "white" else "black"
|
|
||||||
("checkmate", Some(w))
|
|
||||||
case Some(GameResult.Stalemate) =>
|
|
||||||
("stalemate", None)
|
|
||||||
case Some(GameResult.Resign(winner)) =>
|
|
||||||
val w = if winner == Color.White then "white" else "black"
|
|
||||||
("resign", Some(w))
|
|
||||||
case Some(GameResult.AgreedDraw) | Some(GameResult.FiftyMoveDraw) =>
|
|
||||||
("draw", None)
|
|
||||||
case Some(GameResult.InsufficientMaterial) =>
|
|
||||||
("insufficientMaterial", None)
|
|
||||||
case None =>
|
|
||||||
computeLiveStatus(session)
|
|
||||||
|
|
||||||
private def computeLiveStatus(session: GameSession): (String, Option[String]) =
|
|
||||||
val ctx = session.context
|
|
||||||
if DefaultRules.isCheck(ctx) then ("check", None)
|
|
||||||
else if session.drawOfferedBy.isDefined then ("drawOffered", None)
|
|
||||||
else if DefaultRules.isFiftyMoveRule(ctx) then ("fiftyMoveAvailable", None)
|
|
||||||
else ("started", None)
|
|
||||||
|
|
||||||
def moveToUci(move: Move): String =
|
|
||||||
val base = s"${move.from}${move.to}"
|
|
||||||
move.moveType match
|
|
||||||
case MoveType.Promotion(piece) =>
|
|
||||||
val suffix = piece match
|
|
||||||
case PromotionPiece.Queen => "q"
|
|
||||||
case PromotionPiece.Rook => "r"
|
|
||||||
case PromotionPiece.Bishop => "b"
|
|
||||||
case PromotionPiece.Knight => "n"
|
|
||||||
base + suffix
|
|
||||||
case _ => base
|
|
||||||
|
|
||||||
private def buildPgn(moves: List[Move]): String =
|
|
||||||
// Use PgnExporter with no headers to get move-text only (SAN notation)
|
|
||||||
PgnExporter.exportGame(Map.empty, moves)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package de.nowchess.backcore.game
|
|
||||||
|
|
||||||
import de.nowchess.api.board.Color
|
|
||||||
|
|
||||||
sealed trait GameResult
|
|
||||||
object GameResult:
|
|
||||||
case class Checkmate(winner: Color) extends GameResult
|
|
||||||
case object Stalemate extends GameResult
|
|
||||||
case class Resign(winner: Color) extends GameResult
|
|
||||||
case object AgreedDraw extends GameResult
|
|
||||||
case object FiftyMoveDraw extends GameResult
|
|
||||||
case object InsufficientMaterial extends GameResult
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package de.nowchess.backcore.game
|
|
||||||
|
|
||||||
import de.nowchess.api.board.Color
|
|
||||||
import de.nowchess.api.game.GameContext
|
|
||||||
import de.nowchess.api.player.PlayerInfo
|
|
||||||
import de.nowchess.chess.command.CommandInvoker
|
|
||||||
|
|
||||||
case class GameSession(
|
|
||||||
gameId: String,
|
|
||||||
white: PlayerInfo,
|
|
||||||
black: PlayerInfo,
|
|
||||||
context: GameContext,
|
|
||||||
invoker: CommandInvoker,
|
|
||||||
drawOfferedBy: Option[Color] = None,
|
|
||||||
result: Option[GameResult] = None,
|
|
||||||
)
|
|
||||||
@@ -1,260 +0,0 @@
|
|||||||
package de.nowchess.backcore.game
|
|
||||||
|
|
||||||
import de.nowchess.api.board.{Color, Square}
|
|
||||||
import de.nowchess.api.game.GameContext
|
|
||||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
|
||||||
import de.nowchess.api.player.{PlayerId, PlayerInfo}
|
|
||||||
import de.nowchess.backcore.dto.{CreateGameRequest, ImportFenRequest, PlayerInfoDto}
|
|
||||||
import de.nowchess.chess.command.{CommandInvoker, MoveCommand, MoveResult}
|
|
||||||
import de.nowchess.io.fen.FenParser
|
|
||||||
import de.nowchess.io.pgn.PgnParser
|
|
||||||
import de.nowchess.rules.sets.DefaultRules
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
|
||||||
|
|
||||||
import scala.collection.mutable
|
|
||||||
|
|
||||||
@ApplicationScoped
|
|
||||||
class GameStore:
|
|
||||||
private val games: mutable.Map[String, GameSession] = mutable.Map.empty
|
|
||||||
|
|
||||||
// ─── Create / Get ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def create(req: CreateGameRequest): GameSession = synchronized:
|
|
||||||
val id = generateId()
|
|
||||||
val session = newSession(id, req.white, req.black, GameContext.initial)
|
|
||||||
games(id) = session
|
|
||||||
session
|
|
||||||
|
|
||||||
def get(id: String): Option[GameSession] = synchronized:
|
|
||||||
games.get(id)
|
|
||||||
|
|
||||||
// ─── Move-making ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def applyMove(id: String, uci: String): Either[String, GameSession] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
if session.result.isDefined then Left("Game is already over")
|
|
||||||
else
|
|
||||||
parseUci(uci) match
|
|
||||||
case None => Left(s"Invalid UCI notation: $uci")
|
|
||||||
case Some((from, to, promotion)) =>
|
|
||||||
val legalCandidates = DefaultRules.legalMoves(session.context)(from)
|
|
||||||
findMatchingMove(legalCandidates, to, promotion) match
|
|
||||||
case None => Left(s"$uci is not a legal move")
|
|
||||||
case Some(move) =>
|
|
||||||
val nextCtx = DefaultRules.applyMove(session.context)(move)
|
|
||||||
val prevCtx = session.context
|
|
||||||
val cmd = MoveCommand(
|
|
||||||
from = move.from,
|
|
||||||
to = move.to,
|
|
||||||
moveResult = Some(MoveResult.Successful(nextCtx, prevCtx.board.pieceAt(move.to))),
|
|
||||||
previousContext = Some(prevCtx),
|
|
||||||
)
|
|
||||||
session.invoker.execute(cmd)
|
|
||||||
val result = detectGameOver(nextCtx)
|
|
||||||
val updated = session.copy(context = nextCtx, result = result)
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
|
|
||||||
def legalMoves(id: String, square: Option[Square]): Either[String, List[Move]] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
val moves = square match
|
|
||||||
case Some(sq) => DefaultRules.legalMoves(session.context)(sq)
|
|
||||||
case None => DefaultRules.allLegalMoves(session.context)
|
|
||||||
Right(moves)
|
|
||||||
|
|
||||||
// ─── Undo / Redo ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def undo(id: String): Either[String, GameSession] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
if !session.invoker.canUndo then Left("No moves to undo")
|
|
||||||
else
|
|
||||||
val idx = session.invoker.getCurrentIndex
|
|
||||||
session.invoker.history(idx) match
|
|
||||||
case cmd: MoveCommand =>
|
|
||||||
cmd.previousContext match
|
|
||||||
case None => Left("Cannot undo: no previous context stored")
|
|
||||||
case Some(prevCtx) =>
|
|
||||||
session.invoker.undo()
|
|
||||||
val updated = session.copy(context = prevCtx, result = None, drawOfferedBy = None)
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
case _ => Left("Cannot undo this command type")
|
|
||||||
|
|
||||||
def redo(id: String): Either[String, GameSession] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
if !session.invoker.canRedo then Left("No moves to redo")
|
|
||||||
else
|
|
||||||
val idx = session.invoker.getCurrentIndex + 1
|
|
||||||
session.invoker.history(idx) match
|
|
||||||
case cmd: MoveCommand =>
|
|
||||||
cmd.moveResult match
|
|
||||||
case Some(MoveResult.Successful(nextCtx, _)) =>
|
|
||||||
session.invoker.redo()
|
|
||||||
val result = detectGameOver(nextCtx)
|
|
||||||
val updated = session.copy(context = nextCtx, result = result)
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
case _ => Left("Cannot redo: move result not available")
|
|
||||||
case _ => Left("Cannot redo this command type")
|
|
||||||
|
|
||||||
// ─── Resign ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def resign(id: String): Either[String, GameSession] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
if session.result.isDefined then Left("Game is already over")
|
|
||||||
else
|
|
||||||
val winner = session.context.turn.opposite
|
|
||||||
val updated = session.copy(result = Some(GameResult.Resign(winner)))
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
|
|
||||||
// ─── Draw actions ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def drawAction(id: String, action: String): Either[String, GameSession] = synchronized:
|
|
||||||
withSession(id): session =>
|
|
||||||
if session.result.isDefined then Left("Game is already over")
|
|
||||||
else
|
|
||||||
action match
|
|
||||||
case "offer" =>
|
|
||||||
val updated = session.copy(drawOfferedBy = Some(session.context.turn))
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
case "accept" =>
|
|
||||||
session.drawOfferedBy match
|
|
||||||
case None => Left("No draw offer to accept")
|
|
||||||
case Some(offerer) if offerer == session.context.turn =>
|
|
||||||
Left("Cannot accept your own draw offer")
|
|
||||||
case Some(_) =>
|
|
||||||
val updated = session.copy(result = Some(GameResult.AgreedDraw), drawOfferedBy = None)
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
case "decline" =>
|
|
||||||
session.drawOfferedBy match
|
|
||||||
case None => Left("No draw offer to decline")
|
|
||||||
case Some(_) =>
|
|
||||||
val updated = session.copy(drawOfferedBy = None)
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
case "claim" =>
|
|
||||||
if DefaultRules.isFiftyMoveRule(session.context) then
|
|
||||||
val updated = session.copy(result = Some(GameResult.FiftyMoveDraw))
|
|
||||||
games(id) = updated
|
|
||||||
Right(updated)
|
|
||||||
else Left("Fifty-move rule has not been triggered")
|
|
||||||
case other => Left(s"Unknown draw action: $other")
|
|
||||||
|
|
||||||
// ─── Import ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
def importFen(req: ImportFenRequest): Either[String, GameSession] = synchronized:
|
|
||||||
FenParser.parseFen(req.fen) match
|
|
||||||
case Left(err) => Left(err)
|
|
||||||
case Right(ctx) =>
|
|
||||||
val id = generateId()
|
|
||||||
val session = newSession(id, req.white, req.black, ctx)
|
|
||||||
games(id) = session
|
|
||||||
Right(session)
|
|
||||||
|
|
||||||
def importPgn(pgn: String, white: Option[PlayerInfoDto], black: Option[PlayerInfoDto]): Either[String, GameSession] =
|
|
||||||
synchronized:
|
|
||||||
PgnParser.validatePgn(pgn) match
|
|
||||||
case Left(err) => Left(err)
|
|
||||||
case Right(game) =>
|
|
||||||
val id = generateId()
|
|
||||||
val session = newSession(id, white, black, GameContext.initial)
|
|
||||||
replayIntoSession(session, game.moves, GameContext.initial) match
|
|
||||||
case Left(err) => Left(err)
|
|
||||||
case Right(s) =>
|
|
||||||
games(id) = s
|
|
||||||
Right(s)
|
|
||||||
|
|
||||||
// ─── Private helpers ─────────────────────────────────────────────
|
|
||||||
|
|
||||||
private def withSession[A](id: String)(f: GameSession => Either[String, A]): Either[String, A] =
|
|
||||||
games.get(id) match
|
|
||||||
case None => Left(s"Game $id not found")
|
|
||||||
case Some(session) => f(session)
|
|
||||||
|
|
||||||
private def generateId(): String =
|
|
||||||
var id = GameId.generate()
|
|
||||||
while games.contains(id) do id = GameId.generate()
|
|
||||||
id
|
|
||||||
|
|
||||||
private def newSession(
|
|
||||||
id: String,
|
|
||||||
white: Option[PlayerInfoDto],
|
|
||||||
black: Option[PlayerInfoDto],
|
|
||||||
ctx: GameContext,
|
|
||||||
): GameSession =
|
|
||||||
GameSession(
|
|
||||||
gameId = id,
|
|
||||||
white = toPlayerInfo(white, "white", "White"),
|
|
||||||
black = toPlayerInfo(black, "black", "Black"),
|
|
||||||
context = ctx,
|
|
||||||
invoker = new CommandInvoker(),
|
|
||||||
)
|
|
||||||
|
|
||||||
private def toPlayerInfo(dto: Option[PlayerInfoDto], defaultId: String, defaultName: String): PlayerInfo =
|
|
||||||
dto.fold(PlayerInfo(PlayerId(defaultId), defaultName))(d => PlayerInfo(PlayerId(d.id), d.displayName))
|
|
||||||
|
|
||||||
private def parseUci(uci: String): Option[(Square, Square, Option[PromotionPiece])] =
|
|
||||||
if uci.length < 4 || uci.length > 5 then None
|
|
||||||
else
|
|
||||||
for
|
|
||||||
from <- Square.fromAlgebraic(uci.substring(0, 2))
|
|
||||||
to <- Square.fromAlgebraic(uci.substring(2, 4))
|
|
||||||
yield
|
|
||||||
val promotion = if uci.length == 5 then parsePromotionChar(uci.charAt(4)) else None
|
|
||||||
(from, to, promotion)
|
|
||||||
|
|
||||||
private def parsePromotionChar(c: Char): Option[PromotionPiece] =
|
|
||||||
c match
|
|
||||||
case 'q' => Some(PromotionPiece.Queen)
|
|
||||||
case 'r' => Some(PromotionPiece.Rook)
|
|
||||||
case 'b' => Some(PromotionPiece.Bishop)
|
|
||||||
case 'n' => Some(PromotionPiece.Knight)
|
|
||||||
case _ => None
|
|
||||||
|
|
||||||
private def findMatchingMove(
|
|
||||||
candidates: List[Move],
|
|
||||||
to: Square,
|
|
||||||
promotion: Option[PromotionPiece],
|
|
||||||
): Option[Move] =
|
|
||||||
candidates.filter(_.to == to) match
|
|
||||||
case Nil => None
|
|
||||||
case moves =>
|
|
||||||
promotion match
|
|
||||||
case Some(pp) => moves.find(_.moveType == MoveType.Promotion(pp))
|
|
||||||
case None =>
|
|
||||||
moves
|
|
||||||
.find(m => !m.moveType.isInstanceOf[MoveType.Promotion])
|
|
||||||
.orElse(moves.headOption)
|
|
||||||
|
|
||||||
private def detectGameOver(ctx: GameContext): Option[GameResult] =
|
|
||||||
if DefaultRules.isCheckmate(ctx) then Some(GameResult.Checkmate(ctx.turn.opposite))
|
|
||||||
else if DefaultRules.isStalemate(ctx) then Some(GameResult.Stalemate)
|
|
||||||
else if DefaultRules.isInsufficientMaterial(ctx) then Some(GameResult.InsufficientMaterial)
|
|
||||||
else None
|
|
||||||
|
|
||||||
private def replayIntoSession(
|
|
||||||
session: GameSession,
|
|
||||||
moves: List[Move],
|
|
||||||
startCtx: GameContext,
|
|
||||||
): Either[String, GameSession] =
|
|
||||||
moves.foldLeft[Either[String, GameSession]](Right(session)):
|
|
||||||
case (Left(err), _) => Left(err)
|
|
||||||
case (Right(s), move) =>
|
|
||||||
val legal = DefaultRules.legalMoves(s.context)(move.from)
|
|
||||||
legal
|
|
||||||
.find(m => m.from == move.from && m.to == move.to && m.moveType == move.moveType)
|
|
||||||
.orElse(legal.find(m => m.from == move.from && m.to == move.to)) match
|
|
||||||
case None => Left(s"Illegal move in PGN: $move")
|
|
||||||
case Some(legalMove) =>
|
|
||||||
val nextCtx = DefaultRules.applyMove(s.context)(legalMove)
|
|
||||||
val cmd = MoveCommand(
|
|
||||||
from = legalMove.from,
|
|
||||||
to = legalMove.to,
|
|
||||||
moveResult = Some(MoveResult.Successful(nextCtx, s.context.board.pieceAt(legalMove.to))),
|
|
||||||
previousContext = Some(s.context),
|
|
||||||
)
|
|
||||||
s.invoker.execute(cmd)
|
|
||||||
Right(s.copy(context = nextCtx))
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import de.nowchess.backcore.dto.*
|
|
||||||
import de.nowchess.backcore.game.{GameMapper, GameStore}
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
|
||||||
import jakarta.inject.Inject
|
|
||||||
import jakarta.ws.rs.*
|
|
||||||
import jakarta.ws.rs.core.{MediaType, Response}
|
|
||||||
|
|
||||||
@Path("/api/board/game")
|
|
||||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
|
||||||
@ApplicationScoped
|
|
||||||
class GameResource @Inject() (store: GameStore):
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
|
||||||
def createGame(req: CreateGameRequest): Response =
|
|
||||||
val session = store.create(Option(req).getOrElse(CreateGameRequest()))
|
|
||||||
Response.status(201).entity(GameMapper.toGameFull(session)).build()
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{gameId}")
|
|
||||||
def getGame(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.get(gameId) match
|
|
||||||
case Some(session) => Response.ok(GameMapper.toGameFull(session)).build()
|
|
||||||
case None =>
|
|
||||||
Response
|
|
||||||
.status(404)
|
|
||||||
.entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found"))
|
|
||||||
.build()
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{gameId}/stream")
|
|
||||||
@Produces(Array("application/x-ndjson"))
|
|
||||||
def streamGame(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.get(gameId) match
|
|
||||||
case None =>
|
|
||||||
Response
|
|
||||||
.status(404)
|
|
||||||
.`type`(MediaType.APPLICATION_JSON)
|
|
||||||
.entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found"))
|
|
||||||
.build()
|
|
||||||
case Some(session) =>
|
|
||||||
// Simplified: return a single-line NDJSON snapshot of the current game state
|
|
||||||
val event = s"""{"type":"gameFull","game":${GameMapper.toGameFullJson(session)}}"""
|
|
||||||
Response.ok(event + "\n").build()
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/{gameId}/resign")
|
|
||||||
def resignGame(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.resign(gameId) match
|
|
||||||
case Right(_) => Response.ok(OkResponse()).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("RESIGN_ERROR", err)).build()
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/{gameId}/draw/{action}")
|
|
||||||
def drawAction(
|
|
||||||
@PathParam("gameId") gameId: String,
|
|
||||||
@PathParam("action") action: String,
|
|
||||||
): Response =
|
|
||||||
store.drawAction(gameId, action) match
|
|
||||||
case Right(_) => Response.ok(OkResponse()).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("DRAW_ERROR", err)).build()
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{gameId}/export/fen")
|
|
||||||
@Produces(Array(MediaType.TEXT_PLAIN))
|
|
||||||
def exportFen(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.get(gameId) match
|
|
||||||
case None =>
|
|
||||||
Response
|
|
||||||
.status(404)
|
|
||||||
.`type`(MediaType.APPLICATION_JSON)
|
|
||||||
.entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found"))
|
|
||||||
.build()
|
|
||||||
case Some(session) =>
|
|
||||||
import de.nowchess.io.fen.FenExporter
|
|
||||||
Response.ok(FenExporter.exportGameContext(session.context)).build()
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{gameId}/export/pgn")
|
|
||||||
@Produces(Array("application/x-chess-pgn"))
|
|
||||||
def exportPgn(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.get(gameId) match
|
|
||||||
case None =>
|
|
||||||
Response
|
|
||||||
.status(404)
|
|
||||||
.`type`(MediaType.APPLICATION_JSON)
|
|
||||||
.entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found"))
|
|
||||||
.build()
|
|
||||||
case Some(session) =>
|
|
||||||
import de.nowchess.io.pgn.PgnExporter
|
|
||||||
Response.ok(PgnExporter.exportGameContext(session.context)).build()
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import de.nowchess.backcore.dto.{ApiErrorResponse, ImportFenRequest, ImportPgnRequest}
|
|
||||||
import de.nowchess.backcore.game.{GameMapper, GameStore}
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
|
||||||
import jakarta.inject.Inject
|
|
||||||
import jakarta.ws.rs.*
|
|
||||||
import jakarta.ws.rs.core.{MediaType, Response}
|
|
||||||
|
|
||||||
@Path("/api/board/game/import")
|
|
||||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
|
||||||
@Consumes(Array(MediaType.APPLICATION_JSON))
|
|
||||||
@ApplicationScoped
|
|
||||||
class ImportResource @Inject() (store: GameStore):
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/fen")
|
|
||||||
def importFen(req: ImportFenRequest): Response =
|
|
||||||
store.importFen(Option(req).getOrElse(ImportFenRequest())) match
|
|
||||||
case Right(session) => Response.status(201).entity(GameMapper.toGameFull(session)).build()
|
|
||||||
case Left(err) => Response.status(400).entity(ApiErrorResponse("INVALID_FEN", err)).build()
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/pgn")
|
|
||||||
def importPgn(req: ImportPgnRequest): Response =
|
|
||||||
val body = Option(req).getOrElse(ImportPgnRequest())
|
|
||||||
store.importPgn(body.pgn, None, None) match
|
|
||||||
case Right(session) => Response.status(201).entity(GameMapper.toGameFull(session)).build()
|
|
||||||
case Left(err) => Response.status(400).entity(ApiErrorResponse("INVALID_PGN", err)).build()
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import de.nowchess.api.board.Square
|
|
||||||
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
|
|
||||||
import de.nowchess.backcore.dto.*
|
|
||||||
import de.nowchess.backcore.game.{GameMapper, GameStore}
|
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
|
||||||
import jakarta.inject.Inject
|
|
||||||
import jakarta.ws.rs.*
|
|
||||||
import jakarta.ws.rs.core.{MediaType, Response}
|
|
||||||
|
|
||||||
@Path("/api/board/game")
|
|
||||||
@Produces(Array(MediaType.APPLICATION_JSON))
|
|
||||||
@ApplicationScoped
|
|
||||||
class MoveResource @Inject() (store: GameStore):
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/{gameId}/move/{uci}")
|
|
||||||
def makeMove(
|
|
||||||
@PathParam("gameId") gameId: String,
|
|
||||||
@PathParam("uci") uci: String,
|
|
||||||
): Response =
|
|
||||||
store.applyMove(gameId, uci) match
|
|
||||||
case Right(session) => Response.ok(GameMapper.toGameState(session)).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("INVALID_MOVE", err)).build()
|
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/{gameId}/moves")
|
|
||||||
def getLegalMoves(
|
|
||||||
@PathParam("gameId") gameId: String,
|
|
||||||
@QueryParam("square") squareParam: String,
|
|
||||||
): Response =
|
|
||||||
val square = Option(squareParam).flatMap(Square.fromAlgebraic)
|
|
||||||
store.legalMoves(gameId, square) match
|
|
||||||
case Right(moves) =>
|
|
||||||
val dtos = moves.map(toLegalMoveDto)
|
|
||||||
Response.ok(LegalMovesResponse(dtos)).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("ERROR", err)).build()
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/{gameId}/undo")
|
|
||||||
def undoMove(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.undo(gameId) match
|
|
||||||
case Right(session) => Response.ok(GameMapper.toGameState(session)).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("UNDO_NOT_AVAILABLE", err)).build()
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("/{gameId}/redo")
|
|
||||||
def redoMove(@PathParam("gameId") gameId: String): Response =
|
|
||||||
store.redo(gameId) match
|
|
||||||
case Right(session) => Response.ok(GameMapper.toGameState(session)).build()
|
|
||||||
case Left(err) if err.contains("not found") =>
|
|
||||||
Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", err)).build()
|
|
||||||
case Left(err) =>
|
|
||||||
Response.status(400).entity(ApiErrorResponse("REDO_NOT_AVAILABLE", err)).build()
|
|
||||||
|
|
||||||
private def toLegalMoveDto(move: Move): LegalMoveDto =
|
|
||||||
val uci = GameMapper.moveToUci(move)
|
|
||||||
val (moveType, promotion) = move.moveType match
|
|
||||||
case MoveType.Normal(true) => ("capture", None)
|
|
||||||
case MoveType.Normal(false) => ("normal", None)
|
|
||||||
case MoveType.CastleKingside => ("castleKingside", None)
|
|
||||||
case MoveType.CastleQueenside => ("castleQueenside", None)
|
|
||||||
case MoveType.EnPassant => ("enPassant", None)
|
|
||||||
case MoveType.Promotion(pp) =>
|
|
||||||
val pName = pp match
|
|
||||||
case PromotionPiece.Queen => "queen"
|
|
||||||
case PromotionPiece.Rook => "rook"
|
|
||||||
case PromotionPiece.Bishop => "bishop"
|
|
||||||
case PromotionPiece.Knight => "knight"
|
|
||||||
("promotion", Some(pName))
|
|
||||||
LegalMoveDto(
|
|
||||||
from = move.from.toString,
|
|
||||||
to = move.to.toString,
|
|
||||||
uci = uci,
|
|
||||||
moveType = moveType,
|
|
||||||
promotion = promotion,
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package de.nowchess.backcore
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class BackcoreStartupTest:
|
|
||||||
@Test
|
|
||||||
def applicationStarts(): Unit =
|
|
||||||
// If we get here the Quarkus container started successfully
|
|
||||||
()
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import io.restassured.RestAssured
|
|
||||||
import org.hamcrest.Matchers.{equalTo, matchesPattern, notNullValue}
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class GameResourceTest:
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def createGameReturns201WithGameId(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("gameId", matchesPattern("[A-Za-z0-9]{8}"))
|
|
||||||
.body("state.fen", notNullValue())
|
|
||||||
.body("state.turn", equalTo("white"))
|
|
||||||
.body("state.status", equalTo("started"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def createGameWithPlayersReturns201(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("""{"white":{"id":"p1","displayName":"Alice"},"black":{"id":"p2","displayName":"Bob"}}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("white.id", equalTo("p1"))
|
|
||||||
.body("black.displayName", equalTo("Bob"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def getGameReturns200ForExistingGame(): Unit =
|
|
||||||
val gameId = RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("gameId", equalTo(gameId))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def getGameReturns404ForUnknownId(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get("/api/board/game/XXXXXXXX")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import io.restassured.RestAssured
|
|
||||||
import org.hamcrest.Matchers.{containsString, equalTo, matchesPattern, notNullValue}
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class ImportExportTest:
|
|
||||||
|
|
||||||
private val startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
||||||
|
|
||||||
// ─── Import FEN ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def importFenReturns201WithCorrectPosition(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body(s"""{"fen":"$startFen"}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/import/fen")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("gameId", matchesPattern("[A-Za-z0-9]{8}"))
|
|
||||||
.body("state.fen", equalTo(startFen))
|
|
||||||
.body("state.turn", equalTo("white"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def importFenWithCustomPositionWorks(): Unit =
|
|
||||||
val fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body(s"""{"fen":"$fen"}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/import/fen")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("state.fen", equalTo(fen))
|
|
||||||
.body("state.turn", equalTo("black"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def importFenWithInvalidFenReturns400(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("""{"fen":"not-a-fen"}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/import/fen")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
|
|
||||||
// ─── Import PGN ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def importPgnReturns201(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("""{"pgn":"1. e4 e5 2. Nf3 Nc6 *"}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/import/pgn")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.body("gameId", matchesPattern("[A-Za-z0-9]{8}"))
|
|
||||||
.body("state.turn", equalTo("white"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def importPgnWithInvalidPgnReturns400(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("""{"pgn":"1. z9 *"}""")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/import/pgn")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
|
|
||||||
// ─── Export FEN ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def exportFenReturnsStartingFen(): Unit =
|
|
||||||
val gameId = RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId/export/fen")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body(equalTo(startFen))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def exportFenOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get("/api/board/game/XXXXXXXX/export/fen")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
|
|
||||||
// ─── Export PGN ────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def exportPgnReturnsText(): Unit =
|
|
||||||
val gameId = RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId/export/pgn")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body(containsString("e4"))
|
|
||||||
|
|
||||||
// ─── Stream ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def streamReturnsNdjsonSnapshot(): Unit =
|
|
||||||
val gameId = RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
val body = RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId/stream")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.contentType("application/x-ndjson")
|
|
||||||
.extract()
|
|
||||||
.body()
|
|
||||||
.asString()
|
|
||||||
|
|
||||||
assert(body.trim.startsWith("""{"type":"gameFull""""), s"Expected gameFull event, got: $body")
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def streamOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get("/api/board/game/XXXXXXXX/stream")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import io.restassured.RestAssured
|
|
||||||
import org.hamcrest.Matchers.{containsString, empty, equalTo, hasItem, hasItems, not, notNullValue}
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class MoveResourceTest:
|
|
||||||
|
|
||||||
private def createGame(): String =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def makeMoveReturns200WithUpdatedFen(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("fen", containsString("4P3")) // e4 pawn present in FEN
|
|
||||||
.body("turn", equalTo("black"))
|
|
||||||
.body("moves", hasItem("e2e4"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def makeMoveOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/XXXXXXXX/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def illegalMoveReturns400(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e5") // illegal — pawns can't jump 3 squares
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def getLegalMovesReturnsNonEmptyList(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId/moves")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("moves", not(empty()))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def getLegalMovesFilteredBySquareReturnsCorrectMoves(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId/moves?square=e2")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("moves.uci", hasItems("e2e3", "e2e4"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def getLegalMovesOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get("/api/board/game/XXXXXXXX/moves")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import io.restassured.RestAssured
|
|
||||||
import org.hamcrest.Matchers.{equalTo, notNullValue}
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class ResignDrawTest:
|
|
||||||
|
|
||||||
private def createGame(): String =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
// ─── Resign ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def resignReturns200(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/resign")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("ok", equalTo(true))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def afterResignGameShowsResignStatusAndWinner(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/resign")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("state.status", equalTo("resign"))
|
|
||||||
.body("state.winner", notNullValue())
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def resignOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/XXXXXXXX/resign")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
|
|
||||||
// ─── Draw ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def offerDrawSetsDrawOfferedStatus(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/offer")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("ok", equalTo(true))
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("state.status", equalTo("drawOffered"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def acceptDrawAfterOfferSetsDrawStatus(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
// White offers
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/offer")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
// Black moves so it's black's turn... actually the API doesn't enforce turn-based draw accept.
|
|
||||||
// White offered, so black (opponent) accepts — but since there's no auth, we just call accept.
|
|
||||||
// The GameStore checks drawOfferedBy != turn to allow accept.
|
|
||||||
// White offered on white's turn, so black needs to accept — but current turn is still white.
|
|
||||||
// We need to make a move first to switch turns.
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
// Now it's black's turn and white offered the draw — black accepts
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/accept")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("ok", equalTo(true))
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("state.status", equalTo("draw"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def declineDrawClearsOffer(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/offer")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/decline")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("ok", equalTo(true))
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.get(s"/api/board/game/$gameId")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("state.status", equalTo("started"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def acceptWithoutOfferReturns400(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/draw/accept")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def drawOnUnknownGameReturns404(): Unit =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game/XXXXXXXX/draw/offer")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(404)
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package de.nowchess.backcore.resource
|
|
||||||
|
|
||||||
import io.quarkus.test.junit.QuarkusTest
|
|
||||||
import io.restassured.RestAssured
|
|
||||||
import org.hamcrest.Matchers.{containsString, equalTo}
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
@QuarkusTest
|
|
||||||
class UndoRedoTest:
|
|
||||||
|
|
||||||
private val initialFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
||||||
|
|
||||||
private def createGame(): String =
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.contentType("application/json")
|
|
||||||
.body("{}")
|
|
||||||
.when()
|
|
||||||
.post("/api/board/game")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(201)
|
|
||||||
.extract()
|
|
||||||
.path[String]("gameId")
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def undoAfterMoveRestoresOriginalPosition(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/undo")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("fen", equalTo(initialFen))
|
|
||||||
.body("undoAvailable", equalTo(false))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def redoAfterUndoRestoresMovedPosition(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/move/e2e4")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/undo")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/redo")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(200)
|
|
||||||
.body("fen", containsString("4P3"))
|
|
||||||
.body("turn", equalTo("black"))
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def undoWithNoHistoryReturns400(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/undo")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
|
|
||||||
@Test
|
|
||||||
def redoWithNoRedoStackReturns400(): Unit =
|
|
||||||
val gameId = createGame()
|
|
||||||
RestAssured
|
|
||||||
.`given`()
|
|
||||||
.when()
|
|
||||||
.post(s"/api/board/game/$gameId/redo")
|
|
||||||
.`then`()
|
|
||||||
.statusCode(400)
|
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
import org.gradle.api.file.DuplicatesStrategy
|
|
||||||
import org.gradle.jvm.tasks.Jar
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("scala")
|
id("scala")
|
||||||
id("org.scoverage")
|
id("org.scoverage")
|
||||||
application
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "de.nowchess"
|
group = "de.nowchess"
|
||||||
@@ -12,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()
|
||||||
@@ -23,33 +21,23 @@ scala {
|
|||||||
|
|
||||||
scoverage {
|
scoverage {
|
||||||
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
scoverageVersion.set(versions["SCOVERAGE"]!!)
|
||||||
excludedPackages.set(listOf(
|
excludedPackages.set(
|
||||||
"de.nowchess.ui.gui",
|
listOf(
|
||||||
"de.nowchess.ui.terminal",
|
"de\\.nowchess\\.bot\\.bots\\.NNUEBot",
|
||||||
"de.nowchess.ui.Main",
|
"de\\.nowchess\\.bot\\.bots\\.nnue\\..*",
|
||||||
))
|
"de\\.nowchess\\.bot\\.util\\.PolyglotBook",
|
||||||
}
|
)
|
||||||
|
)
|
||||||
application {
|
excludedFiles.set(scoverageExcluded)
|
||||||
mainClass.set("de.nowchess.ui.Main")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<ScalaCompile> {
|
tasks.withType<ScalaCompile> {
|
||||||
scalaCompileOptions.additionalParameters = listOf("-encoding", "UTF-8")
|
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 {
|
dependencies {
|
||||||
|
|
||||||
implementation("org.scala-lang:scala3-compiler_3") {
|
compileOnly("org.scala-lang:scala3-compiler_3") {
|
||||||
version {
|
version {
|
||||||
strictly(versions["SCALA3"]!!)
|
strictly(versions["SCALA3"]!!)
|
||||||
}
|
}
|
||||||
@@ -60,27 +48,10 @@ dependencies {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
implementation(project(":modules:core"))
|
|
||||||
implementation(project(":modules:rule"))
|
|
||||||
implementation(project(":modules:api"))
|
implementation(project(":modules:api"))
|
||||||
implementation(project(":modules:io"))
|
implementation(project(":modules:io"))
|
||||||
|
implementation(project(":modules:rule"))
|
||||||
// ScalaFX dependencies
|
implementation("com.microsoft.onnxruntime:onnxruntime:${versions["ONNXRUNTIME"]!!}")
|
||||||
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(platform("org.junit:junit-bom:${versions["JUNIT_BOM"]!!}"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
@@ -102,3 +73,7 @@ tasks.test {
|
|||||||
tasks.reportScoverage {
|
tasks.reportScoverage {
|
||||||
dependsOn(tasks.test)
|
dependsOn(tasks.test)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
|||||||
|
# Data and weights are local artifacts, not committed
|
||||||
|
data/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
tactical_data/
|
||||||
|
trainingdata/
|
||||||
|
/datasets/
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
# Training Dataset Management
|
||||||
|
|
||||||
|
The NNUE training pipeline now features versioned dataset management, similar to model versioning. This prevents data loss and allows you to maintain multiple training configurations.
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
datasets/
|
||||||
|
ds_v1/
|
||||||
|
labeled.jsonl # Training data: {"fen": "...", "eval": 0.5, "eval_raw": 150}
|
||||||
|
metadata.json # Version info and composition
|
||||||
|
ds_v2/
|
||||||
|
labeled.jsonl
|
||||||
|
metadata.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metadata Schema
|
||||||
|
|
||||||
|
Each dataset has a `metadata.json` file tracking its composition:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"created": "2026-04-13T15:30:45.123456",
|
||||||
|
"total_positions": 1000000,
|
||||||
|
"stockfish_depth": 12,
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"type": "generated",
|
||||||
|
"count": 500000,
|
||||||
|
"params": {
|
||||||
|
"num_positions": 500000,
|
||||||
|
"min_move": 1,
|
||||||
|
"max_move": 50
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tactical",
|
||||||
|
"count": 300000,
|
||||||
|
"max_puzzles": 300000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file_import",
|
||||||
|
"count": 200000,
|
||||||
|
"path": "/path/to/original_file.txt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TUI Workflow
|
||||||
|
|
||||||
|
### Main Menu
|
||||||
|
```
|
||||||
|
1 - Manage Training Data
|
||||||
|
2 - Train Model
|
||||||
|
3 - Export Model
|
||||||
|
4 - Exit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Training Data Management Submenu
|
||||||
|
```
|
||||||
|
1 - Create new dataset
|
||||||
|
2 - Extend existing dataset
|
||||||
|
3 - View all datasets
|
||||||
|
4 - Delete dataset
|
||||||
|
5 - Back
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a Dataset
|
||||||
|
|
||||||
|
Use the "Create new dataset" option to add data from one or more sources:
|
||||||
|
|
||||||
|
1. **Generate random positions** — Play random games and sample positions
|
||||||
|
- Number of positions
|
||||||
|
- Move range (min/max move number to sample from)
|
||||||
|
- Number of worker threads
|
||||||
|
|
||||||
|
2. **Import from file** — Load positions from a FEN file
|
||||||
|
- File must contain one FEN string per line
|
||||||
|
- Duplicates are automatically removed
|
||||||
|
|
||||||
|
3. **Extract tactical puzzles** — Download and extract Lichess puzzle database
|
||||||
|
- Maximum number of puzzles to include
|
||||||
|
- Automatically filters for tactical themes (forks, pins, mates, etc.)
|
||||||
|
|
||||||
|
You can combine multiple sources in a single dataset creation session. All positions are:
|
||||||
|
- Deduplicated (only unique FENs are kept)
|
||||||
|
- Labeled with Stockfish evaluations
|
||||||
|
- Saved to `datasets/ds_vN/labeled.jsonl`
|
||||||
|
|
||||||
|
## Extending a Dataset
|
||||||
|
|
||||||
|
Use "Extend existing dataset" to add more positions to an existing dataset:
|
||||||
|
|
||||||
|
1. Select the dataset version to extend
|
||||||
|
2. Choose data sources (same options as creation)
|
||||||
|
3. Confirm labeling parameters
|
||||||
|
4. New positions are:
|
||||||
|
- Labeled with Stockfish
|
||||||
|
- Deduplicated against the target dataset (preventing duplicates)
|
||||||
|
- Merged into the existing `labeled.jsonl`
|
||||||
|
- Metadata is updated with the new source entry
|
||||||
|
|
||||||
|
## Training with a Dataset
|
||||||
|
|
||||||
|
When you start training (Standard or Burst mode), you'll be prompted to select a dataset version. The TUI will display all available datasets with:
|
||||||
|
- Version number
|
||||||
|
- Total number of positions
|
||||||
|
- Source types (generated, tactical, imported)
|
||||||
|
- Stockfish depth used
|
||||||
|
- Creation date
|
||||||
|
|
||||||
|
## Legacy Data Migration
|
||||||
|
|
||||||
|
If you have existing labeled data in `data/training_data.jsonl` from before this update:
|
||||||
|
|
||||||
|
1. Open the "Manage Training Data" menu
|
||||||
|
2. Choose "Create new dataset"
|
||||||
|
3. Select "Import from file"
|
||||||
|
4. Point to `data/training_data.jsonl`
|
||||||
|
5. Complete the dataset creation
|
||||||
|
|
||||||
|
Alternatively, you can manually copy the file to `datasets/ds_v1/labeled.jsonl` and create a `metadata.json` file.
|
||||||
|
|
||||||
|
## Viewing Dataset Details
|
||||||
|
|
||||||
|
Use "View all datasets" to see a table of all datasets with:
|
||||||
|
- Version number
|
||||||
|
- Position count
|
||||||
|
- Source composition
|
||||||
|
- Stockfish depth
|
||||||
|
- Creation date
|
||||||
|
|
||||||
|
## Deleting a Dataset
|
||||||
|
|
||||||
|
Use "Delete dataset" to remove a dataset and free up disk space. **This action cannot be undone.**
|
||||||
|
|
||||||
|
⚠️ The system does not prevent deleting datasets used by model checkpoints. Plan accordingly.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Deduplication Strategy
|
||||||
|
|
||||||
|
When extending a dataset, positions are deduplicated **within that dataset only**. This allows different datasets to contain overlapping positions if desired.
|
||||||
|
|
||||||
|
When creating a new dataset from multiple sources, all sources are combined and deduplicated before labeling.
|
||||||
|
|
||||||
|
### Labeled Position Format
|
||||||
|
|
||||||
|
Each line in `labeled.jsonl` is a JSON object:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||||
|
"eval": 0.0,
|
||||||
|
"eval_raw": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `fen`: The position in Forsyth-Edwards Notation
|
||||||
|
- `eval`: Normalized evaluation ([-1, 1] range using tanh)
|
||||||
|
- `eval_raw`: Raw Stockfish evaluation in centipawns
|
||||||
|
|
||||||
|
### Storage Location
|
||||||
|
|
||||||
|
Datasets are stored in the `datasets/` directory relative to the script location. The old `data/` directory is preserved for backward compatibility but is not actively used by the new system.
|
||||||
|
|
||||||
|
## Performance Tips
|
||||||
|
|
||||||
|
- **Smaller datasets train faster** — Start with 100k-500k positions
|
||||||
|
- **Deduplication matters** — Use the extend functionality to build up your dataset without redundant data
|
||||||
|
- **Stockfish depth** — Depth 12-14 balances accuracy and labeling speed
|
||||||
|
- **Workers** — Use 4-8 workers for labeling if your machine supports it; more workers = faster but uses more CPU/memory
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# NNUE Python Pipeline
|
||||||
|
|
||||||
|
Central CLI for training and exporting chess evaluation neural networks (NNUE).
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
python/
|
||||||
|
├── nnue.py # Main CLI entry point
|
||||||
|
├── src/ # Python modules
|
||||||
|
│ ├── generate.py # Generate random chess positions
|
||||||
|
│ ├── label.py # Label positions with Stockfish
|
||||||
|
│ ├── train.py # Train NNUE model
|
||||||
|
│ └── export.py # Export weights to Scala
|
||||||
|
├── data/ # Training data (gitignored)
|
||||||
|
│ ├── positions.txt
|
||||||
|
│ └── training_data.jsonl
|
||||||
|
└── weights/ # Model weights (gitignored)
|
||||||
|
├── nnue_weights_v1.pt
|
||||||
|
├── nnue_weights_v1_metadata.json
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Train a new model (500k positions, auto-detect checkpoint)
|
||||||
|
python nnue.py train
|
||||||
|
|
||||||
|
# Train from specific checkpoint
|
||||||
|
python nnue.py train --from-checkpoint 2
|
||||||
|
|
||||||
|
# Train with custom games count
|
||||||
|
python nnue.py train --games 200000
|
||||||
|
|
||||||
|
# Train with custom positions file
|
||||||
|
python nnue.py train --positions-file my_positions.txt
|
||||||
|
|
||||||
|
# Export specific version to Scala
|
||||||
|
python nnue.py export 2
|
||||||
|
|
||||||
|
# List all checkpoints
|
||||||
|
python nnue.py list
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Commands
|
||||||
|
|
||||||
|
### `train` - Train NNUE model
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python nnue.py train [OPTIONS]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:**
|
||||||
|
- `--from-checkpoint N` - Resume from checkpoint version N (default: uses latest)
|
||||||
|
- `--games N` - Number of games to generate (default: 500000)
|
||||||
|
- `--positions-file FILE` - Use existing positions file instead of generating
|
||||||
|
- `--stockfish PATH` - Path to Stockfish binary (default: `$STOCKFISH_PATH` or `/usr/games/stockfish`)
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Train with latest checkpoint
|
||||||
|
python nnue.py train
|
||||||
|
|
||||||
|
# Train from v2 with 100k games
|
||||||
|
python nnue.py train --from-checkpoint 2 --games 100000
|
||||||
|
|
||||||
|
# Train with custom positions
|
||||||
|
python nnue.py train --positions-file my_games.txt --stockfish /opt/stockfish/sf15
|
||||||
|
```
|
||||||
|
|
||||||
|
### `export` - Export weights to Scala
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python nnue.py export WEIGHTS [output_path]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Arguments:**
|
||||||
|
- `WEIGHTS` - Version number (e.g., `2`) or full filename (e.g., `nnue_weights_v2.pt`)
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
```bash
|
||||||
|
# Export version 2
|
||||||
|
python nnue.py export 2
|
||||||
|
|
||||||
|
# Export with full filename
|
||||||
|
python nnue.py export nnue_weights_v3.pt
|
||||||
|
```
|
||||||
|
|
||||||
|
Output goes to `../src/main/scala/de/nowchess/bot/bots/nnue/NNUEWeights_vN.scala`
|
||||||
|
|
||||||
|
### `list` - List available checkpoints
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python nnue.py list
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows all available model versions with file sizes.
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
1. **Generate** → `data/positions.txt`
|
||||||
|
- Random chess positions from 8-20 move openings
|
||||||
|
- Filters out checks, game-over states, and captures
|
||||||
|
|
||||||
|
2. **Label** → `data/training_data.jsonl`
|
||||||
|
- Evaluates each position with Stockfish at depth 12
|
||||||
|
- Stores FEN + evaluation in JSONL format
|
||||||
|
|
||||||
|
3. **Train** → `weights/nnue_weights_vN.pt`
|
||||||
|
- Trains neural network on labeled positions
|
||||||
|
- Auto-versioning (v1, v2, v3, etc.)
|
||||||
|
- Saves metadata alongside weights
|
||||||
|
|
||||||
|
4. **Export** → `NNUEWeights_vN.scala`
|
||||||
|
- Converts weights to Scala object
|
||||||
|
- Ready for integration into bot
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
- Models are automatically versioned (v1, v2, v3, etc.)
|
||||||
|
- Each version gets a `_metadata.json` file with training info
|
||||||
|
- Training from checkpoint uses latest version unless specified with `--from-checkpoint`
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `data/` and `weights/` are gitignored (local artifacts)
|
||||||
|
- Documentation in `docs/` explains training, debugging, and incremental improvements
|
||||||
|
- Source modules in `src/` are independent and can be imported for custom workflows
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user