Repository navigation
Releases: dlbbld/ashlar-chess
Release list
22.1.0
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
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.
PgnMovestoresList<Nag>instead of a storedMoveSuffixAnnotation; the six suffix glyphs (!,?,!!,??,!?,?!) are parsed as NAG codes1-6.PgnMove.moveSuffixAnnotation()remains as a derived read convenience for the first assessment NAG, but code that constructs or inspects moves should usenags(). UnwinnabilityQuickVerdictgainsWINNABLE(paper Figure 10 is three-valued): the quick analysis now proves winnability on quickly matable positions instead of answeringPOSSIBLY_WINNABLE. Exhaustive switches over the enum need a new case; comparisons againstUNWINNABLE(the adjudication use) are unaffected.WinnableProofis removed andUnwinnabilityFullAnalysisis now(verdict, mateLine): the basic-helpmate-existence theorem no longer short-circuits the full analyzer (it lives on as a test-side oracle), so everyWINNABLEverdict is search-proven andmateLine()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$Ntokens 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$Nsequence, 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_analysisheuristics. Some verdicts move betweenUNWINNABLEandPOSSIBLY_WINNABLEin 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
UNDETERMINEDwhere the old code could in principle over-claimUNWINNABLE. - 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:
UNWINNABLEwherever 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 provesUNWINNABLEby 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
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
unwinnabilitypackage-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 Rustchasolver. 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 - thegithub.com/miguel-ambrona/D3-ChessURL now redirects to the newer Rustchasolver, 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
UNDETERMINEDare now provenWINNABLE"). The search already proved these classesWINNABLE; 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 analyzerUNWINNABLE- 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
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.
UnwinnableSemiStaticno longer applies an unsound shortcut that dropped a limited pawn from the semi-static visitor set, which could over-claimUNWINNABLEon 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 reportedUNDETERMINEDrather thanUNWINNABLE— sound, but less complete. AffectsBoard.unwinnableFull/Board.unwinnableQuickand theAdjudicator. - 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 isStrictFenSemanticValidationProblem.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
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.javaexports only the public API:board,board.enums,fen,fen.model,pgn,san,adjudication,report,unwinnability,exceptions,bitboard(BitboardPositionas 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/ImmutableMapreturn types are now JDKList/Set/Map. Drop Guava (and anyrequires 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,DynamicPositionnow import fromio.github.dlbbld.ashlarchess.board. The base exception hierarchy (ChessApiRuntimeException,UsageException,ProgrammingMistakeException,NonePointerException,FileSystemAccessException) is in the top-levelexceptionspackage. - Implementation types are now hidden (moved into non-exported packages such as the
*.internalsubpackages,moves,squares,analyze): the move-generation engine,CastlingUtility,SquareUtility/RankUtility, the SAN/LAN generatorsMoveToSan/MoveToLan,UpdateSquare,MoveNumberFormat, and theNulls/Messagehelpers, among others. Use the public API instead (see Added). - No forced logging or utility dependency —
commons-lang3andlog4jare no longer dependencies; internal logging uses the JDK'sjava.util.logging. (org.eclipse.jdt.annotationisrequires static transitive: optional and compile-only.) - Full unwinnability verdict collapsed back to the public domain answer —
UnwinnabilityFullVerdictexposesWINNABLE,UNWINNABLE,UNDETERMINED. The proof detail lives onUnwinnabilityFullAnalysis:winnableProof()returnsWinnableProof.THEOREM/HELPMATE/NONE, andmateLine()carries the concrete UCI line for searched wins.
Added
module-info.java— moduleio.github.dlbbld.ashlarchess.- Castling geometry on
CastlingMove—kingFromSquare(Side),kingToSquare(Side),rookFromSquare(Side),rookToSquare(Side)resolve the squares a castling move touches (a castlingMoveSpecificationstoresSquare.NONEfor from/to). MoveSpecification.isCastling()— companion to the existingisPromotion().LegalMoveconveniences —isCapture(),isCastling(),isPromotion(),isEnPassant(), andenPassantCapturedPawnSquare()(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-exportedinternalpackage; dead post-Guava code (ImmutableUtility) removed. - A module-boundary guard test fails the build if the exported package set or the javadoc
excludePackageNamesdrift from the intended public API.
19.1.0
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 derivedashlar.chessfrom the file name — an accidental name nobody chose.
Declaring it in the manifest locks the intentionalio.github.dlbbld.ashlarchess(mirroring the base
package) ahead of the explicitmodule-info.javaplanned 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-plugin0.7.0 → 0.11.0. The Central Portal API added awarningsfield
to its publish response that 0.7.0's response model could not parse, crashingmvn -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
javadocfails the-javadoc.jarbuild on a package containing nothing but apackage-info.java.
Internal only; no public type affected.
19.0.0
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.mddraws the line: records and enums hold data; computational, translation, and factory logic lives in dedicated*Utility/*Translatorclasses.BitboardPositionis 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 theFen*SymbolandPromotionPieceTypefactories to their enums and addedMoveSpecification.isPromotion(). It also dropped thecalculateIs/calculateHasprefix from boolean predicates (is/has), switched PGN export to theto*idiom, surfaced immutable collection types in signatures, and gave whole-position dead detection its ownDeadPositionAnalyzer. See the Breaking list. HalfMoveis retired. The played-move row is nowMoveRecord, rebuilt inside the report layer fromBoard's existing per-move accessors; the public PGN movetext type isPgnMove.- "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, thePlyCounttag) and the engine/search internals. - Core board enums print conventional chess notation.
Square.toString()now returnsa1..h8(not theA1constant name);File→a..h;Rank→1..8;Piece→ the FEN letter (P/p, ...);PieceType→P/N/B/R/Q/K(andnonefor theNONEsentinels). 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 coreboard.enumstypes (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 aLenientPgnParserValidationExceptionwith the newLenientPgnParserValidationProblem.MOVETEXT_TERMINATION_MARKER_INVALID; previously it leaked a rawIllegalArgumentExceptionout ofResultTagValue.parse. The strict parser already guarded this. (Found by the parser fuzz harness.) NonePointerExceptionis now aUsageExceptionrather than aProgrammingMistakeException, and the FEN/PGNvalidate(...)methods now let aProgrammingMistakeExceptionpropagate instead of masking it asUNKNOWN_ERROR. See Breaking → Exceptions.
Breaking
PGN model:
PgnHalfMoverenamed toPgnMove.PgnGame.halfMoveList()renamed toPgnGame.moves(), andPgnGame.tagList()renamed toPgnGame.tags()(record components and accessors) — domain plurals over the implementation-type*Listsuffix.
Board:
getPerformedHalfMoveCount()renamed togetPerformedMoveCount().- Removed
Board(String fen). FEN strings now use the explicit factory pair:Board.fromFenStrict(String)for strict FEN andBoard.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 aStringconstructor. - Removed
getHalfMoveList()andgetLastHalfMove(); the played-move list is no longer exposed onBoard. It is rebuilt inside thereportlayer, andPgnCreate.createPgnGame(board).moves()returns the played moves asPgnMoves. HalfMoveis gone from the public API (its replacement,MoveRecord, is package-private toreport).isUnwinnableQuick(Side)/isUnwinnableFull(Side)renamed tounwinnableQuick(Side)/unwinnableFull(Side), matching theUnwinnableQuickAnalyzer.unwinnableQuick(...)/UnwinnableFullAnalyzer.unwinnableFull(...)engines they delegate to. TheBoardmethods return the verdict directly; the analyzers return the full analysis.move(MoveSpecification),movesStrict(String...), andmovesLenient(String...)now returnvoidinstead ofboolean. The discarded return was alwaystrue(failure is signalled by exception), so it carried no information.calculateFiftyMoveRuleClaimRights()/calculateThreefoldRepetitionRuleClaimRights()renamed tofiftyMoveRuleClaimRights()/threefoldRepetitionRuleClaimRights()— dropping the implementation-flavouredcalculateprefix in favour of domain nouns, matching theunwinnableQuick/deadPositionQuickaccessors.- Removed
calculateInsufficientMaterial()and the four-waycommon.enums.InsufficientMaterialenum (NONE/WHITE_ONLY/BLACK_ONLY/BOTH). It was fully derivable from — and read confusingly beside — the side-specific booleans; useisInsufficientMaterial()andisInsufficientMaterial(Side). - Collection accessors use domain plurals instead of the implementation-type
*Listsuffix, and the notation variants gainAs:getPossibleMoveSpecificationList()→getLegalMoveSpecifications(),getPerformedMoveSpecificationList()→getPerformedMoveSpecifications(),getPerformedLegalMoveList()→getPerformedMoves(),getLegalMovesSan()→getLegalMovesAsSan(),getLegalMovesUci()→getLegalMovesAsUci(), andgetSanList()→getPerformedMovesAsSan()(the performed-move SAN history, mirroringgetLegalMovesAsSan()).getLegalMoves()is already a domain plural and is unchanged, as are the single-movegetSan()/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(), andSetUpTagValue.calculateList()→allowedValuesText()— each returns the allowed-values text, not a list.BitboardPositionUtility.toSquareSet(long)→toSquares(long)(returnsImmutableSet<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, andForgivenTagItem.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 aString, so theListsuffix was misleading.- Removed
ImmutableUtility.constructListSquare(Square...)— a thinImmutableListadapter redundant withNulls.listOf(...); call sites useNulls.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 toReporter.report(...)and now returns the report asImmutableList<String>instead of writing toSystem.out; the caller decides where the lines go (e.g.Reporter.report(board).forEach(System.out::println)). Applies to all three overloads (Board, PGNString, PGN file). A library should not own the output stream.
Vocabulary — "side to move":
- The non-standard
havingMovevocabulary is replaced by the standard chess term. A position's side to move issideToMove; the side that makes a move ismovingSide(besideLegalMove.movingPiece()). Renames:Board.getHavingMove()→getSideToMove(); theFen/DynamicPositionhavingMove()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...
18.1.0
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.adjudicationpackage.Adjudicatoradjudicates 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 (rulesDRAW/LOSSfrom the fast analyzer - bounded latency, for live play) and a full variant (addsUNDETERMINEDfrom the complete analyzer, for game-end and offline use). Verdicts are the newAdjudicationResultenum (DRAW,LOSS,UNDETERMINED). - The README is now generated from
README.template.mdplus 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 at50/50, automatic draw at75/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
InsufficientMaterialUtilitybishop predicates) and supplied an explicit charset toIoUtility's stream-to-string conversion.
18.0.0
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),
unwinnableFulldecides 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 newWINNABLE_BY_THEOREMverdict (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
UnwinnabilityQuickVerdictbecomes{ UNWINNABLE, POSSIBLY_WINNABLE }(dropsWINNABLE);UnwinnabilityQuickAnalysisis verdict-only (dropsmateLine).UnwinnabilityFullVerdict.WINNABLEsplits intoWINNABLE_HELPMATE(mate line present) andWINNABLE_BY_THEOREM(no line);isWinnable()covers both.- Dead-position queries move off
BoardtoUnwinnableQuickAnalyzer.unwinnableQuick(board)/UnwinnableFullAnalyzer.unwinnableFull(board)(reuse the verdict enums;UNWINNABLE= dead). RemovedBoard.isDeadPosition()/isDeadPositionQuick()/isDeadPositionFull()and theDeadPositionQuick/DeadPositionFullenums. - Removed
GameEndFacts,Board.calculateGameEndFacts(), andBoard.isGameEnd(). Query theBoardpredicates andBasicChessUtility.calculateOutcome(board)directly.
Internal
- Fixed a latent
HelpmateSearchBoardbuffer 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; addedScore/ Going-to-corner spec tests.
Full changelog: https://github.com/dlbbld/ashlar-chess/blob/main/CHANGELOG.md
17.0.0
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.