ayunus wrote: ↑Wed Sep 18, 2024 1:48 pm
My progress on understanding code is like this so far:
- Checkmate in tbgen is controlling if anything not illegal exist, so how is illegal is marked.
"Broken" positions are positions with two pieces on the same square. "Illegal" positions are positions where the side to move can capture the opponent's king. I think Shatranj is not different from regular chess here.
- Still was not fully understood how illegal marking working but we use king king notation during visitation, illegal marking use some different nottion with work_priv1
- While it is ignoring adjacent kings, and mirrors, it still produce combinations from all white pieces like they are kings, and marked them as illegal.
- Maybe each position intended to be marked as illegal durring calc_illegal_bw calls, did not read rest of the code yet.
Adjacent kings don't exist in the indexing scheme use by rtbgen.c, since the KK are mapped to the 462 possible positions (after mirroring) with non-adjacent KK. (I think indexing scheme is used if SMALL is defined.)
All the necessary mirrorring, (un)move generation for pieces other than pawns and index calculations are handled by the existing macros. The macro definitions are too ugly to look at (but see generics.c), but they kind of nicely allow for "high-level" generation code.
The work_g, work_piv0 and work_piv1 arguments are lists of indices on which to divide the work to be done over multiple threads. No need to worry about those (as long as you use the right one in the right call
). Basically piv0 is the white king, piv1 is the black king, which together form the KK part of the index, and the other pieces are each 6 bits in the remaining part of the index.
That thread is mainly about the probing code. For the generator it is important to know that the table for, say, KQvKR is two arrays of 462*64*64 bytes, one for white-to-move, one for black-to-move. The value of the position (wK,wQ,bK,bR) is the byte at index KK_map[wK][bK] * 4096 + wQ * 64 + bR. [I think the generator switches the R and Q because Q is more mobile in regular chess, so it is better to have positions where the Q moves closer together.]
The KK_map[10][64] array is defined in generics.c.
To use it, you first have to "mirror" the wK (and the other pieces with it) to one of the squares in the A1-D1-D4 triangle and, if the wK is ont the A1-D4 diagonal, the bK to the lower A1-H1-H8 triangle.
The generator's "board structure" is basically the index from 0 to 462*64*64-1 (for a 4-piece position like KQvKR). To move the bR to a different position, just change the last 6 bits that represent the bR square. Same for the wQ. To move the wK and the bK you need to be more careful. First map the KK-part, an integer from 0..461, to a pair of squares for wK, bK, then move the king you want to move, mirror the two Ks (and the remaining positions) back to one of the 462 configurations, and you're done.
In generics.c, MakeMove0(idx, sq) moves the wK to sq, MakeMove1(idx,sq) moves the bK to sq, MakeMove2(idx,k,sq) moves the other pieces (k=2,...,n) to sq.
To calculate the possible attacks for sliding pieces (Q,R,B in regular chess), the generator keeps track of an occupancy bitboard (usually "occ").
The indexing scheme assigns a unique index to most positions, but not to positions with both Ks on the diagonal, because those can be mirrored in the A1H8 diagonal to get another index value. This has to be taken into account by the "unmove" code: if you start from a "unique" position, e.g. a lost position, with the Ks not both on the diagonal and unmove to a position with both Ks on the diagonal, e.g. to mark that position as a win, you have to mark both indexes that correspond to the position.
Maybe you can verify my understanding about it also but please ignore if it will be hard to explain. We consider white king inside a triangle, and all other possibilities are ignored where white king could be placed because it is considered we have already covered its scenario with our triangle, via mirroring it into that triangle. Mirror table is used for deciding if position of kings is one of these ignored mirrors. Than question comes to my mind is like (sorry if stupid ignore me) what about other pieces, are them still covered for scenarios where white king outside of the triangle?
So when you mirror the white king, you also have to mirror the black king and the other pieces.
We are talking here about positions without pawns. This means that you can mirror the board horizontally, vertically and diagonally, and the position stays "the same". So you can first move the white king to the lower half of the board by mirroring vertically, if necessary, then to the left half of the board by mirroring horizontally, if necessary. Now the wK is in A1-D1-D4-A4, and you can move it to the lower triangular part of this square by mirroring in the A1H8 diagonal, if necessary. If the wK is on A1-D4, then mirroring it diagonally won't change its position, so you can use the A1H8 mirroring operation to get the bK to A1-H1-H8. The horizontal/vertical mirroring can be done with a single xor-operation. Look up the mirror-mask in a table with 64 entries indexed by the wK square, and xor the index with the precalculated value in that entry. Diagonally is a bit more tricky but still relatively easy (you have to rearrange the bits in the index: abcdef -> defabc, where abc is the row 0-7 and def is the file 0-7).
Not sure but having 3 piece tables seperately, could be hypothetically helpfull for keeping same interface with regular contrarily?
3-piece tables could make some sense, since they are small anyway and could perhaps keep the code a bit simpler. But I don't think they are really needed. If you end up with a 3-piece position with e.g. KK and R on the squares wK, bK and bR and white to move, you only need to check if (king_range[wK] & ~king_range[bK] & bit[bR]) is true to know if white has a draw (otherwise it is lost for white).