Skip to content

Releases: lynx-chess/Lynx

v1.10.0

29 Jun 21:34
Compare
Choose a tag to compare

πŸ” 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 * depth and no mate scores (#1768)
    • SE: limit double extensions: not after doubleExtensions[ply] >= 6 (#1777)
    • SE: Multicut limit singularScore not 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 ponderhit settle 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 bestmove while 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 GameState between regular and null moves and pass Position instance to populate them (#1705)
  • Add private fields to Position and use them internally instead of properties (#1712)
  • Read Game._stack once (#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 Threads and Hash update 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.json as 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

05 Apr 00:43
Compare
Choose a tag to compare

πŸ›πŸ§  Pondering bugfixes

  • Fix illegal moves on fast ponderhit (#1610)
  • Fix time losses on fast ponderhit (#1614)

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

11 Mar 21:47
Compare
Choose a tag to compare

πŸ” 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 >= 3 condition (#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 ScoreMove between regular search and qsearch (#1410)
  • Remove Engine.StopSearching and Engine_stopRequested (#1296)
  • Use in readonly struct when passing args, when possible (#1462)
  • Precalculated pawn islands (#1440)
  • Remove passed pawns double array (#1491)
  • Add inline hints to TTElement getters (#1513)
  • Use explicit SEE.IsGoodCapture() method when we know it's a capture (#1528)
  • Replace GetLS1BIndex() + ResetLS1B() loop calls with a WithoutLS1B() method that 'outs' the index (#1536)

βŒ› Time management

  • Don't search for shorter mates when low on time (#1398)

🟰 Multithreaded search

  • Replace Task.WhenAll with Task.WhenEach, processing faster results from extra threads (#1545)

πŸ› Bugfixes

  • Engine doesn't becomes unresponsible any more when number of Threads gets close or over the logical cores of the machine (#1292)
  • Fix crash when multithreading and pondering when GUI doesn't wait for bestmove after a stop (#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 hashfull calculation 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

20 Dec 17:15
Compare
Choose a tag to compare

πŸ” 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_BestScoreBetaMargin 80 -> 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.Add for PSQT() (#1153)
  • Make TaperedEvaluationTerm fields constant (#1174)
  • Make TT (wrapper) a readonly struct (#1200)
  • Flatten killer moves array (and stack-allocate it) (#1247)
  • Minimal speedup in go UCI command parsing (#1264)

πŸ› Bug fixes

  • Clear PlyStackEntry shared 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 staticEval var 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

    Image

    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 nps reporting (#1081)
  • Use the total max ply as selfdepth value instead of last search's max ply (#1289)
  • Make bench quiet by default, and add verbosebench (#1286)
  • Various big refactorings to accommodate multithreaded search (#1147, #1184, #1201, #1262)

Full Changelog: v1.7.0...v1.8.0

v1.7.0

04 Oct 22:18
Compare
Choose a tag to compare

πŸ” Search

  • Regular search: fail hard -> fail soft (#1039, #1040, #1041)
  • QSearch: fail hard -> fail soft (#1052)
  • Fail soft TT cutoffs (#1044)
  • Remove pvNode condition for first move full search (#1045)
  • History pruning: quiet history (#972)
  • Improve queen promotion with capture move ordering (#1061)
  • Make TranspositionTableElement.Key an ushort instead of a short (#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 Move serialization to Writer thread (#999)
  • Add a Board array to Position to track where pieces are indexed by square (#849)
  • Remove good old _isFollowingPV and _isScoringPV (#1034)
  • Make TaperedEvaluationTerm an 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.Moves an array and optimize its population (#986)
  • Refactor Game.PositionHashHistory into a private array (#991)
  • Remove Position.MakeMoveCalculatingCapturedPiece, using Position.Board instead (#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 readonly flat array initial allocations for inline arrays (#948)
  • Use ArrayPool to reduce recurrent allocations (#983)
  • Stop generating logs/log-βˆ—.log files by default (#1004)
  • Cache Move.UCIString results in a Dictionary(#1001)
  • Optimize go command parsing (#1005)
  • Remove unused props from SearchResult and 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 info and bestmove string allocations if you're using Searcher class 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.SearchResult or Lynx.UCI.Commands.Engine.BestMoveCommand) (#999)
    • Remove Position(Position, Move) constructors, now you're forced to use MakeMove/UnMakeMove methods (#976)
    • Remove parameterless Game constructor, a fen or a parsing result is always required now (tip: Constants.InitialPositionFEN can be used) (#980)
    • Make Position.UniqueIdentifier an ulong instead of a long (#1078)

Full Changelog: v1.6.0...v1.7.0

v1.6.0

15 Aug 00:18
Compare
Choose a tag to compare
  • πŸ” 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 stackalloc local 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 Configuration class, making them no longer configurable via appsettings.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

21 Jun 09:39
Compare
Choose a tag to compare
  • πŸ› Threefold repetition bugfix: Revert "Detect threefold repetition on pvNode (#796)" (#818)

Full Changelog: v1.5.0...v1.5.1

v1.5.0

09 Jun 00:16
Compare
Choose a tag to compare
  • πŸ” 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 StringBuilder to generate UCI info command (#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.MakeMove behavior 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

21 Mar 11:38
Compare
Choose a tag to compare
  • πŸ” 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 Engine constructor and after ucinewgame (#685)
  • ⚑ Use packed evaluation (#697)
  • πŸ› Clear history on newgame (#649)
  • πŸ› Fix long input position commands 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 fen UCI command (#688
  • Increase max TT size from 1GB to 8GB (#669)
  • πŸ› Fix behavior of consecutive go commands (#655)

Full Changelog: v1.3.0...v1.4.0

v1.3.0

04 Feb 22:17
Compare
Choose a tag to compare
  • πŸ” Add basic (quiet) history malus/penalty (#610)
  • πŸ” Add capture history (#634)
  • πŸ” Update (quiet) history moves only in beta cutoffs (#608)
  • πŸ” Stop clearing quiet history (#637)
  • πŸ” Take quiet history into consideration for LMR (#613)
  • ⚑ Refactor move encoding methods and stop encoding special move flags individually (#622)
  • ⚑ Store captured pieces as part of the move (#604)
  • ⚑ Use jagged arrays ([][]) instead of multidimensional ones ([,]) (#605, #606, #607)
  • ⚑ Simplify triple repetition detection logic, removing some branching (#623)
  • ⚑ Make Piece an integer enum (#603)

Full Changelog: v1.2.0...v1.3.0