Ronald wrote: ↑Fri May 28, 2021 2:51 pm
Do you always return the value found in the hashtable (if depth is enough and correct bound) or do you only return the hashvalue for non PV nodes?
I do directly return hits from the TT. Additionally I probably make this worse by not using any LMR or similar pruning. I'm trying to be systematic and do most of my current testing with fairly vanilla PVS (null move + some delta pruning in QS both if which I frequently disable for testing) since that is easier to reason about when I see something suspicious. Which probably also means this "brick wall effect" is more pronounced since context based extensions/reductions would smooth this out a bit. I noticed that for example if you let Stockfish search to the same depth on the same position twice in a row the second search is only about 2x faster and will also produce different results. While my current code will just instantly return the previous PV again.
So this may be something that just goes away once I get more into selectivity.
hgm wrote: ↑Fri May 28, 2021 10:39 am
1) You calculate how much time per move you still have, (the 'nominal' thinking time) depending on what is left on your clock, and the TC type.
2) When you have already spent more than a certain fraction of that (say 60%), you won't start a new iteration. This fraction is determined experimentally, such that time usage is resonably distributed over the game.
Right, I might just have to tune that a bit more. I tried to keep a table of "cost to depth" per depth that I would only overwrite if the search was over some time threshold. So if the previous deepening iteration didn't provide a plausible prediction I'd use that instead. That had issues if at some point searching to depth 10 was expensive but subsequently wasn't. It would overestimate the cost and start playing unreasonably fast for a couple of moves.
What I don't like about this approach of fudge factors (60% etc.) is that it drifts from game to game. I guess one option would be to keep some "overflow buffer" of time. Essentially, whenever I find myself at say 80% of the allotted time I don't start a new iteration but add those 20% on the next move. That way the time consumption would be more steady. Intuitively that already happens implicitly since on the next move since w/btime will be larger by whatever amount you haven't used. However if you calculate times by dividing the leftover time by some move estimate you under predict this and are likely to keep making "fast" moves for a while.
hgm wrote: ↑Fri May 28, 2021 10:39 am
3) You won't search new moves in the root if you exceed another factor (2-3) times the nominal time,
and your score has not dropped too much compared to the previous iteration.
4) Finally, there is a 'cold-turkey timeout' that aborts the search no matter what. I usually set that such that a move can take 10 times as much time as any other remaining move. So with T on the clock and still N moves to go this would be 10*T/(N+9).
I hadn't really considered 3. I guess you gain some granularity by doing that. As in always go one deeper and then decide whether to continue after you observed whether the PV still looks good or not.
There is also some temptation to just always do the "cold-turkey" approach and just argue that the truncated search between the last complete search and the truncation isn't wasted since it still populates the TT for the next search where you "reclaim" some of that effort.