Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7136803c7e | |||
| faf7eb38ea |
@@ -1259,3 +1259,61 @@
|
||||
### Reverts
|
||||
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
## (2026-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||
* **ncs-110:** feed NNUE root-move scores into search move ordering ([#83](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/83)) ([e4fee85](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e4fee8513430093d46957970618935e99591519f))
|
||||
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||
* **official-bots:** activate opening book in expert bot (native-safe) ([260db25](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/260db25803ec55ce99e55782791eabdc190dfed4))
|
||||
* **official-bots:** add Google Colab notebook for NNUE training (NCS-111) ([#81](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/81)) ([fa10852](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fa10852bc98451d4068ec6fb9e7a486b5e53ef5c))
|
||||
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||
* **official-bots:** implement king-relative (HalfKP) encoding in NNUE (NCS-109) ([#80](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/80)) ([44f376f](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/44f376f03221f086b898741436e13c93fd314dd1))
|
||||
* **official-bots:** make HybridBot veto actionable and use it for expert ([1df29cf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1df29cf3a6e21af3f396b2b7a6da67d978f941ae))
|
||||
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||
* **official-bots:** standalone self-play + one-shot dataset builder for NNUE training ([1c80abd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1c80abdb8a45814d642d43c633cde81ce7374c4f))
|
||||
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bot:** include quiet promotions in quiescence search ([4938560](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/49385600147021cd29f00a8eecc6be7ba8470717))
|
||||
* **bot:** seed search with game history, add contempt and NNUE mop-up ([faf7eb3](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/faf7eb38ea7a0d3bc41ae4c2ef9a5195822f390c))
|
||||
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||
* modified training pipeline ([9f9140c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9f9140cb585345cd244a1dfee1a06e51a5f7f7a8))
|
||||
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||
* **official-bots:** derive tournament game color from game endpoint ([#79](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/79)) ([bfc4672](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/bfc46723e615bb9b65f7f9bba5f53877c4f079a7))
|
||||
* **official-bots:** discover tournament games by polling, not just the stream ([10113fd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/10113fd0579b614d15870798d933bc9c495d2049))
|
||||
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||
* **official-bots:** play games by polling state instead of NDJSON stream ([bfb15c7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/bfb15c7299bd471d5e064a577ed10af98e2ea90a))
|
||||
* **official-bots:** play only own tournament games with correct color ([4651bb7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4651bb796f07a21bd013d9521b2dfe2e1078cebb))
|
||||
* **official-bots:** prevent Colab OOM in NNUE training ([e2b4342](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/e2b4342f602215b5e8de6fccafc4105525a1ddd1))
|
||||
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||
* **official-bots:** resolve per-difficulty bot token on tournament join ([fdf4c94](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/fdf4c94811d086996447bb4657fac1d9bd6e5a93))
|
||||
* **official-bots:** resume tournaments already joined after restart ([285b73e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/285b73efbd6dd98cec410ade9eead9881d693a8f))
|
||||
* **official-bots:** stream NNUE features as sparse indices to stop host OOM ([9d65662](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9d656624d85889f55746faa5704578e248f9b088))
|
||||
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||
* **official-bots:** use ThreadLocalRandom in PolyglotBook for native image ([1b30c3b](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1b30c3be393d25712c8743d3d9057207f8bbb67c))
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||
|
||||
@@ -12,7 +12,7 @@ object EvaluationNNUE extends Evaluation:
|
||||
val DRAW_SCORE: Int = 0
|
||||
|
||||
/** Full-board evaluate — used as fallback and by non-search callers. */
|
||||
def evaluate(context: GameContext): Int = nnue.evaluate(context)
|
||||
def evaluate(context: GameContext): Int = nnue.evaluate(context) + MopUp.score(context)
|
||||
|
||||
// ── Accumulator hooks (incremental L1) ───────────────────────────────────
|
||||
|
||||
@@ -28,4 +28,4 @@ object EvaluationNNUE extends Evaluation:
|
||||
else nnue.pushAccumulator(childPly, move, parent.board, child.board)
|
||||
|
||||
override def evaluateAccumulator(ply: Int, context: GameContext, hash: Long): Int =
|
||||
nnue.evaluateAtPlyWithValidation(ply, context.turn, hash, context.board)
|
||||
nnue.evaluateAtPlyWithValidation(ply, context.turn, hash, context.board) + MopUp.score(context)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.nowchess.bot.bots.nnue
|
||||
|
||||
import de.nowchess.api.board.{Color, PieceType, Square}
|
||||
import de.nowchess.api.game.GameContext
|
||||
|
||||
/** Endgame "mop-up" correction for the NNUE evaluation.
|
||||
*
|
||||
* Pure NNUE lacks explicit mating knowledge, so KX-vs-K conversions stall. When one side is reduced to a lone king and
|
||||
* the other holds sufficient mating material, this term rewards driving the bare king to the edge and walking the
|
||||
* winning king in. Returns a value from the side-to-move perspective (positive = good for side to move). Zero in any
|
||||
* position that is not a lone-king endgame, so middlegame NNUE output is untouched.
|
||||
*/
|
||||
object MopUp:
|
||||
|
||||
private val EDGE_WEIGHT = 10
|
||||
private val PROXIMITY_WEIGHT = 4
|
||||
private val MIN_WINNER_VALUE = 400
|
||||
|
||||
def score(context: GameContext): Int =
|
||||
loneKingColor(context) match
|
||||
case None => 0
|
||||
case Some(loser) =>
|
||||
val winner = loser.opposite
|
||||
if winnerValue(context, winner) < MIN_WINNER_VALUE then 0
|
||||
else mopUp(context, winner, loser) * (if context.turn == winner then 1 else -1)
|
||||
|
||||
private def mopUp(context: GameContext, winner: Color, loser: Color): Int =
|
||||
(for
|
||||
loserKing <- context.kingSquare(loser)
|
||||
winnerKing <- context.kingSquare(winner)
|
||||
yield EDGE_WEIGHT * centerDistance(loserKing) +
|
||||
PROXIMITY_WEIGHT * (14 - kingDistance(winnerKing, loserKing))).getOrElse(0)
|
||||
|
||||
private def loneKingColor(context: GameContext): Option[Color] =
|
||||
val nonKing = context.board.pieces.values.filter(_.pieceType != PieceType.King)
|
||||
val whiteHasOther = nonKing.exists(_.color == Color.White)
|
||||
val blackHasOther = nonKing.exists(_.color == Color.Black)
|
||||
if whiteHasOther == blackHasOther then None
|
||||
else if whiteHasOther then Some(Color.Black)
|
||||
else Some(Color.White)
|
||||
|
||||
private def winnerValue(context: GameContext, winner: Color): Int =
|
||||
context.board.pieces.values.foldLeft(0) { (sum, piece) =>
|
||||
if piece.color != winner then sum
|
||||
else
|
||||
sum + (piece.pieceType match
|
||||
case PieceType.Queen => 900
|
||||
case PieceType.Rook => 500
|
||||
case PieceType.Bishop => 330
|
||||
case PieceType.Knight => 320
|
||||
case _ => 0)
|
||||
}
|
||||
|
||||
private def centerDistance(sq: Square): Int =
|
||||
val fileDist = math.max(3 - sq.file.ordinal, sq.file.ordinal - 4)
|
||||
val rankDist = math.max(3 - sq.rank.ordinal, sq.rank.ordinal - 4)
|
||||
fileDist + rankDist
|
||||
|
||||
private def kingDistance(a: Square, b: Square): Int =
|
||||
(a.file.ordinal - b.file.ordinal).abs + (a.rank.ordinal - b.rank.ordinal).abs
|
||||
@@ -1,6 +1,6 @@
|
||||
package de.nowchess.bot.logic
|
||||
|
||||
import de.nowchess.api.board.PieceType
|
||||
import de.nowchess.api.board.{CastlingRights, PieceType}
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.api.move.{Move, MoveType}
|
||||
import de.nowchess.bot.ai.Evaluation
|
||||
@@ -26,6 +26,7 @@ final class AlphaBetaSearch(
|
||||
private val TIME_CHECK_FREQUENCY = 1000
|
||||
private val FUTILITY_MARGIN = 100
|
||||
private val CHECK_EXTENSION = 1
|
||||
private val CONTEMPT = 25
|
||||
|
||||
private val timeStartMs = AtomicLong(0L)
|
||||
private val timeLimitMs = AtomicLong(0L)
|
||||
@@ -67,6 +68,7 @@ final class AlphaBetaSearch(
|
||||
timeLimitMs.set(Long.MaxValue / 4)
|
||||
nodeCount.set(0)
|
||||
val rootHash = ZobristHash.hash(context)
|
||||
val history = historyCounts(context)
|
||||
(1 to maxDepth)
|
||||
.foldLeft((None: Option[Move], 0)) { case ((bestSoFar, prevScore), depth) =>
|
||||
val (alpha, beta) =
|
||||
@@ -78,6 +80,7 @@ final class AlphaBetaSearch(
|
||||
beta,
|
||||
ASPIRATION_DELTA,
|
||||
rootHash,
|
||||
history,
|
||||
excludedRootMoves,
|
||||
hints,
|
||||
)
|
||||
@@ -115,6 +118,7 @@ final class AlphaBetaSearch(
|
||||
timeLimitMs.set(timeBudgetMs)
|
||||
nodeCount.set(0)
|
||||
val rootHash = ZobristHash.hash(context)
|
||||
val history = historyCounts(context)
|
||||
|
||||
@scala.annotation.tailrec
|
||||
def loop(bestSoFar: Option[Move], prevScore: Int, depth: Int, lastDepth: Int): (Option[Move], Int) =
|
||||
@@ -129,6 +133,7 @@ final class AlphaBetaSearch(
|
||||
beta,
|
||||
ASPIRATION_DELTA,
|
||||
rootHash,
|
||||
history,
|
||||
excludedRootMoves,
|
||||
hints,
|
||||
)
|
||||
@@ -154,10 +159,11 @@ final class AlphaBetaSearch(
|
||||
beta: Int,
|
||||
initialWindow: Int,
|
||||
rootHash: Long,
|
||||
history: Map[Long, Int],
|
||||
excludedRootMoves: Set[Move],
|
||||
hints: Map[Move, Int],
|
||||
): (Int, Option[Move]) =
|
||||
val state = SearchState(rootHash, Map(rootHash -> 1))
|
||||
val state = SearchState(rootHash, history)
|
||||
|
||||
@scala.annotation.tailrec
|
||||
def loop(currentAlpha: Int, currentBeta: Int, delta: Int, attempt: Int): (Int, Option[Move]) =
|
||||
@@ -173,6 +179,32 @@ final class AlphaBetaSearch(
|
||||
|
||||
loop(alpha, beta, initialWindow, 0)
|
||||
|
||||
private def drawScore(ply: Int): Int =
|
||||
if ply % 2 == 0 then weights.DRAW_SCORE - CONTEMPT else weights.DRAW_SCORE + CONTEMPT
|
||||
|
||||
private def historyCounts(context: GameContext): Map[Long, Int] =
|
||||
val initialTurn = if context.moves.size % 2 == 0 then context.turn else context.turn.opposite
|
||||
val root = GameContext(
|
||||
board = context.initialBoard,
|
||||
turn = initialTurn,
|
||||
castlingRights = CastlingRights.Initial,
|
||||
enPassantSquare = None,
|
||||
halfMoveClock = 0,
|
||||
moves = List.empty,
|
||||
)
|
||||
val rootHash = ZobristHash.hash(root)
|
||||
context.moves
|
||||
.foldLeft((root, rootHash, List(rootHash))) { case ((cur, curHash, acc), move) =>
|
||||
val next = rules.applyMove(cur)(move)
|
||||
val nextHash = ZobristHash.nextHash(cur, curHash, move, next)
|
||||
(next, nextHash, nextHash :: acc)
|
||||
}
|
||||
._3
|
||||
.groupBy(identity)
|
||||
.view
|
||||
.mapValues(_.size)
|
||||
.toMap
|
||||
|
||||
private def hasNonPawnMaterial(context: GameContext): Boolean =
|
||||
context.board.pieces.values.exists { piece =>
|
||||
piece.color == context.turn &&
|
||||
@@ -226,7 +258,8 @@ final class AlphaBetaSearch(
|
||||
): Option[(Int, Option[Move])] =
|
||||
if count % TIME_CHECK_FREQUENCY == 0 && isOutOfTime then
|
||||
Some((weights.evaluateAccumulator(params.ply, params.context, params.state.hash), None))
|
||||
else if params.state.repetitions.getOrElse(params.state.hash, 0) >= 3 then Some((weights.DRAW_SCORE, None))
|
||||
else if params.ply > 0 && params.state.repetitions.getOrElse(params.state.hash, 0) >= 2 then
|
||||
Some((drawScore(params.ply), None))
|
||||
else ttCutoff(params)
|
||||
|
||||
private def ttCutoff(params: SearchParams): Option[(Int, Option[Move])] =
|
||||
@@ -248,12 +281,12 @@ final class AlphaBetaSearch(
|
||||
if legalMoves.isEmpty then
|
||||
Some(
|
||||
(
|
||||
if rules.isCheckmate(params.context) then -(weights.CHECKMATE_SCORE - params.ply) else weights.DRAW_SCORE,
|
||||
if rules.isCheckmate(params.context) then -(weights.CHECKMATE_SCORE - params.ply) else drawScore(params.ply),
|
||||
None,
|
||||
),
|
||||
)
|
||||
else if rules.isInsufficientMaterial(params.context) || rules.isFiftyMoveRule(params.context) then
|
||||
Some((weights.DRAW_SCORE, None))
|
||||
Some((drawScore(params.ply), None))
|
||||
else if params.depth == 0 then
|
||||
Some((quiescence(params.context, params.ply, params.window.alpha, params.window.beta, params.state.hash), None))
|
||||
else None
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.nowchess.bot
|
||||
|
||||
import de.nowchess.api.board.{Board, Color, File, Piece, Rank, Square}
|
||||
import de.nowchess.api.game.GameContext
|
||||
import de.nowchess.bot.bots.nnue.MopUp
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class MopUpTest extends AnyFunSuite with Matchers:
|
||||
|
||||
private def ctx(turn: Color, pieces: (Square, Piece)*): GameContext =
|
||||
GameContext.initial.withBoard(Board(pieces.toMap)).withTurn(turn)
|
||||
|
||||
private val wk = Square(File.E, Rank.R1) -> Piece.WhiteKing
|
||||
private val wq = Square(File.D, Rank.R1) -> Piece.WhiteQueen
|
||||
private val bkCorner = Square(File.H, Rank.R8) -> Piece.BlackKing
|
||||
private val bkCenter = Square(File.D, Rank.R4) -> Piece.BlackKing
|
||||
|
||||
test("zero in a balanced middlegame-like position (both sides have material)"):
|
||||
MopUp.score(ctx(Color.White, wk, wq, bkCorner, Square(File.A, Rank.R8) -> Piece.BlackQueen)) should be(0)
|
||||
|
||||
test("zero when winner lacks mating material (lone king vs king)"):
|
||||
MopUp.score(ctx(Color.White, wk, bkCorner)) should be(0)
|
||||
|
||||
test("positive for the winning side to move in KQ vs K"):
|
||||
MopUp.score(ctx(Color.White, wk, wq, bkCorner)) should be > 0
|
||||
|
||||
test("negative for the bare-king side to move in KQ vs K"):
|
||||
MopUp.score(ctx(Color.Black, wk, wq, bkCorner)) should be < 0
|
||||
|
||||
test("cornered bare king scores higher than centralized bare king"):
|
||||
val cornered = MopUp.score(ctx(Color.White, wk, wq, bkCorner))
|
||||
val centralized = MopUp.score(ctx(Color.White, wk, wq, bkCenter))
|
||||
cornered should be > centralized
|
||||
@@ -1,3 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=41
|
||||
MINOR=42
|
||||
PATCH=0
|
||||
|
||||
Reference in New Issue
Block a user