Rein Halbersma wrote: ↑Sun Mar 01, 2026 3:29 pm
syzygy wrote: ↑Sun Mar 01, 2026 4:44 am
Rein Halbersma wrote: ↑Sun Mar 01, 2026 12:59 amI’m also thinking of 10x10 draughts with e.g. 5v5 king endgames. The question is how to pick canonical 2-king configs to slice over. Not sure how to do that.
I guess one approach is to distinguish the canonical kings from the other kings, but this reduces symmetry.
What is the normal way to enumerate the 5v5 positions, taking into account symmetries?
Without using symmetries, the indexing is a product of binom(50,5)*binom(45,5) for which you can use the usual binomial decomposition of K=5 identical pieces on N squares. Note that you can use pext()/pdep() for indexing and de-indexing e.g. black kings in the presence of white kings (by mapping the 50 physical squares 0...49 to the 45 logical squares 0...44 giving that somewhere in between there are 5 white kings).
Yes, this is what I'm now also doing for identical pieces (using pdep() to find the nth empty square and popcnt() to count the number of preceding occupied squares).
I don't know of an exact way to enumerate taking into account the full symmetry group (D2, the dihedral group of a rectangle). A problem is that some position are invariant under the entire symmetry group (4-fold symmetry), then there are positions that are invariant under one of the three 2-fold symmetry subgroups (single corner diagonal mirroring, double corner diagonal mirroring and 180 degree rotations) and there are positions that don't have any symmetry. I haven't found a formula which decomposes into products of binomials.
Yes, it is very annoying to write perfect mapping functions, but it should be possible to get within a few percent of optimal.
I guess just concentrate on placing the 5 white kings and ignore the case when the 5 white kings are placed symmetrically (which would in theory leave room for reducing the number of placements of the black kings).
My suicide indexing code (which has to deal e.g. with KKKKKvK) does this:
Code: Select all
} else { /* 3 and higher, e.g. KKKvK and KKKKvK */
for (i = 1; i < norm[0]; i++)
if (triangle[pos[0]] > triangle[pos[i]])
Swap(pos[0], pos[i]);
if (pos[0] & 0x04)
for (i = 0; i < n; i++)
pos[i] ^= 0x07;
if (pos[0] & 0x20)
for (i = 0; i < n; i++)
pos[i] ^= 0x38;
if (offdiag[pos[0]] > 0)
for (i = 0; i < n; i++)
pos[i] = flipdiag[pos[i]];
for (i = 1; i < norm[0]; i++)
for (j = i + 1; j < norm[0]; j++)
if (mtwist[pos[i]] > mtwist[pos[j]])
Swap(pos[i], pos[j]);
idx = multidx[norm[0] - 1][triangle[pos[0]]];
for (i = 1; i < norm[0]; i++)
idx += binomial[i][mtwist[pos[i]]];
}
Here triangle[s] is basically the "orbit" of square s under the symmetry group.
Code: Select all
static const uint8_t triangle[] = {
6, 0, 1, 2, 2, 1, 0, 6,
0, 7, 3, 4, 4, 3, 7, 0,
1, 3, 8, 5, 5, 8, 3, 1,
2, 4, 5, 9, 9, 5, 4, 2,
2, 4, 5, 9, 9, 5, 4, 2,
1, 3, 8, 5, 5, 8, 3, 1,
0, 7, 3, 4, 4, 3, 7, 0,
6, 0, 1, 2, 2, 1, 0, 6
};
I select the piece/king with the "lowest" orbit and mirror the board so the king ends up in the triangle a1-d1-d4. Let's call it the primary king. Then I sort the rest according to their mtwist[] values:
Code: Select all
const uint8_t mtwist[] = {
15, 63, 55, 47, 40, 48, 56, 12,
62, 11, 39, 31, 24, 32, 8, 57,
54, 38, 7, 23, 16, 4, 33, 49,
46, 30, 22, 3, 0, 17, 25, 41,
45, 29, 21, 2, 1, 18, 26, 42,
53, 37, 6, 20, 19, 5, 34, 50,
61, 10, 36, 28, 27, 35, 9, 58,
14, 60, 52, 44, 43, 51, 59, 13
};
This maps the remaining identical pieces to numbers that are from 0 to the mtwist[] value of the primary king.
This is not optimal, in particular when the primary king is on the diagonal or when there are multiple kings in the lowest orbit, but it works and isn't too complicated. Of course in chess (variants) a KKKKKvK table is already tiny compared to a KQRBNvK table.
A king on a 10x10 board can have at most 9+8 = 17 moves. So doing (wK, bK) slicing and binom(48,4)*binom(44,4) positions per slice, you would need at most 18 slices in RAM for 55.4 Gb (similar to the 9 king-slices for chess), but then you have indeed excessive I/O.
Yes, perhaps it is still acceptable if it's just a one-time exercise.
But if you can enumerate perfectly or close to perfectly, the total number of position is 647156159148 (if ChatGPT applied Burnside's lemma correctly), which is just 75.34 GB with 1 bit per position. (And this number seems correct if we approximate using 50!/40!/5!/5!/4/8/1024/1024/1024.)
I'm curious to learn what prompt you gave ChatGPT to get that result.
I asked: "how many ways are there to place 5v5 kings on a 10x10 draughts board, taking into account any symmetries of the board."
I instructed Gemini to expand the Cycle Index Polynomial and the result is the same (647,156,159,148 positions):
Expand ((e+b+w)^50 + 2*(e^2+b^2+w^2)^25 + (e+b+w)^10 * (e^2+b^2+w^2)^20)/4 and collect the coefficient of the term e^40 b^5 w^5
The problem is how to enumerate that number. Do you have any idea or suggestion? Ideally, I'd like it to be a sum of products of binomials, so that one can index each term in the sum efficiently. But there are many cases (e.g. there can be 0, 2, or 4 kings on the main diagonal for each color, the other kings are unrestricted, and for those positions you have to restrict one to a 15 square triangle, but which king?)
So see above for a way to get an approximation. I did not calculate how close it gets.
To do better, you have to treat the special cases:
- primary king on the diagonal. Then find a secondary king by looking at the orbits under the remaining symmetry.
- choice of primary kings from 2, 3 or 4 kings in the same lowest orbit.
You probably only want to treat the cases that are worth it, i.e. that lead to a substantial reduction.
The black kings are then treated in the usual way.