syzygy wrote:
Obviously I haven't seen your code, but you probably re-use the move generator of your engine instead of writing dedicated routine working as much as possible on the index as board representation.
Discussions without code are complicated.
I'll clean up what I have and post it for discussion purposes, but since the algorithm is not great it may not be such an interesting discussion.
I actually don't reuse the board representation or move generation - at least not directly. I loop over all squares for all pieces (skipping the ones that are excluded by symmetry or because pieces overlap), then in the inner loop I construct an occupancy bitboard from the piece positions. If the king is in check I actually generate a second one with the position of the king cleared.
I then call each piece's individual generator, passing square and occupancy, and get back a bitboard with all move possibilities. From this I generate successor positions and look up their score. I then set the score for the current node to best/worst result of all child nodes+1 (depending on whether I'm being mated or trying to avoid mate). It's actual mini-max, I should probably change that to negamax.
Also you are using forward generation instead of retrograde analysis. Instead of doing a 1-ply search for each position on each iteration, you could start with the positions that are mate, mark all positions that go 1 ply back, then loop over the table and do the 1-ply search only on those positions. This way you find losses and from those you can go 1 ply back and mark positions as wins (no need for a 1-ply search here). From the newly found wins you go 1 ply back to the potential losses, and so on.
My initial idea was the following:
I mark all positions that are mate and push them on a (fifo) stack. Then I enter a loop where I pop elements off the stack. For each position I pop off the stack I generate the precursors. These have an expected score of 1-worse than the current node. If I've visited the node before and it has the expected score, I do nothing. If I haven't visited the node before I assign it the expected value I just found and push the node onto the stack. If I have visited the node before, I update the score and push the node onto the stack.
Once the stack is empty I've processed all positions and I'm done.
I thought that doing it this way would minimise the redundant work I need to do (because I only work on the positions that I actually need to work on) and I thought that the situation of visiting a node and having to update its score wouldn't actually occur (because the first time I find it is also the closest to mate it can be). The stack can probably grow relatively large though (of the order of the tablebase itself) so that's a downside to this method. Otherwise I think it's basically what you're describing?
Anyway, when I started to implement that it didn't work and I figured I was better off picking a simpler algorithm that was easier to debug and getting that to work first.
