What's wrong with fixed depth?
The most basic setup: level L plays at depth L. This is problematic for a few reasons
- Endgames: fewer pieces mean less calculation. Especially pawn races in pawn endgames where humans can calculate almost linearly. So we would need more depth as Effective Branching Factor reduces.
- Search explosion. Some positions have explosive EBF, so you need to impose a cap on nodes besides the depth limit, or the engine might get stuck and never reach the target depth. Less common in modern engines, but still a reality to contend with: time to depth, or nodes to depth, can vary widely.
Level L plays at nodes ~ b^L, for some b>1, controlling how far apart levels should be. This is already better, because it reduces the aforementioned Endgame problem, and eliminates the Search Explosion problem.
However, the minimum search possible is still 1 ply, with quiescence search. Couple that with a strong evaluation, especially NN based, and the lowest level will be stronger than most humans, and completely out of reach for beginners. Even if you win, it's not satisfying: you get asphyxiated by the computer, and out of nowhere, it blunders a simple tactic. So it feels like losing, then engine has a bug, allowing you to win, undeservedly.
So we must dumb down the evaluation dramatically, not only the search.
Fixed nodes with noisy evaluation
Level L setup:
- Nodes ~ b^L (b >1): exponential growth
- Noise ~ d^L (d<1): exponential decay
Additionally, make sure to use a random seed, so that when initial conditions are repeated (eg. new game), the computer plays differently. Otherwise humans keep playing and memorising the same game until they win, by iteration. Plus you want your training partner to expose you to diverse play.
Note that there is a side effect of noise on search. The nodes to depth increase with noisy evaluation (messing up alpha beta, causing aspirations failures etc).
Endgame adjustments
Experience shows that you still need to increase nodes and reduce noise for endgames (eg. enough to win KQK without making the lowest level too strong otherwise). For example, say you want to add 1 level worth of nodes for everything material halving:
- r = piece material/ initial piece material (counting piece values for both sides).
- Nodes *= b^(-log2(r))
- Perhaps flooring r to limit the node increase.
Disable polluting variables
Be sure to force Threads=1 in Level mode. Otherwise, SMP causes distortions: both search widening (more elo per depth) and reduction of search efficiency (more nodes to depth).
Same for Hash. Make it fixed. Like 1MB, perhaps more for the highest levels, if needed.
Is it a zero cost approach?
Almost, but not quite. It ads a branch to the evaluation function, which is the critical execution path of the engine. But it's just a bool check like
Code: Select all
if (noisy evaluation) { ... }
PS: I put some levels on lichess.
https://lichess.org/@/Demolito_L3 (up to level 6, after that it's too strong for humans).