Skip to content

Releases: dlbbld/ashlar-chess

22.1.0

Choose a tag to compare

@dlbbld dlbbld released this 07 Oct 07:45
862b3ef

Magic bitboards and faster adjudication

Bishop and rook attack generation now uses magic bitboards, with unchanged move-generation semantics and a measured reduction in legal-move generation time across three machines. Flag-fall and resignation adjudication gain a guarded elementary-material theorem pre-check that avoids search where the result is already proven. The release also adds reproducible performance tooling and hardens the release procedure. No public API is removed or changed.

Notable

  • Magic-bitboard slider attacks. Bishop and rook queries use precomputed occupancy-indexed tables; queen attacks combine them. Independent geometric, differential and perft checks verify correctness. Measured legal-move generation time falls by approximately 17–25% across the reviewed ThinkPad, iMac M3 and IdeaCentre workloads. The tables require 841 KiB of attack entries plus metadata, with a one-time initialization cost. See the verification record for workloads, environments and corrected baselines.
  • Faster adjudication on covered elementary-material positions. Quick and full flag-fall/resignation methods first consult the basic-helpmate-existence theorem. The pre-check excludes KBNvK and opposite-coloured KBBvK, whose retro-illegal counterexamples can pass strict FEN parsing, and defers those classes to the existing analyzers. The standalone quick and full unwinnability analyzers retain their paper-derived algorithms and witness contracts.

Validation and tooling

  • An opt-in JMH profile provides slider microbenchmarks and production workloads without adding JMH to the published library. iMac and Windows procedures preserve machine/JVM details, raw fork results, logs and harness fingerprints.
  • A default-profile corpus lint catches malformed strict PGN file structure before the expensive release suites. Additional proof games pin the chasolver exhaustion challenges, with external oracle coverage.
  • The test-only chesslib comparison dependency is updated to 1.3.7.
  • Release preflight now runs fail-fast gates in cost order: clean working tree, license headers, JavaDoc, then both full regression suites. Release notes are generated from the changelog with paragraphs unwrapped for GitHub rendering.

22.0.0

Choose a tag to compare

@dlbbld dlbbld released this 04 Jul 12:22
f04800f

Unwinnability now straight from the FUN 2022 paper

The unwinnability engine is now ashlar's own independent, paper-derived implementation of Miguel Ambrona's FUN 2022 paper A Practical Algorithm for Chess Unwinnability (Figures 5-13, Lemmas 5/6, Theorem 12), governed by the committed specification fun22-spec.pdf and derived from the paper only. It replaces the Java port of Ambrona's C++ CHA (D3-Chess): the algorithm is now traceable to the published paper rather than to another codebase. The engine was built and validated as a standalone independent project first: zero soundness contradictions against the D3-Chess ground-truth corpus, against the previous cha-port analyzer, and against Ambrona's independent Rust chasolver. Alongside the engine swap, PGN import learns real-world move annotations: NAGs and suffix glyphs are one unified, preserved model (validated against python-chess), and the lenient parser skips analysis side-lines (RAV) to read the game that was played.

Breaking

  • PGN move annotations are now a unified NAG model. PgnMove stores List<Nag> instead of a stored MoveSuffixAnnotation; the six suffix glyphs (!, ?, !!, ??, !?, ?!) are parsed as NAG codes 1-6. PgnMove.moveSuffixAnnotation() remains as a derived read convenience for the first assessment NAG, but code that constructs or inspects moves should use nags().
  • UnwinnabilityQuickVerdict gains WINNABLE (paper Figure 10 is three-valued): the quick analysis now proves winnability on quickly matable positions instead of answering POSSIBLY_WINNABLE. Exhaustive switches over the enum need a new case; comparisons against UNWINNABLE (the adjudication use) are unaffected.
  • WinnableProof is removed and UnwinnabilityFullAnalysis is now (verdict, mateLine): the basic-helpmate-existence theorem no longer short-circuits the full analyzer (it lives on as a test-side oracle), so every WINNABLE verdict is search-proven and mateLine() is its witness - empty exactly for a zero-move helpmate (the submitted position is already the intended winner's checkmate). Elementary-material wins (KQvK, KRvK, KNNvK, ...) therefore now carry concrete mate lines instead of a theorem certification.

Behavioral

  • PGN import/export now handles real-world move annotations. Strict PGN accepts numeric annotation glyphs ($0-$255) after moves; both parsers fold suffix glyphs and $N tokens into the same move NAG list. Semantic export writes the first assessment NAG as a glyph and every other NAG as $N; archival export writes a deduplicated, sorted $N sequence, including assessment codes. The lenient parser also skips recursive annotation variations (...) to keep the mainline; strict parsing still rejects RAV.
  • Quick analysis follows the paper's Figure 10 (bounded DFS with a global depth-9 interrupt, then the semi-static check gated to pawn/bishop/king material without semi-open files) instead of CHA's deployed quick_analysis heuristics. Some verdicts move between UNWINNABLE and POSSIBLY_WINNABLE in both directions; definite verdicts remain sound.
  • Full analysis follows the paper's Figure 9 (semi-static shortcut, then Find-Helpmate under iterative deepening with the Figure 12/13 heuristics and the Figure 5 footnote-b reward chaining); the mate line on wins and the 500 000-node global budget are kept. Resolution on hard positions may differ from the cha port in both directions (the paper and cha diverge on completeness, never soundness); the exhausted-budget fall-through now correctly answers UNDETERMINED where the old code could in principle over-claim UNWINNABLE.
  • The basic-helpmate-existence theorem moved from production to the test suite. The full analyzer is the paper's Figure 9 with no theorem step; the theorem now certifies the engine in tests (agreement over the curated elementary-material corpus: UNWINNABLE wherever the theorem proves unwinnability, a concrete helpmate wherever it guarantees one). A side effect improves the out-of-domain story: on the known retro-illegal theorem counterexamples, the full analyzer now proves UNWINNABLE by search exhaustion, agreeing with the quick analyzer and chasolver - the previously documented full-vs-quick disagreement on those illegal positions is gone.

Internal

  • The cha-port internals (mobility/semi-static/search/material classes and their unit tests) are deleted; the paper-derived engine classes and their ported unit tests replace them, including the permanent Theorem 12 soundness sweep (over the curated chasolver ground-truth corpus).

21.1.0

Choose a tag to compare

@dlbbld dlbbld released this 02 Jul 23:10
339a778

Oracle housekeeping

A documentation-and-test housekeeping release around the unwinnability analyzers and their oracles: the legal-position contract is now stated on the API, the 21.0.0 notes are corrected, and the known retro-illegal theorem counterexamples are locked down as tests. No behavioral change to any analyzer or parser.

Documentation

  • Legal-position contract for the unwinnability analyzers. The unwinnability package-info, the manual, and the three public analyzers (UnwinnableFullAnalyzer, UnwinnableQuickAnalyzer, DeadPositionAnalyzer) now state the contract explicitly: the analysis is guaranteed only on legal positions (reachable from the initial position); on illegal input the result is undefined and the full and quick analyzers may visibly disagree. Legality beyond strict-FEN checks is not enforced - it is decidable only by retrograde analysis - so submitting legal positions is the caller's responsibility.
  • ashlar ports the C++ cha, not the Rust chasolver. The README and package-info now say precisely which of Miguel Ambrona's implementations ashlar ports: the earlier C++ cha (D3-Chess) and the CHA paper - the github.com/miguel-ambrona/D3-Chess URL now redirects to the newer Rust chasolver, which ashlar does not port.
  • 21.0.0 release-notes correction. The 21.0.0 notes overclaimed the KNNvK/KRRvK/KQQvK theorem as a completeness improvement ("positions that could previously land at UNDETERMINED are now proven WINNABLE"). The search already proved these classes WINNABLE; the theorem is a performance and proof-provenance improvement. The [21.0.0] CHANGELOG entry and the published 21.0.0 GitHub Release notes are corrected in place.

Internal

  • Retro-illegal theorem-counterexample lock-down (test-only). The 19 known retro-illegal representatives from basic-helpmate-existence ("Illegal positions not satisfying the conclusion") are asserted as documented out-of-domain cases: strict FEN accepts them, the full analyzer answers theorem-certified WINNABLE, the quick analyzer UNWINNABLE - the exact divergence the legal-position contract documents.
  • Oracle housekeeping (test-only): CHA oracle fixtures reorganized, stale accepted-differences rows pruned, the chasolver node-limit exception PGN fixed, and an Eclipse null-analysis / auto-format pass over the source tree.

21.0.0

Choose a tag to compare

@dlbbld dlbbld released this 27 Jun 17:57
152de9c

Unwinnability soundness and endgame helpmate theorems

A correctness release for the CHA unwinnability / adjudication path, with stricter FEN legality checking. No public type or signature was removed or changed, but several adjudication verdicts and validation outcomes are observably different, so this is a major bump.

Behavioral

  • Unwinnability soundness fix. UnwinnableSemiStatic no longer applies an unsound shortcut that dropped a limited pawn from the semi-static visitor set, which could over-claim UNWINNABLE on positions where a pawn-net helpmate exists. The fix removes those false draws; the trade-off is that some pawn-wall fortress draws the shortcut also (correctly) proved are now reported UNDETERMINED rather than UNWINNABLE — sound, but less complete. Affects Board.unwinnableFull / Board.unwinnableQuick and the Adjudicator.
  • Endgame helpmate-existence theorems (KNNvK, KRRvK, KQQvK). The complete analyzer now decides these three material classes directly via the basic-helpmate-existence theorem (tracking the sibling project's 1.2.0 release) instead of running the (sometimes long) cooperative-mate search. A performance and proof-provenance improvement, not a completeness one: the search already proved these classes WINNABLE; the theorem answers immediately with a theorem-certified verdict (WinnableProof.THEOREM, no mate line).
  • Strict FEN rejects unreachable check configurations. Board.fromFenStrict (and PGN import of a [SetUp "1"] / [FEN ...] header) now rejects positions whose check configuration cannot arise from any legal move: a double check by two same-coloured bishops, two rooks, two queens, or two knights, or three or more simultaneous checkers. The new reason is StrictFenSemanticValidationProblem.INVALID_POSITION_IMPOSSIBLE_CHECK. A FEN accepted before that encodes such an impossible check is now rejected.

Internal

  • chasolver (Rust) cross-validation oracle. A differential-test harness validates ashlar against Miguel Ambrona's Rust chasolver (successor to his C++ cha / D3-Chess), confirming the Rust implementation adheres to the C++ and that ashlar never contradicts either on a legal position. Test-only; no shipped change.
  • Stockfish board-replay survey added for board-pipeline cross-checking. Test-only.

20.0.0

Choose a tag to compare

@dlbbld dlbbld released this 25 Jun 15:57
d5639b6

JPMS module boundary and public-API surface reset

ashlar-chess 20.0.0 is now a proper JPMS module with a deliberately narrow public API. This is a breaking release, and the breaks apply to plain class-path consumers too — they are package relocations, not just module encapsulation. The module name io.github.dlbbld.ashlarchess is identical to the Automatic-Module-Name shipped in 19.1.0, so adopting the named module is not itself a rename.

Breaking

  • JPMS module — module-info.java exports only the public API: board, board.enums, fen, fen.model, pgn, san, adjudication, report, unwinnability, exceptions, bitboard (BitboardPosition as documented advanced low-level API). Every other package is internal and inaccessible to modular consumers.
  • Guava removed from the public API (and the library entirely) — Guava ImmutableList/ImmutableSet/ImmutableMap return types are now JDK List/Set/Map. Drop Guava (and any requires com.google.common) from your build; ashlar-chess no longer pulls it in.
  • Public vocabulary consolidated into board — LegalMove, LegalMoveKind, UciMove, MoveSpecification, Outcome, Termination, MoveCheck, InvalidMoveException, ClaimRights, ClaimableMove, DynamicPosition now import from io.github.dlbbld.ashlarchess.board. The base exception hierarchy (ChessApiRuntimeException, UsageException, ProgrammingMistakeException, NonePointerException, FileSystemAccessException) is in the top-level exceptions package.
  • Implementation types are now hidden (moved into non-exported packages such as the *.internal subpackages, moves, squares, analyze): the move-generation engine, CastlingUtility, SquareUtility/RankUtility, the SAN/LAN generators MoveToSan/MoveToLan, UpdateSquare, MoveNumberFormat, and the Nulls/Message helpers, among others. Use the public API instead (see Added).
  • No forced logging or utility dependency — commons-lang3 and log4j are no longer dependencies; internal logging uses the JDK's java.util.logging. (org.eclipse.jdt.annotation is requires static transitive: optional and compile-only.)
  • Full unwinnability verdict collapsed back to the public domain answer — UnwinnabilityFullVerdict exposes WINNABLE, UNWINNABLE, UNDETERMINED. The proof detail lives on UnwinnabilityFullAnalysis: winnableProof() returns WinnableProof.THEOREM/HELPMATE/NONE, and mateLine() carries the concrete UCI line for searched wins.

Added

  • module-info.java — module io.github.dlbbld.ashlarchess.
  • Castling geometry on CastlingMove — kingFromSquare(Side), kingToSquare(Side), rookFromSquare(Side), rookToSquare(Side) resolve the squares a castling move touches (a castling MoveSpecification stores Square.NONE for from/to).
  • MoveSpecification.isCastling() — companion to the existing isPromotion().
  • LegalMove conveniences — isCapture(), isCastling(), isPromotion(), isEnPassant(), and enPassantCapturedPawnSquare() (the captured pawn does not stand on the move's destination square).

Internal

  • Package-by-feature reset: the duplicate by-kind buckets (model/common.model, enums/common.enums, common.exceptions) are gone; cross-cutting helpers consolidated into a single non-exported internal package; dead post-Guava code (ImmutableUtility) removed.
  • A module-boundary guard test fails the build if the exported package set or the javadoc excludePackageNames drift from the intended public API.

19.1.0

Choose a tag to compare

@dlbbld dlbbld released this 21 Jun 20:12
df05612

Intentional JPMS module name

A packaging-only release. The JAR now declares a stable, intentional automatic module name, so JPMS
(named-module) consumers get a contract-grade requires name instead of one derived by accident from the
file name. No code, behavior, or public-API changes — every verdict, report, and signature is identical to
19.0.0.

Notable

  • Automatic-Module-Name: io.github.dlbbld.ashlarchess. Previously the JAR carried no module name, so
    on the module path the JVM derived ashlar.chess from the file name — an accidental name nobody chose.
    Declaring it in the manifest locks the intentional io.github.dlbbld.ashlarchess (mirroring the base
    package) ahead of the explicit module-info.java planned for 20.0.0, so the eventual named module adopts
    a name consumers can already depend on. Plain class-path users are unaffected.

Internal

  • Bumped central-publishing-maven-plugin 0.7.0 → 0.11.0. The Central Portal API added a warnings field
    to its publish response that 0.7.0's response model could not parse, crashing mvn -Prelease deploy;
    0.11.0 handles it. Build-only — not in the published artifact.
  • Removed the empty base-package package-info.java. It held only a no-op @NonNullByDefault, and JDK 21's
    javadoc fails the -javadoc.jar build on a package containing nothing but a package-info.java.
    Internal only; no public type affected.

19.0.0

Choose a tag to compare

@dlbbld dlbbld released this 21 Jun 16:23
9ca2b54

The essential shape: coherent API, domain-owned behavior, declared scope

A surface-only release. It retires the HalfMove concept, replaces the "halfmove" / "ply" vocabulary with "move" across the public model and reports, and applies a "data carriers, not behavior" pass that moves computed, translation, and factory logic off records and enums into dedicated utility / translator classes. No rule, parser, or analysis behavior changes — every verdict and every report is identical to 18.1.0; what changes is the shape of the API. It is heavily binary-incompatible, so the breaking list is long, but most migrations are a single type rename or a Type.method(x) → TypeUtility.method(x) call-site change.

Notable

  • Records and enums are now data carriers. A new rule in coding-conventions.md draws the line: records and enums hold data; computational, translation, and factory logic lives in dedicated *Utility / *Translator classes. BitboardPosition is the one documented exception — it carries its move-generation / king-safety engine on the record for hot-path allocation reasons.
  • A last-chance API-polish pass was folded in before publication, so no further major bump is needed for it. It refined the data-carrier rule: type-creating factories (of(...) from typed components, parse(...) from text), same-type conversions (to*), and tiny intrinsic self-description predicates live on the enum or record they describe, while rule evaluation stays in utilities — this returned the Fen*Symbol and PromotionPieceType factories to their enums and added MoveSpecification.isPromotion(). It also dropped the calculateIs / calculateHas prefix from boolean predicates (is / has), switched PGN export to the to* idiom, surfaced immutable collection types in signatures, and gave whole-position dead detection its own DeadPositionAnalyzer. See the Breaking list.
  • HalfMove is retired. The played-move row is now MoveRecord, rebuilt inside the report layer from Board's existing per-move accessors; the public PGN movetext type is PgnMove.
  • "Move" is the consistent term for one player's action across the public model, move history, and reports. "halfmove" and "ply" now appear only where they are protocol- or engine-correct: FEN/PGN counters (halfMoveClock, the PlyCount tag) and the engine/search internals.
  • Core board enums print conventional chess notation. Square.toString() now returns a1..h8 (not the A1 constant name); File → a..h; Rank → 1..8; Piece → the FEN letter (P/p, ...); PieceType → P/N/B/R/Q/K (and none for the NONE sentinels). Logging and diagnostics now read like a chess board. The explicit accessors (getName(), getLetter(), getNumber()) are unchanged.
  • Added a Maven Central version badge to the README.

Behavioral

  • No rule, report, or adjudication behavior changes: move legality, FEN/SAN/PGN export, unwinnability and adjudication verdicts, and all report output are identical to 18.1.0. Parsing is identical except for the deliberate changes below. The one cosmetic change is the conventional toString() on the core board.enums types (see Notable).
  • The lenient PGN parser now accepts ; rest-of-line comments (PGN import format, spec section 5), attaching them as commentary exactly like brace comments; previously the ; leaked into a SAN token and the parse failed. The strict PGN parser still rejects them — export format does not include ; comments.
  • The lenient PGN parser now reports a malformed game-termination marker — any digits/hyphen/slash run that is not one of 1-0 / 0-1 / 1/2-1/2 / * (e.g. 1-2, 7/) — as a LenientPgnParserValidationException with the new LenientPgnParserValidationProblem.MOVETEXT_TERMINATION_MARKER_INVALID; previously it leaked a raw IllegalArgumentException out of ResultTagValue.parse. The strict parser already guarded this. (Found by the parser fuzz harness.)
  • NonePointerException is now a UsageException rather than a ProgrammingMistakeException, and the FEN/PGN validate(...) methods now let a ProgrammingMistakeException propagate instead of masking it as UNKNOWN_ERROR. See Breaking → Exceptions.

Breaking

PGN model:

  • PgnHalfMove renamed to PgnMove.
  • PgnGame.halfMoveList() renamed to PgnGame.moves(), and PgnGame.tagList() renamed to PgnGame.tags() (record components and accessors) — domain plurals over the implementation-type *List suffix.

Board:

  • getPerformedHalfMoveCount() renamed to getPerformedMoveCount().
  • Removed Board(String fen). FEN strings now use the explicit factory pair: Board.fromFenStrict(String) for strict FEN and Board.fromFenLenient(String) for recovered/tolerant FEN. Board(Fen) remains for already-parsed FEN values. This makes FEN construction match the library's strict/lenient parser vocabulary instead of hiding strict parsing behind a String constructor.
  • Removed getHalfMoveList() and getLastHalfMove(); the played-move list is no longer exposed on Board. It is rebuilt inside the report layer, and PgnCreate.createPgnGame(board).moves() returns the played moves as PgnMoves.
  • HalfMove is gone from the public API (its replacement, MoveRecord, is package-private to report).
  • isUnwinnableQuick(Side) / isUnwinnableFull(Side) renamed to unwinnableQuick(Side) / unwinnableFull(Side), matching the UnwinnableQuickAnalyzer.unwinnableQuick(...) / UnwinnableFullAnalyzer.unwinnableFull(...) engines they delegate to. The Board methods return the verdict directly; the analyzers return the full analysis.
  • move(MoveSpecification), movesStrict(String...), and movesLenient(String...) now return void instead of boolean. The discarded return was always true (failure is signalled by exception), so it carried no information.
  • calculateFiftyMoveRuleClaimRights() / calculateThreefoldRepetitionRuleClaimRights() renamed to fiftyMoveRuleClaimRights() / threefoldRepetitionRuleClaimRights() — dropping the implementation-flavoured calculate prefix in favour of domain nouns, matching the unwinnableQuick / deadPositionQuick accessors.
  • Removed calculateInsufficientMaterial() and the four-way common.enums.InsufficientMaterial enum (NONE / WHITE_ONLY / BLACK_ONLY / BOTH). It was fully derivable from — and read confusingly beside — the side-specific booleans; use isInsufficientMaterial() and isInsufficientMaterial(Side).
  • Collection accessors use domain plurals instead of the implementation-type *List suffix, and the notation variants gain As: getPossibleMoveSpecificationList() → getLegalMoveSpecifications(), getPerformedMoveSpecificationList() → getPerformedMoveSpecifications(), getPerformedLegalMoveList() → getPerformedMoves(), getLegalMovesSan() → getLegalMovesAsSan(), getLegalMovesUci() → getLegalMovesAsUci(), and getSanList() → getPerformedMovesAsSan() (the performed-move SAN history, mirroring getLegalMovesAsSan()). getLegalMoves() is already a domain plural and is unchanged, as are the single-move getSan() / getLan().

Collection / *List naming (library-wide final pass):

  • SquareUtility.getPromotionRankList(Side) → getPromotionRankSquares(Side); SquareUtility.calculateEnPassantCaptureTargetSquareList(Side) → getEnPassantCaptureTargetSquares(Side).
  • UciMoveValidationUtility.getUciMoveList() → getUciMoves().
  • MoveSuffixAnnotation.calculateValueList(), ResultTagValue.calculateList(), and SetUpTagValue.calculateList() → allowedValuesText() — each returns the allowed-values text, not a list.
  • BitboardPositionUtility.toSquareSet(long) → toSquares(long) (returns ImmutableSet<Square> — the return type carries the set-ness, the name carries the domain).
  • MobilitySolution.getPiecePlacementSet() → getPiecePlacements().
  • Public collection constants also use domain names: ForgivenFenItem.EMPTY_LIST, ForgivenSanItem.EMPTY_LIST, and ForgivenTagItem.EMPTY_LIST → NO_ITEMS; TagUtility.SEVEN_TAG_ROSTER_TAG_LIST → SEVEN_TAG_ROSTER_TAGS.
  • ListUtility.calculateCommaSeparatedList / calculateSpaceSeparatedList / calculateLineSeparatedList → toCommaSeparatedString / toSpaceSeparatedString / toLineSeparatedString; ListUtility.calculateSquareList(Set<Square>) → formatSquares(...) — these return a String, so the List suffix was misleading.
  • Removed ImmutableUtility.constructListSquare(Square...) — a thin ImmutableList adapter redundant with Nulls.listOf(...); call sites use Nulls.listOf(...).

Kept deliberately: Nulls' Java-collection adapters (copyOfList, asList, subList, listOf, …), PgnCreate.toPgnLines, and the PGN parsers' parseLines(...) — there "list" / "lines" is the genuine Java-adapter or domain concept, not an implementation-type leak.

Reporter:

  • Reporter.printReport(...) renamed to Reporter.report(...) and now returns the report as ImmutableList<String> instead of writing to System.out; the caller decides where the lines go (e.g. Reporter.report(board).forEach(System.out::println)). Applies to all three overloads (Board, PGN String, PGN file). A library should not own the output stream.

Vocabulary — "side to move":

  • The non-standard havingMove vocabulary is replaced by the standard chess term. A position's side to move is sideToMove; the side that makes a move is movingSide (beside LegalMove.movingPiece()). Renames: Board.getHavingMove() → getSideToMove(); the Fen / DynamicPosition havingMove() component → sideToMove(); LegalMove.havingMove() → movingSide(); StrictFenSemanticValidationProblem.INVALID_HAVING_MOVE_RANGE → INVALID_SIDE_TO_MOVE_RANGE.

Boolean accessors (JavaBeans is / has idiom, no get prefix):

  • Side.getIsWhite() / getIsBlack() → isWhite() / isBlack().
  • `Castling...
Read more

18.1.0

Choose a tag to compare

@dlbbld dlbbld released this 10 Jun 23:03
1e49c50

Adjudication API

Adds a public adjudication API for the two external game-ending events - flag-fall and resignation - that applies the FIDE "opponent cannot win" draw exception for you, and changes the fifty-move report to count in moves by each player rather than halfmoves. The release is additive: no public type changes shape.

Notable

  • New io.github.dlbbld.ashlarchess.adjudication package. Adjudicator adjudicates a flag-fall (FIDE 6.9) or resignation (FIDE 5.1.2): the event is a loss unless the would-be winner cannot checkmate by any series of legal moves, in which case it is a draw. Each event has a quick variant (rules DRAW / LOSS from the fast analyzer - bounded latency, for live play) and a full variant (adds UNDETERMINED from the complete analyzer, for game-end and offline use). Verdicts are the new AdjudicationResult enum (DRAW, LOSS, UNDETERMINED).
  • The README is now generated from README.template.md plus executed example source: every shown snippet provably compiles and every shown output is captured from running it, and a drift test fails the build if the committed README does not match a fresh render.

Behavioral

  • The fifty-move report (Reporter) now expresses every no-progress run's counts as moves by each player, written (White/Black), instead of a single halfmove count - matching the FIDE "moves by each player" wording (claimable at 50/50, automatic draw at 75/75). Consumers that snapshot or parse the printed report text will see the new format.

Internal

  • Compiler-warning cleanup: reduced the visibility of package-internal helpers (including two InsufficientMaterialUtility bishop predicates) and supplied an explicit charset to IoUtility's stream-to-string conversion.

18.0.0

Choose a tag to compare

@dlbbld dlbbld released this 04 Jun 21:03
0e30ba6

Endgame theorem and unwinnability API

Breaking release. Adds a proved basic-helpmate-existence theorem to the full unwinnability analysis, realigns the quick analyzer to a faithful two-valued port of CHA 2.6.1, and simplifies the dead-position / game-end API.

Notable

  • Basic-helpmate-existence theorem. For KRvK, KQvK, KBBvK (opposite bishops), KBNvK, KRvKB, and KRvKN (and colour-reversed), unwinnableFull decides winnability directly from a finite-state theorem instead of the helpmate search - faster and always decisive on these classes. A theorem-decided win reports the new WINNABLE_BY_THEOREM verdict (no mate line).
  • Quick analyzer is now a faithful port of CHA 2.6.1 quick_analysis - two-valued, proves unwinnability or leaves it open, never claims winnability.

Breaking

  • UnwinnabilityQuickVerdict becomes { UNWINNABLE, POSSIBLY_WINNABLE } (drops WINNABLE); UnwinnabilityQuickAnalysis is verdict-only (drops mateLine).
  • UnwinnabilityFullVerdict.WINNABLE splits into WINNABLE_HELPMATE (mate line present) and WINNABLE_BY_THEOREM (no line); isWinnable() covers both.
  • Dead-position queries move off Board to UnwinnableQuickAnalyzer.unwinnableQuick(board) / UnwinnableFullAnalyzer.unwinnableFull(board) (reuse the verdict enums; UNWINNABLE = dead). Removed Board.isDeadPosition()/isDeadPositionQuick()/isDeadPositionFull() and the DeadPositionQuick / DeadPositionFull enums.
  • Removed GameEndFacts, Board.calculateGameEndFacts(), and Board.isGameEnd(). Query the Board predicates and BasicChessUtility.calculateOutcome(board) directly.

Internal

  • Fixed a latent HelpmateSearchBoard buffer overflow on searches past 128 plies.
  • Regenerated the Ambrona unwinnability oracle from CHA 2.6.1.
  • Rewrote the Score (FUN22 Fig 12) loser branch to CHA's accumulator; added Score / Going-to-corner spec tests.

Full changelog: https://github.com/dlbbld/ashlar-chess/blob/main/CHANGELOG.md

17.0.0

Choose a tag to compare

@dlbbld dlbbld released this 29 May 19:56
881e56c

First release on Maven Central.

<dependency>
  <groupId>io.github.dlbbld</groupId>
  <artifactId>ashlar-chess</artifactId>
  <version>17.0.0</version>
</dependency>

Moved from JitPack (com.github.dlbbld:clean-chess) to Maven Central (io.github.dlbbld:ashlar-chess); the base package is now io.github.dlbbld.ashlarchess. Requires Java 17+.

See the CHANGELOG for full details.