Simple question:
I dont want to explore a tree of games or chess position with known good moves - but I am looking for an algorithm that can generate hard trees on its own.
Search algos deal with imperfect eval() data at so I would like to have a mocked eval() value that can simulate these things:
Random trees that skew towards losing or winning generally
Distributed in that tree should be moves that look good (high eval()) but turn out to be bad in the long run
Distributed in that tree should be moves that look neutral (not the highest eval) but turn out to be the best in the long run.
Forced wins that are N plies deep.
Horizon effect: Alphabeta(8) clearly winning - Alphabeta(9) clearly losing
In summary an algorithm that can generate a gametree of arbitrary difficulty and arbitrary depth and arbitrary branching factor (within memory limits)
For example a tree where the first 6 plies are more or less neutral - but after 6 plies there are 20 ply poisoned moves that need to be avoided and 1 forcing win in there somewhere.
How would one achieve this?
Emulating an arbitrarily difficult gametree
Moderator: Ras
-
- Posts: 1062
- Joined: Tue Apr 28, 2020 10:03 pm
- Full name: Daniel Infuehr
Emulating an arbitrarily difficult gametree
Worlds-fastest-Bitboard-Chess-Movegenerator
Daniel Inführ - Software Developer
Daniel Inführ - Software Developer
-
- Posts: 965
- Joined: Fri Aug 21, 2020 1:25 am
- Location: Planet Earth, Sol system
- Full name: Michael J Sherwin
Re: Emulating an arbitrarily difficult gametree
A material search only that keeps track of statistics for each side for each root move.
1. number of moves for each side
2. number of checkmates for each side
3. number of beta cutoffs
4. number of failed null moves
And whatever else that can be thought of. I'd start with just checkmates. Create two ratios w#cm/w#m - b#cm/b#m and then convert that to some amount of material value to add or subtract from the root move.
1. number of moves for each side
2. number of checkmates for each side
3. number of beta cutoffs
4. number of failed null moves
And whatever else that can be thought of. I'd start with just checkmates. Create two ratios w#cm/w#m - b#cm/b#m and then convert that to some amount of material value to add or subtract from the root move.
-
- Posts: 1062
- Joined: Tue Apr 28, 2020 10:03 pm
- Full name: Daniel Infuehr
Re: Emulating an arbitrarily difficult gametree
The answer will be an algorithm that spits out a tree:
Where eval is filled in such a way to fullfil above requirements when searched. A poisoned position would look good with negamax(8) and bad with (12).
Code: Select all
struct Node
{
Node* Parent;
Node* Children;
int NChildren;
int eval;
}
Worlds-fastest-Bitboard-Chess-Movegenerator
Daniel Inführ - Software Developer
Daniel Inführ - Software Developer
-
- Posts: 28353
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: Emulating an arbitrarily difficult gametree
For a given depth you can start with a PV, and assign it the score you want in the end leaf. You can then built a refutation tree (of alternating cut-nodes and all nodes) by generating only a single move in the cut-nodes (the cut-move), and assign all the leaves a score better (for the side playing the cut-moves) than the PV score. You can then go back to the cut-nodes, and generate trees from them through their other moves with totally random leaf evaluation.
If you want a tree with a poisoned move, that looks best on d=N, and is bad on d=N+1, you first generate the d=N tree according to the above recipe, and from the former leaf add moves that lead to new leaves with the bad score. Since you will be extending all branches by one ply, all leaves will be new. You can just randomly designate one of the (extended) other branches as the new PV, and repeat the procedure.
If you want a tree with a poisoned move, that looks best on d=N, and is bad on d=N+1, you first generate the d=N tree according to the above recipe, and from the former leaf add moves that lead to new leaves with the bad score. Since you will be extending all branches by one ply, all leaves will be new. You can just randomly designate one of the (extended) other branches as the new PV, and repeat the procedure.
-
- Posts: 1062
- Joined: Tue Apr 28, 2020 10:03 pm
- Full name: Daniel Infuehr
Re: Emulating an arbitrarily difficult gametree
I have literally no idea what any of this means.hgm wrote: ↑Sun Aug 21, 2022 3:04 pm For a given depth you can start with a PV, and assign it the score you want in the end leaf. You can then built a refutation tree (of alternating cut-nodes and all nodes) by generating only a single move in the cut-nodes (the cut-move), and assign all the leaves a score better (for the side playing the cut-moves) than the PV score. You can then go back to the cut-nodes, and generate trees from them through their other moves with totally random leaf evaluation.

Could you post you idea in pseudocode? (english leaves too much for interpretation imho)
Worlds-fastest-Bitboard-Chess-Movegenerator
Daniel Inführ - Software Developer
Daniel Inführ - Software Developer
-
- Posts: 28353
- Joined: Fri Mar 10, 2006 10:06 am
- Location: Amsterdam
- Full name: H G Muller
Re: Emulating an arbitrarily difficult gametree
It is somewhat hard to express this in something as exact as pseudo-code. Because hwat you request is rather vague. A game tree can be characterized by many parameters. Even for just the topology (i.e. not looking at scores) you can have any distribution of the number of daughters. And such a distribution can be homogeneous throughout the tree, or slowly varying through some 'inheritance with mutation' scheme. (E.g. in chess you can start with 30 moves/position in the root, which drops to 10 in subtrees where one converts to a pawn ending, and within some nodes of the latter jumps back up to 40 after a promotion.)
The above routine would walk a tree of fixed depth with fixed branching ratio, and a non-degenerate PV where any deviation at least scores 1 point worse.
Code: Select all
Tree(depth, score, bound)
{
if(depth == 0) {
switch(bound) {
case EXACT: return score;
case UPPER: return score - 1;
case LOWER: return score + 1;
case ANY: return randomScore();
}
} else {
n = N; // desired number of children
switch(bound) {
case EXACT: // PV node
Tree(depth-1, -score, EXACT); n--; // one PV child
children = LOWER; // other moves are refuted, i.e. lead to cut-nodes
break;
case LOWER: // cut-node
Tree(dept-1, -score, UPPER); n--; // one cut-move
children = ANY; // the other children are irrelevant
break;
case UPPER: // all-node
children = LOWER; // all children are cut-nodes
break;
case ANY: // unspecified node type
children = ANY;
}
while(n-- > 0) {
Tree(depth-1, -score, children);
}
}
}