Releases: lynx-chess/Lynx
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 * 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 passPosition
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
andHash
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
ππ§ 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
cutnode
s 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
andEngine_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 aWithoutLS1B()
method that 'outs' the index (#1536)
β Time management
- Don't search for shorter mates when low on time (#1398)
π° Multithreaded search
- Replace
Task.WhenAll
withTask.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 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
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
π 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
forPSQT()
(#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 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 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
pvNode
condition for first move full search (#1045) - History pruning: quiet history (#972)
- Improve queen promotion with capture move ordering (#1061)
- Make
TranspositionTableElement.Key
anushort
instead 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
Move
serialization toWriter
thread (#999) - Add a
Board
array toPosition
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
, usingPosition.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 aDictionary
(#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
andbestmove
string allocations if you're usingSearcher
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
orLynx.UCI.Commands.Engine.BestMoveCommand
) (#999)- Remove
Position(Position, Move)
constructors, now you're forced to useMakeMove
/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
anulong
instead 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
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 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
StringBuilder
to generate UCIinfo
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
- π 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 afterucinewgame
(#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
- π 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