Releases: lynx-chess/Lynx
v1.11.0
β New features
- DFRC support (#2046)
π Search
- SE: low depth extension (#1796)
- SE: avoid correcting on verification (#1844)
- History: move to
x^2 + x +cformula, split between between bonus & malus + tuning (#1818) - History: don't update on low depths (#1876)
- Major correction history / corrhist - major key revival, weight 150 (#1912)
- NMP: add
staticEval - betamargin >= 30 (#1918) - Corrplexity (#1923)
- Remove TT score usage as static eval (#1971)
- Use TT score as static eval for RFP (#1973)
- Use TT static eval in QSearch (#2017)
- Use TT score as static eval for NMP (#2055)
- Allow static eval correction and improving calculation on pv nodes (#1999)
- Threat history (#2020)
- IIR min depth: 4 -> 5 (#2035)
- RFP: max depth 7 -> 9 (#1842)
- Razoring simplification: no need to run QSearch when tt corrected eval (#2039)
- SPSA 2025-5-10 - including TM tuning (#1955)
βοΈ Evaluation
- Threats: piece threats, simplest impl (no defenders) (#1817)
- Threats: defended piece threats - defended by pawns (#1820)
- Threats: king threats (#1822)
- Backwards pawn: (blocked or can't push) and undefended (#1994)
- King ring attacks - linear, excluding king square (#2067)
- Total king ring attacks (#2079)
- King ring total attacks: add pawns two squares ahead of the king when 1st and 8th rank (#2081)
- Endgames: OCB - eval/=2 (#1875)
- Endgames: scale down bishop & A/H pawns (#1879)
- Pawnless endgames: scale down R vs minor and 2 minors vs 1 (#1877)
- Pawnless endgames: scale down RB and RN vs R (#1878)
- Pawnless endgames: scale down R vs R and R vs 2 minors (#1880)
- Mobility: only exclude same side pawns, instead of all pieces (#2063)
- King mobility, restricting only opponent pawn attacks (#2069)
- Isolated pawns: split by file (#1988)
- Rook open/semiopen bonus penalty: split by file (#1992)
- Safe/unsafe checks: split existing checks (#1823)
- King open/semiopen bonus-penalty: split by file and king bucket (#2001)
- Rook open/semiopen bonus: split by kings buckets (#2002)
- Simplify
PieceProtectedByPawnBonus, removing buckets (#1855) - [DFRC] Cornered bishop penalty (#2057)
- [DFRC] Cornered bishop + blocked pawn (#2073)
- 777777 -> 1M Ethereal FRC data (#2053)
- 0.5M Clockwork 2.1 data (#2090)
- 2M Sirius 40ppg data (#2147)
β‘Speedups
- GC mode
Server->Workstation(#1718) - Use
array.AsSpan().Clear()for small arrays (#1835) - Don't accept
EvaluationContextasref readonlyparameter (#1933, #2040) - Refactor TT proving to return a boolean, an out-return a
TTProbeResultref readonly struct (#1984) - Span:
ref + Unsafe.Add()(#2112) - Only copy the needed items during
Positionclone constructor (#1803) - Castling free squares using presaved bitboards (#1816)
ScoreMove: score quiets first, as they should be more common (#1831)- Pawn movegen: improve branching and
doublePushSquarecalculation (#1861, #1863) - Pawn movegen: incremental promo encoding + simplification (#1862, #1864, #1866, #1867)
- Movegen: kings - don't generate pseudolegal king moves that land in an attacked square attacked (#1894, #1896)
- Movegen: reuse attacks and occupancy arrays (#1895, #1897)
- Move encoding: Remove
IsCaptureflag (#1929) - Use mask to check if a piece is a minor (#1910)
- FEN parsing: huge en-passant optimization (#2075)
- FEN parsing: avoid duplicated occupancy population (#1972)
Positionspeedups (#2109, #2110)- Replace every pair of friend-enemy PQST() calls by a single one (#2150)
- Reduce indirections and other minor improvements (#1806, #2095)
π Bugfixes
- Fix engine stalls/loses on time on low
movestogo(#2050) - Avoid too shallow searches when stopping on mate (#2130)
- Ensure TT clearing on first
ucinewgame, regardless of previousgocommands (#1978) - Correction history: use node type instead of TT entry type for update checks (#1909)
- TT replacement policy: empty entries (#2010, #2012, #2013)
- TT replacement: on collision (#2014)
- TT static eval saving: depth overflow (#2011)
- Don't correct TT 'no-scores' (#2016)
Non-strength winning changes
- Add
MoveOverheadUCI option (#1809) - Make UCI booleans lowercase (#2060)
- Add support for positions with more than 250 pseudolegal moves (#1928)
- Allow positions with >8 pawns (#1810)
- Show half-move counter when printing position (#2059)
Full Changelog: v1.10.0...v1.11.0
v1.10.0
π Search
- Correction history:
- Pawn correction history (#1662, #1663, )
- Non-pawn correction history (#1677, #1678)
- Minor correction history (#1702, #1709, #1714)
- Correction history: don't save corrected eval to TT (#1671)
- Correction history: add missing eval correction in PVnodes (#1676)
- Correction history: remove QS double eval correction, and the mysterious extra scaling required to pass nonpawncorrhist (#1708)
- Correction history weights + SPSA 2024-05-05 (#1717)
- Singular extensions:
- Singular Extensions (SE) (#1731)
- SE: double extension (margin 15) (#1742)
- SE: negative extension (#1743)
- SE: multicut with
singularScore(#1751) - SE: Don't use depth extensions (#1732)
- SE: don't do check extensions when verifying SE (#1737)
- SE: limit
ply < 3 * depthand no mate scores (#1768) - SE: limit double extensions: not after
doubleExtensions[ply] >= 6(#1777) - SE: Multicut limit
singularScorenot mate/mated score (#1761)
- Store static eval early in TT (#1561)
- Include 50mr counter in Zobrist key - only when probing/saving TT, half moves 20 - 10 (#1724)
- NMP: don't return mate scores (#1790)
- LMP: remove depth limit (#1551)
- Killers: remove 3rd (#1554)
- Continuation history: use it in LMR and history pruning (#1557)
- Post-LMR contHist update - score > alpha (#1758)
- LMR: quiet (#1602)
- LMR: penalize root moves requiring more visited moves (#1774)
- Add ply & depth condition for TT non-cutoffs extension condition (#1673)
- Update capture history in Qsearch as if in depth 3 (#1722)
- SPSA (#1789, #1801)
π° Multithreaded search
π§ Pondering
- On
ponderhitsettle for initial search when time is low and a min depth is reached (#1609) - Fix time losses on fast
ponderhit(#1613) - Fix illegal move during pondering on too fast
ponderhit(40fc9c2) - Avoid Premature
bestmovewhile pondering (#1648)
β‘ Speedups
- Memoize quiet history calculation (#1558)
- Replace FileMasks[] access with
Constants.AFile << (square % 8)(#1565) - Pawn movegen: Improve branching on double pawn push (#1579)
- Add some optimizations to
SearchResult.ToString()(#1659) - Avoid unnecessary GameState key copies during null moves (#1697)
- Split
GameStatebetween regular and null moves and passPositioninstance to populate them (#1705) - Add private fields to
Positionand use them internally instead of properties (#1712) - Read
Game._stackonce (#1692) - Reduce nlog allocations (#1658)
- Castling movegen methods micro-optimizations (#1793)
π Bugfixes
- Fix LMR clamping (#1594)
- Shallower: use
newDepth(#1729) - Apply quiet history malus to last searched move (#1756)
Non-strength winning changes
- Don't defer
ThreadsandHashupdate until 'ucinewgame' (#1655) - Implement TT 'multithreaded initialization', aka clearing (#1617)
- Double max supported moves per game to 1024 (#1586)
- Add option to estimate multithreaded-search NPS (#1682, #1695)
- Add
global.json(#1643) - Add
appsettings.tournament.jsonas highest priority config file (#1726) - Add Makefile support for Windows ARM (#1780)
- Introduce development versioning (#1641)
Full Changelog: v1.9.1...v1.10.0
v1.9.1
ππ§ Pondering bugfixes
Note for testers
These bugfixes only affect pondering logic.
Unless you're specifically using pondering in your testing, this version behaves exactly the same as v1.9.0 (so no need to re-test).
Full Changelog: v1.9.0...v1.9.1
v1.9.0
π Search
- Add PVS SEE pruning (#1521)
- LMR: reduce more on TT capture when current move isn't (#1529)
- LMR: reduce more on no TT PV (#1476)
- LMR: deeper/shallower (#1535)
- LMR: fractional w/ quantised base (#1514)
- LMR: split base and history factor in quiet and noisy (#1512)
- QSearch: don't use TT static eval (#1312)
- QSearch: reuse TT score instead of TT static eval in QSearch when possible (#1319)
- QSearch: don't do standing pat when in check (#1389)
- Always predict
cutnodes in ZWS with reduction, even if the reduction is 0 (#1304) - Extend on TT hit at low depths when TT cutoff doesn't happen but TT depth > depth (#1342)
- Replace first legal move with
BestMoveRoot()method when search depth 1 isn't completed (#1449) - Countermoves update: add
pvNode || depth >= 3condition (#1456) - IIR: on
!ttHit-> on!ttHit || !ttMove(#1516) - Use final eval when available (checkmate, stalemate) as cached TT static eval (#1288)
- SPSA (#1405, #1537)
βοΈ Evaluation
- Pieces protected by pawns -> bucketed array, indexed by piece (#1324, #1336)
- Passed pawns -> opponent buckets to passed pawns bucketed arrays (#1349)
- Bishop major threats (#1415, #1420)
- Bishop in non-blocked long diagonal (#1439)
- Pawn islands (#1431)
- Connected rooks (#1436)
- Incremental static evaluation for PSQTs (#1350, #1353)
- Pawn structure eval table (#1365)
- Incremental phase (#1412)
β‘Speedups
- Split
ScoreMovebetween regular search and qsearch (#1410) - Remove
Engine.StopSearchingandEngine_stopRequested(#1296) - Use
in readonly structwhen passing args, when possible (#1462) - Precalculated pawn islands (#1440)
- Remove passed pawns double array (#1491)
- Add inline hints to
TTElementgetters (#1513) - Use explicit
SEE.IsGoodCapture()method when we know it's a capture (#1528) - Replace
GetLS1BIndex()+ResetLS1B()loop calls with aWithoutLS1B()method that 'outs' the index (#1536)
β Time management
- Don't search for shorter mates when low on time (#1398)
π° Multithreaded search
- Replace
Task.WhenAllwithTask.WhenEach, processing faster results from extra threads (#1545)
π Bugfixes
- Engine doesn't becomes unresponsible any more when number of
Threadsgets close or over the logical cores of the machine (#1292) - Fix crash when multithreading and pondering when GUI doesn't wait for
bestmoveafter astop(#1394) - Fix cutechess warnings when pondering by avoiding single-move early returns while pondering (#1395)
- Use TT recalculated scores everywhere and move TT cutoffs to NegaMax and QSearch methods (#1310)
- Distinguish side on initial Zobrist key calculation (#1363)
- Ensure that
hashfullcalculation doesn't fail if TT < 1k items (even if not currently possible) (#1393)
Non-strength winning changes
- Make engine <-> GUI time overhead configurable (via
EngineGuiCommunicationTimeOverhead) (#1442) - Refuse to parse FENs without (or with more than) one white and one black king (#1351)
- Don't stop when a short-enough mate is found, continue trying to find a faster one (#1287)
- Make single move score more recognizable (#1461)
- Replace first legal move with BestMoveRoot() method when search depth 1 isn't completed (#1449)
- Plan for whenever ChessGUI wakes up feeling like sending negative
movestogo(#1519)
Full Changelog: v1.8.0...v1.9.0
v1.8.0
π Search
- Improving: LMP (#1129)
- Improving: RFP (#1130, #1133)
- Improving: LMR (#1135)
- NMP, tweak reduction using eval - beta (#1139)
- NMP: Use the right score for TT condition (#1268)
- LMR: reduce more on cutnode (#1233)
- LMR: avoid when being checkmated (#1231)
- LMR: increase pv min moves 2 (#1230)
- History:
History_BestScoreBetaMargin80 -> 60 (#1118) - History: increase bonus when best score is over beta by some margin (#1110)
- History: increase bonus when static eval is lower than alpha (#1123)
- Save static eval in TT (#1084, #1085)
- Aspiration windows: fail high reduction (#800, #1285)
β Time management
- Add node time management (#1203, #1206)
- Add best move stability (#1211)
- Add score stability (#1223)
- Soft limit <= hard limit (#1210)
β‘ Speedups
- Move to .NET 9 (#1108)
- Don't attempt continuation history on root moves (#1065)
- Use
Unsafe.AddforPSQT()(#1153) - Make
TaperedEvaluationTermfields constant (#1174) - Make TT (wrapper) a readonly struct (#1200)
- Flatten killer moves array (and stack-allocate it) (#1247)
- Minimal speedup in
goUCI command parsing (#1264)
π Bug fixes
- Clear
PlyStackEntryshared array on return (#1182) - Don't allow the search to stop if no best move is found (#1251)
- IDDFS finishes some depths with no moves (#1266)
- Incorrect (too negative) mate scores when being checkmated (#1271)
- Make sure
staticEvalvar always gets initialized as part of the search (#1272) - Prevent aspiration windows to go outside of [MinEval, MaxEval] after window overflow (#1275)
π° Multithreaded search
-
Add support for multi-threaded search: basic lazy SMP implementation (#1263)
Threads # 1 2 4 8 ELO @ 8+0.08 - +100.96 +195.65 +263.42 ELO @ 40+0.4 - +83.35 +167.44 +220.54 NPS 1.21 Mnps 2.47 Mnps 4.88 Mnps 10.07 Mnps More detailed results can be found here.
Non strength-winning changes
- Allow non-power of two Hash sizes, implemented via 'fixed-point multiplication trick' (#1072)
- Improve and standardize
npsreporting (#1081) - Use the total max ply as
selfdepthvalue instead of last search's max ply (#1289) - Make
benchquiet by default, and addverbosebench(#1286) - Various big refactorings to accommodate multithreaded search (#1147, #1184, #1201, #1262)
Full Changelog: v1.7.0...v1.8.0
v1.7.0
π Search
- Regular search: fail hard -> fail soft (#1039, #1040, #1041)
- QSearch: fail hard -> fail soft (#1052)
- Fail soft TT cutoffs (#1044)
- Remove
pvNodecondition for first move full search (#1045) - History pruning: quiet history (#972)
- Improve queen promotion with capture move ordering (#1061)
- Make
TranspositionTableElement.Keyanushortinstead of ashort(#1070) - SPSA 2024-10-1 (#1074)
βοΈ Evaluation
- Enemy king related PSQTs (#924)
- Friendly and enemy king distance to passed pawn (#955)
- Passed pawns: bonus for not enemy pieces ahead (#998, #1008)
- Pawn phalanx (#1009, #1010)
- Bishop penalty: same color pawns (#1022)
- Bishop penalty: blocked central pawns (#1029)
- Checks (#1027)
- Mobility: exclude squares attacked by opponent's pawn (#958)
- Add 50 moves rule scaling, down to 50% of the score (#965)
- Improve endgame scaling with pawn count (#928)
- Bucketed passed pawns (#945)
- Index queen mobility bonus by attacks count excluding own pieces (#774)
- Tuning: use some Ethereal FRC data (#916)
- Tuning: tune at 5k epochs (50k epochs -> 10k epochs -> 5k) epochs (#1031)
β Time management
- Use expected moves to go (#996)
β‘ Speedups
- Move
Moveserialization toWriterthread (#999) - Add a
Boardarray toPositionto track where pieces are indexed by square (#849) - Remove good old
_isFollowingPVand_isScoringPV(#1034) - Make
TaperedEvaluationTerman integer I (#935) - Add PSQT class to store
PackedPSQT(#939) - Flatten PSQTs [][][][] (#927)
- Flatten capture history (#870)
- Pin 1 dimension arrays (#953)
- Pin attack-related arrays (#985)
- Reverse killer moves arrays (#861)
- Replace Chebyshev distance calculation with lookup table (double array) (#957)
- Refactor additional evaluations: pass piece side (#963)
- Speedup
InfoCommand.SearchResultInfo(#984) - Make
SearchResult.Movesan array and optimize its population (#986) - Refactor
Game.PositionHashHistoryinto a private array (#991) - Remove
Position.MakeMoveCalculatingCapturedPiece, usingPosition.Boardinstead (#1021) - Only update PV table on PV nodes (#1042)
π§ Memory usage (!)
- Remove unnecessary, initial TT initializations (#989)
- Allocate TT only once, clearing it afterwards on
ucinewgame(#990) - Avoid
static readonlyflat array initial allocations for inline arrays (#948) - Use
ArrayPoolto reduce recurrent allocations (#983) - Stop generating
logs/log-β.logfiles by default (#1004) - Cache
Move.UCIStringresults in aDictionary(#1001) - Optimize
gocommand parsing (#1005) - Remove unused props from
SearchResultand re-order the ones left (#1006)
π Bug fixes
- Don't prune moves in regular search while being checkmated (#1060)
- Prevent negative checkmate scores from being lower than
EvaluationConstants.MinEval(#1063)
Non-strength winning changes:
Relevant for testers:
- By default log files are no longer generated under
logs/dir unless warnings or errors happen (#1004) - Lynx process' memory usage won't skyrocket to twice the expected (TT) value anymore, as it could briefly happen in the past, providing there was such memory available for it.
- Simplify
appsettings.json(#1075)
Relevant for developers that consume the NuGet package:
- Lynx allocates way less than before when searching, which implies much lower GC pressure.
- You have control now of UCI
infoandbestmovestring allocations if you're usingSearcherclass to interact with the engine, since now the channel doesn't send the information pre-serialized (details below). - API changes:
Channel<string>->Channel<object>, you're now expected to invoke.ToString()on whatever comes from the channel to print it. Alternatively, you can just consume it by checking object types (they are either strings,Lynx.Model.SearchResultorLynx.UCI.Commands.Engine.BestMoveCommand) (#999)- Remove
Position(Position, Move)constructors, now you're forced to useMakeMove/UnMakeMovemethods (#976) - Remove parameterless
Gameconstructor, a fen or a parsing result is always required now (tip:Constants.InitialPositionFENcan be used) (#980) - Make
Position.UniqueIdentifieranulonginstead of along(#1078)
Full Changelog: v1.6.0...v1.7.0
v1.6.0
- π Countermoves (#859)
- π Continuation history - countermove history (1 ply) (#645)
- π Don't always stop search when a mate is found (#827)
- π SPSA 2024-6-27 (#839)
- βοΈ King-bucketed PSQTs (#873 (2) -> #876 (8) -> #879 + #888 (16) -> #893 (24) -> #902 (23))
- βοΈ Escale endgame eval with pawn count (#821, #829)
- βοΈ Give bonus to pieces protected by friendly pawns and penalty to pieces attacked by opponent pawns (#830)
- βοΈ Use some Pedantic data for HCE tuning (#905)
- β‘ Avoid
stackalloclocal initialization when allocating it for movegen (#858) - β‘ Optimize
MoveGenerator.GeneratePieceCaptures()(#846) - β‘ Micro-optimization in
MoveGenerator.IsAnyPieceMoveValid(#845)
Non strength-winning changes:
- βοΈ Move eval parameters out of
Configurationclass, making them no longer configurable viaappsettings.json(#889, #911) - π Make
.ToEPDString()fully PGN/EPD compliant (#841) - π Avoid node count overflow (#835)
Full Changelog: v1.5.1...v1.6.0
v1.5.1
Full Changelog: v1.5.0...v1.5.1
v1.5.0
- π Add Futility pruning (FP) (#733)
- π LMR: allow when in check (#702)
- π LMR: reduce more if there's a TT move and a capture (#706)
- π Move RFP before NMP (#732)
- π SPSA search parameters tuning (#730, #764)
- βοΈ Remove double pawns penalty [proper SPRT pawn eval] (#746)
- βοΈ Index bishop mobility bonus by attacks count (#758)
- βοΈ Index rook mobility bonus by attacks count exluding own pieces (#768)
- βοΈ Add knight mobility bonus and index it by attacks count excluding own pieces (#775)
- βοΈ Take only pawns into account for king shield (#789)
- βοΈ Add king virtual mobility indexed by mobility count (#785)
- βοΈ Use some Stoofvlees quiet data for eval tuning (#710)
- β‘ Improve search logic (#725)
- β‘ Use optimized method to check if a move was valid (#716)
- β‘ Use a
StringBuilderto generate UCIinfocommand (#805) - β‘ Remove option to disable TT (#720)
- β‘ Remove manual piece count during static eval (#698)
- β‘ Simplify
TaperedEvaluationTermByRank(#757) - β‘ Simplify
TaperedEvaluationTermByCount(#767) - β‘ Simplify Aspiration windows (#802)
- π Fix index out of range exception on max depth (#708)
- π Detect threefold repetition on
pvNode(#796) - π Fix
Game.MakeMovebehavior on invalid moves (#804)
Non strength-winning changes:
- Add ponder support (#772)
- Generate UCI options for search parameters dynamically (#734)
- Normalize mobility values (#788)
Full Changelog: v1.4.0...v1.5.0
v1.4.0
- π Improve RFP (#652)
- π Avoid doing TT cutoffs on PV nodes (#653)
- π Use TT score as positional eval for pruning (#692)
- β Tweak pawnless endgames evaluation (#693)
- β Tweak time management (#664, #665, #667, #668, #671, #677, #691)
- β‘ Stop checking for two/threefold repetition and 50 moves draws in QSearch (#673)
- β‘ Refactor
Update50movesRule()method (#678) - β‘ Reimplement repetition detection (#679)
- β‘ Prefetch TT entry in NegaMax search (#681)
- β‘ Remove
Position.StaticEval()heap allocations (#683) - β‘ Force GC collection at the end of
Engineconstructor and afterucinewgame(#685) - β‘ Use packed evaluation (#697)
- π Clear history on
newgame(#649) - π Fix long input
positioncommands parsing (#650) - π Fix engine stall when depth over 100 is reached during search (#651)
- π Don't search with fixed depth when cutechess provides 0s to move (#654)
- π Prevent illegal moves when low in time (#657)
- π Error when searching at max depth (#670)
Non strength-winning changes:
- Add
fenUCI command (#688 - Increase max TT size from 1GB to 8GB (#669)
- π Fix behavior of consecutive
gocommands (#655)
Full Changelog: v1.3.0...v1.4.0
