Porting syzgy/tb to an old version chess (shatranj)

Discussion of anything and everything relating to chess playing software and machines.

Moderators: hgm, Rebel, chrisw

User avatar
hgm
Posts: 28022
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by hgm »

ayunus wrote: Wed Sep 18, 2024 1:48 pmMaybe 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?

If mentioned changes could be done without knowing all above details, that would still need expertise of codebase IMHO.

On the other hand it sounds wise to not delving into seperate tables for 3 pieces.
Not sure but having 3 piece tables seperately, could be hypothetically helpfull for keeping same interface with regular contrarily?
In absence of Pawns Chess is symmetric under horizontal, vertical and diagonal refelctions. (And consequently under rotations, which result from pairs of reflections.) So any Pawnless position position can be mirrored / rotated such that the white King ends up in the triangle a1-d4-d1. If the white King is on the diagonal a1-d4 this can even be done in two ways, as an extra reflection in this diagonal then does not alter the location of that King. To not waste time in encoding both these equivalent positions, we can decide to only include the one that has the black King in the triangle a1-h8-a8 in our calculations.

Of course the reflections and rotations should not be applied to just the Kings, but to the entire position. So when you want to probe for a position where the Kings are not in the desired triangles, you would have to transform the location of all the pieces. And the table indexed by the original King locations would tell you what transformation you should apply to those. After this transformation you would have a position where the Kings are in their 'cannonical' locations, and which would thus can be found in the EGT.

During generation you might have to do some mirroring after applying King moves that bring the King outside the triangle they started in.
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

hgm wrote: Wed Sep 18, 2024 9:03 am
syzygy wrote: Tue Sep 17, 2024 11:51 pm
What I also need is game endings, in shatranj when opponents do not have any piece we win.
Also when we capture last piece of opponent, if opponent could also capture our last piece, then draw.
The tables you generate will themselves not have positions where one side has no pieces (obviously). But they will have positions from where such lost or drawn positions are reachable.
Note that 'does not have any pieces' here should be read as 'has only a king'. I suppose such positions do occur in some of the generated tables.
Yes, as far as I understand the role of the king is exactly the same as in regular chess, except for no castling. I suppose the final capture by the lone king to "draw" the position may not bring the king "in check".

Positions with a bare king do not sporadically occur in tables. Either all positions in the table have a bare king, or none. Tables with bare-king positions (so KXYZvK) do not need to be generated since the value of any position can be calculated very quickly.
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

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.
I really find King King (462) approach complicated even with explanations in here https://www.talkchess.com/forum/viewtopic.php?t=59947 if any paper exist in this topic, I would appriciate, since topic is fun.
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).
User avatar
hgm
Posts: 28022
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by hgm »

syzygy wrote: Thu Sep 19, 2024 12:10 am Yes, as far as I understand the role of the king is exactly the same as in regular chess, except for no castling. I suppose the final capture by the lone king to "draw" the position may not bring the king "in check".
That is correct. The counter-baring move has to be legal. In an engine I implement the game-termination rule as that you win when the opponent does not have a King at the end of your turn, otherwise you draw if both have a bare King, and otherwise you lose if you are the only one with a bare King.
Positions with a bare king do not sporadically occur in tables. Either all positions in the table have a bare king, or none. Tables with bare-king positions (so KXYZvK) do not need to be generated since the value of any position can be calculated very quickly.
Indeed. The exception for King capture that I mentioned above on the result being the same for every position in such a table would not even exist if your indexing scheme is such that the Kings can never be adjacent in the first place. Only in the 2-vs-1 table you would have to exempt some positions with the weak side to move, and it is easi to determine which.
ayunus
Posts: 5
Joined: Tue Sep 10, 2024 8:33 am
Full name: Yunus Emre Ayhan

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by ayunus »

Thanks hgm syzygy, explanations really helped.

In summary (for whom reading this at the end of the world, or possibly future me):
If white king outside a1-d1-d4 triangle, we apply mirroring positions, like

- ^0x07 000111 (vertical mirroring) (6 bits sufficiently represents position of the board for 64 squares)
- ^0x3C 111000 (horizontal mirroring)
- ((sq & 0x07) << 3) | (sq >> 3) (diagonal a1h8 mirroring)

if white king on the diaognal, diagonal mirroring will not change white kings position, so black king could be assumed will only need below a1h8 diagonal range for something more minor squashing, because if it is above that diagonal we can just mirror board diagonally once more.

Updates are needed for getting a kind of key for non pawn tablebases.
If we remember those updates, we can also reverse tablebase response to actual position.
Pawn tablebases have no such squash implemented in tb code.
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

White wins in 7 ply:
[d]8/8/8/3R4/8/8/8/rk1K4 w
This seems correct. 1.Rb5+ Ka2+ 2.Kc2 Ka3 3.Ra5+ Ka4 4.Rxa1#

White wins in 43 ply:
[d]F7/7k/8/8/f7/8/8/K7 w
This seems suspect. Do you have a way to verify whether this is a win for white?

Black wins in 5 ply:
[d]8/8/8/3R4/8/8/8/K1k1n3 e
Correct. 1...Nc2+ 2.Ka2 2.Nb4+ Kb3 3.Nxd5#
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

White wins in 3 ply:
[d]8/8/8/8/8/1F1r4/8/1K1k4 w
That does not seem right. 1.Qc2+ Kd2 2.Qxd3 Kxd3. Draw.
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

syzygy wrote: Tue Sep 17, 2024 11:51 pm
What I also need is game endings, in shatranj when opponents do not have any piece we win.
Also when we capture last piece of opponent, if opponent could also capture our last piece, then draw.
The tables you generate will themselves not have positions where one side has no pieces (obviously). But they will have positions from where such lost or drawn positions are reachable.

What you need to do is to loop through the pieces that can be captured.
Then for each piece, you loop through all the resulting "no pieces" positions after it was captured. Then you "undo" the capture in all possible ways, which results in the positions in the table that you are generating which have a win-in-1 (namely the capture that you "undid").

The complication is that some of those "no pieces" positions are not lost but drawn because the lone king can capture the last piece (so this only happens if you start with 4 pieces (including kings)). When you undo captures into these positions, you land in table positions that have a forced draw (by capturing). You have to mark those positions are "draw-by-capture" (CAPT_DRAW in rtbgen.c).

If a position is both "win-in-1" and "draw-by-capture", then "win-in-1" of course takes precedence. (The "draw-by-capture" positions can later still turn out to be e.g. win-in-30.)
Actually, as long as the probe_tb() function handles bare-king positions correctly, I think the tb generation code essentially does not need any changes. (For pawns the double-pawn push code has to be removed, and promotions only to Q/Ferz.)

So removing the special handling of capture initialisation, which seems to have contained some bug, I get this.
[d]1e6/8/8/8/5E2/8/8/k1K5 w
White wins in 23 ply? I would not expect that.

[d]8/8/3F4/8/8/k6f/8/1K6 w
White wins in 63 ply?

[d]8/8/8/8/5F2/7k/2K5/n7 w
White wins in 15 ply?

[d]2k2F2/8/8/8/8/8/4n3/2K5 w[d]
Black wins in 40 ply?

[d]6Fk/8/7f/8/7F/8/3K4/8 w
White wins in 129 ply (cursed win)
syzygy
Posts: 5647
Joined: Tue Feb 28, 2012 11:56 pm

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by syzygy »

syzygy wrote: Sat Sep 21, 2024 7:35 pmActually, as long as the probe_tb() function handles bare-king positions correctly, I think the tb generation code essentially does not need any changes. (For pawns the double-pawn push code has to be removed, and promotions only to Q/Ferz.)
But I forgot about stalemate=loss so far.
ayunus
Posts: 5
Joined: Tue Sep 10, 2024 8:33 am
Full name: Yunus Emre Ayhan

Re: Porting syzgy/tb to an old version chess (shatranj)

Post by ayunus »

As a reference question, I have something like this

https://tromp.github.io/chess/chess.html
  • al-Suli's Diamond
  • al-Suli's Rough Diamond
And there is solutions for them, maybe you can test them too.