So I seem to have developed a new hobby: writing tablebase generators.
I was annoyed with some of the design decisions in my last attempt, which made it a bit tricky to do things like KQKR (more than one piece on the black side). So I decided to build one from the ground up again, this time with a different storage/update scheme.
In my previous attempt, the DTM (or DTC) table was the main data structure I used, and I would probe into that to figure out if I'd processed a position before or not. My new generator uses bitlists for everything (implemented as bitboards), indexed as [wk][wp1][wp2][…][bp1][…] and with set bits corresponding to the position of the black king. I think this is similar to HGM's generator.
I alternate between loss and win finding, collecting newly found positions into a "new" list:
Code: Select all
/* Find new won positions from recently found lost positions */
old = new
new = empty
for all positions in "old":
record all precursors (as "won")
/* Filter out previously won positions */
new &= ~won;
/* Update list of won positions */
won |= new;
/* Find new lost positions from recently found won positions */
old = new
new = empty
for all positions in "old":
test all precursors and record those that are lost (as "lost")
I can transcribe the bitlist into the DTM table when I have found the newly won positions. By construction this never updates the same position twice.
I have a few things to speed things up:
1. When testing if a position is lost I simply and the bitmask of black king moves into the "won" mask (derived from the configuration of the other pieces). If there is a move that does not lead to a won position, then the current position is not lost (otherwise I have to try moves by other black pieces that may be present, but those I will have to make/unmake).
2. I keep track of the position of the white king in newly won or newly lost positions, and skip positions where the white king is not in a "winning" location. I do the same for the first white piece (indexed with the position of the white king).
The second idea gave a huge speed boost in my old generator, it seems to be less of a benefit here, which is odd. The indexing scheme in my old generator is different and it's possible that this causes the difference.
There are a few things I don't do:
1. Updating the index on the fly. Right now I calculate it from scratch, using a generic function for N pieces. This could safe quite a bit of time.
2. Exploit the symmetry of the board. I'm planning to have this as an optional feature, but if I can make it fast enough without it I may just not bother with it.
With all that, my new generator is about a factor of 3-5 slower than my old one, which is not bad considering that one does use the symmetry to speed things up. It's about a factor 30 slower than Marcel's generator posted earlier in this thread.
That means I have some work to do to get generating KBNK back to under 1s, which seems like a good target time.
Interestingly enough, writing the new generator only took me two evenings, rather than a week.