Hi Uri,
if i understand correct, you need a kind of statefull movegenerator.
You have to keep a "state"-variable per ply, with a defined set of states. Also, you probably need at least two indices into the movelist per depth. One put-, and one get-index. put[ply] to store the next generated move with postincrement.
Each time getNextMove is called:
If get[ply] < put[ply], a move is taken from the movelist, postincrementing get[idx].
Otherwise state[ply] is interpreted to generate moves accordingly.
Also state[ply] has to be changed, until something like "AllMovesGenerated" finally occures and no more next move is available.
Fur cut-nodes in full search may be with following switch(state):
- Code: Select all
case MGFullCutInitState:
genHashMove();
state = MGFullCutGoodSEECaptures;
if ( get < put )
break; // hash move generated
case MGFullCutGoodSEECaptures:
genGoodSEECaptures();
state = MGFullCutKiller0;
if ( get < put )
break; // good capture(s) generated
case MGFullCutKiller0:
genKiller0();
state = MGFullCutKiller1;
if ( get < put )
break;
...
case MGFullCutKillerN:
genKillerN();
state = MGFullCutRemaining;
if ( get < put )
break;
case MGFullCutRemaining:
genRemaining();
state = AllMovesGenerated;
break;
With other Initial States one may implement different move generators for different node types, like "cut", "all" or "pv", and maybe also for different depth from/to horizon, including quiescence and/or in Check or in double Check.
Depending on window related stuff, different "incarnations" of genRemaining may do more or less effort to evaluate generated moves for more accurate sorting.
As you already mentioned, a problem might be be to keep track of already generated moves to avoid multiple generation of same moves in several movegen states. One obvious solution is to keep a third movelist index per ply - firstPut[ply]. Before pushing a fresh generated move, one has to loop from firstPut[ply] to put[ply] over the movelist, to look whether the move was already tried. The more moves already tried - without cut, the more expensive the lookup becomes.
One may keep track disjoint source/target sets and/or also disjoint movelists for captures and quite moves. For instance obvoius winning captures (without SEE?) {P,N,B}x{Q,R} or {all}x{hanging pieces} are only generated in genGoodSEECaptures and skipped in genRemaining.
To avoid any bookholding in expected all-nodes, it probably makes sense to generate all at once here:
- Code: Select all
case MGFullAllInitState:
genAllAndSort();
state = AllMovesGenerated;
-------------------------------------------------------------
Is it worth the trouble?
So nice such FSM-like movegen sounds - it is more and error-prone code.
You need a lot of additional code in a debug-version.
IMHO it is important to search exactly the same trees, both with generating moves all at once and incrementally, assuming same move evaluation and pick up by best value.
Cheers,
Gerd