SAN test position

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

SAN test position

Post by matthewlai »

I have just added SAN support to Giraffe, and found it to be a bit harder than I had imagined because of all the corner cases and rules.

So I made a test position with as many corner cases and likely bugs as possible. Here it is -
[d]Bn1N3R/ppPpNR1r/BnBr1NKR/k3pP2/3PR2R/N7/3P2P1/4Q2R w - e6 0 1

Things it tests:
  • * Castling must be done with a king (Qg1 is not castling!)
    * En-passant must be done with a pawn (Ne6 is not en passant)
    * Rank and file disambiguations, with and without captures (rooks on the left)
    * Must prefer using file to disambiguate (Rfxh7, not R7xh7, even though both are unambiguous)
    * No need to disambiguate with opponent piece or other piece types (Nd4, not Nad4 - knight on b6 is black, and bishop on a6 is a bishop)
    * Pawn captures must include file, even if there's no ambiguity (dxe5)
    * No disambiguation needed if the other candidate move is illegal because it's pinned (Nd5, not Ned5).
    * Cases where both rank and file disambiguations are required (Ba6xb7)
    * Capture promotions and normal promotions (cxb8=X, c8=X)
Feel free to modify the position to add more tests, but make sure it still tests all those things!

Not tested:
  • * Castling (conflicts with #1)
I believe the correct solution is:

Code: Select all

d3+
g3
g4
d5
dxe5
fxe6
cxb8=R
cxb8=N
cxb8=B
cxb8=Q
c8=R
c8=N
c8=B
c8=Q
Nb1
Nc2
Nc4+
Nb5
Nd5
Nc8
Ng8
Ne6
Nxb7+
Bf1
Be2
Bd3
Bc4
Bab5
Ba6xb7
Ba4
Bcb5
Bd5
Bcxb7
Bxd7
Ba8xb7
Rf1
Rg1
R1h2
R1h3
Re2
Re3
Ref4
Reg4
Rxe5+
R4h2
R4h3
Rhf4
Rhg4
R4h5
R6h5
Rh6xh7
Rg7
Rfxh7
Rff8
Rh8xh7
Re8
Rhf8
Rg8
Qa1
Qb1
Qc1
Qd1
Qf1
Qg1
Qe2
Qf2
Qe3
Qg3
Kg5
Kh5
Kxh7
But that code has just been written, so is quite possibly buggy!

Note that this is a SAN-generation test, not a move-gen test. So this doesn't test for possible move-gen bugs, if they aren't likely to cause SAN-generation bugs.
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
User avatar
Evert
Posts: 2929
Joined: Sat Jan 22, 2011 12:42 am
Location: NL

Re: SAN test position

Post by Evert »

Note that the en-passant field is bogus: black's last move was not e7-e5.

The way I do SAN generation is as follows:
1. Generate a list of legal moves.
2. If a move is a castling move, generate the appropriste SAN and continue.
2. Test if the combination of piece/destination is unique.
2a. If so, we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.
3. Test if the combination of piece, from-file and destination square is unique.
3b. If so, we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.
4. Verify that the combination of piece, from-rank and destination square is unique.
5. If it is not, something is screwed up. Otherwise we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.

Personally, I don't bother marking checking moves as such, and I make no distinction for en-passant captures. The important point is to consider moves within the context of the entire move list, and to do the disambiguation in the correct order.
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: SAN test position

Post by matthewlai »

Evert wrote:Note that the en-passant field is bogus: black's last move was not e7-e5.

The way I do SAN generation is as follows:
1. Generate a list of legal moves.
2. If a move is a castling move, generate the appropriste SAN and continue.
2. Test if the combination of piece/destination is unique.
2a. If so, we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.
3. Test if the combination of piece, from-file and destination square is unique.
3b. If so, we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.
4. Verify that the combination of piece, from-rank and destination square is unique.
5. If it is not, something is screwed up. Otherwise we are done except for window dressing ("x" for captures, en-passant, check markers and promotion suffices). Continue.

Personally, I don't bother marking checking moves as such, and I make no distinction for en-passant captures. The important point is to consider moves within the context of the entire move list, and to do the disambiguation in the correct order.
Good catch. I believe this one has valid ep:
[d]Bn1N3R/ppPp1R1r/BnBr1NKR/k3pP2/3PR2R/N3N3/3P2P1/4Q2R w - e6 0 1

That is how I do it, too, but it's easy to forget checking for piece types for kings (castling) and pawns (which require special handling for promotions and always need file).

It's also easy to forget that file-disambiguation should be preferred, and that may take a while to catch, since it doesn't usually matter.
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
ZirconiumX
Posts: 1334
Joined: Sun Jul 17, 2011 11:14 am

Re: SAN test position

Post by ZirconiumX »

Dorpsgek gets most of these right - it fails the Nd5 test, but since the calculation for pinned pieces is inelegant for its board array, I'm going to leave that corner case as-is. Better to be simple than correct for what is essentially window-dressing.

Code: Select all

# Allocated 16 megabyte hash table of 1048576 elements.
# Allocated 4 megabyte eval hash table of 524288 elements.
# never exceed: 61220 no iteration: 3749
# Dorpsgek Bloody Mary 1
# by Dan Ravensloft (Matthew Brades).
# Dedicated to Morgan Redbourne, my loyal tester.
#
# 3 parts vodka
# 6 parts tomato juice
# 1 part lemon juice
# 2-3 dashes of Worcestershire sauce
# Tabasco, celery salt, pepper to taste.
# Pour all ingredients into highball glass. Garnish with celery and lemon wedge.
setboard Bn1N3R/ppPpNR1r/BnBr1NKR/k3pP2/3PR2R/N7/3P2P1/4Q2R w - e6 0 1
divide 1
Qa1 1 (1)
Nb1 1 (2)
Qb1 1 (3)
Qc1 1 (4)
Qd1 1 (5)
Bf1 1 (6)
Rf1 1 (7)
Qf1 1 (8)
Rg1 1 (9)
Qg1 1 (10)
Nc2 1 (11)
Be2 1 (12)
Re2 1 (13)
Qe2 1 (14)
Qf2 1 (15)
R4h2 1 (16)
R1h2 1 (17)
Bd3 1 (18)
Re3 1 (19)
Qe3 1 (20)
Qg3 1 (21)
R4h3 1 (22)
R1h3 1 (23)
Ba4 1 (24)
Nc4 1 (25)
Bc4 1 (26)
Ref4 1 (27)
Rhf4 1 (28)
Reg4 1 (29)
Rhg4 1 (30)
Nb5 1 (31)
Bab5 1 (32)
Bcb5 1 (33)
Ned5 1 (34)
Bd5 1 (35)
dxe5 1 (36)
Rxe5 1 (37)
Kg5 1 (38)
R6h5 1 (39)
R4h5 1 (40)
Kh5 1 (41)
fxe6e.p. 1 (42)
Ne6 1 (43)
Nxb7 1 (44)
Ba8xb7 1 (45)
Ba6xb7 1 (46)
Bc6xb7 1 (47)
Bxd7 1 (48)
Rg7 1 (49)
Rh8xh7 1 (50)
Rf7xh7 1 (51)
Rh6xh7 1 (52)
Kxh7 1 (53)
cxb8N 1 (54)
cxb8B 1 (55)
cxb8R 1 (56)
cxb8Q 1 (57)
Nc8 1 (58)
Re8 1 (59)
Rhf8 1 (60)
Rff8 1 (61)
Neg8 1 (62)
Rg8 1 (63)
c8Q 1 (64)
c8R 1 (65)
c8B 1 (66)
c8N 1 (67)
d5 1 (68)
d3 1 (69)
g3 1 (70)
g4 1 (71)
Nodes: 71 in 0.092 seconds
Some believe in the almighty dollar.

I believe in the almighty printf statement.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: SAN test position

Post by bob »

matthewlai wrote:I have just added SAN support to Giraffe, and found it to be a bit harder than I had imagined because of all the corner cases and rules.

So I made a test position with as many corner cases and likely bugs as possible. Here it is -
[d]Bn1N3R/ppPpNR1r/BnBr1NKR/k3pP2/3PR2R/N7/3P2P1/4Q2R w - e6 0 1

Things it tests:
  • * Castling must be done with a king (Qg1 is not castling!)
    * En-passant must be done with a pawn (Ne6 is not en passant)
    * Rank and file disambiguations, with and without captures (rooks on the left)
    * Must prefer using file to disambiguate (Rfxh7, not R7xh7, even though both are unambiguous)
    * No need to disambiguate with opponent piece or other piece types (Nd4, not Nad4 - knight on b6 is black, and bishop on a6 is a bishop)
    * Pawn captures must include file, even if there's no ambiguity (dxe5)
    * No disambiguation needed if the other candidate move is illegal because it's pinned (Nd5, not Ned5).
    * Cases where both rank and file disambiguations are required (Ba6xb7)
    * Capture promotions and normal promotions (cxb8=X, c8=X)
Feel free to modify the position to add more tests, but make sure it still tests all those things!

Not tested:
  • * Castling (conflicts with #1)
I believe the correct solution is:

Code: Select all

d3+
g3
g4
d5
dxe5
fxe6
cxb8=R
cxb8=N
cxb8=B
cxb8=Q
c8=R
c8=N
c8=B
c8=Q
Nb1
Nc2
Nc4+
Nb5
Nd5
Nc8
Ng8
Ne6
Nxb7+
Bf1
Be2
Bd3
Bc4
Bab5
Ba6xb7
Ba4
Bcb5
Bd5
Bcxb7
Bxd7
Ba8xb7
Rf1
Rg1
R1h2
R1h3
Re2
Re3
Ref4
Reg4
Rxe5+
R4h2
R4h3
Rhf4
Rhg4
R4h5
R6h5
Rh6xh7
Rg7
Rfxh7
Rff8
Rh8xh7
Re8
Rhf8
Rg8
Qa1
Qb1
Qc1
Qd1
Qf1
Qg1
Qe2
Qf2
Qe3
Qg3
Kg5
Kh5
Kxh7
But that code has just been written, so is quite possibly buggy!

Note that this is a SAN-generation test, not a move-gen test. So this doesn't test for possible move-gen bugs, if they aren't likely to cause SAN-generation bugs.
Alas, the position is illegal. When you add the white pawns, + white pieces, white can never have more than 16 pieces on the board total, counting the king. Yours has 20 white pieces.
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: SAN test position

Post by matthewlai »

bob wrote:
matthewlai wrote:I have just added SAN support to Giraffe, and found it to be a bit harder than I had imagined because of all the corner cases and rules.

So I made a test position with as many corner cases and likely bugs as possible. Here it is -
[d]Bn1N3R/ppPpNR1r/BnBr1NKR/k3pP2/3PR2R/N7/3P2P1/4Q2R w - e6 0 1

Things it tests:
  • * Castling must be done with a king (Qg1 is not castling!)
    * En-passant must be done with a pawn (Ne6 is not en passant)
    * Rank and file disambiguations, with and without captures (rooks on the left)
    * Must prefer using file to disambiguate (Rfxh7, not R7xh7, even though both are unambiguous)
    * No need to disambiguate with opponent piece or other piece types (Nd4, not Nad4 - knight on b6 is black, and bishop on a6 is a bishop)
    * Pawn captures must include file, even if there's no ambiguity (dxe5)
    * No disambiguation needed if the other candidate move is illegal because it's pinned (Nd5, not Ned5).
    * Cases where both rank and file disambiguations are required (Ba6xb7)
    * Capture promotions and normal promotions (cxb8=X, c8=X)
Feel free to modify the position to add more tests, but make sure it still tests all those things!

Not tested:
  • * Castling (conflicts with #1)
I believe the correct solution is:

Code: Select all

d3+
g3
g4
d5
dxe5
fxe6
cxb8=R
cxb8=N
cxb8=B
cxb8=Q
c8=R
c8=N
c8=B
c8=Q
Nb1
Nc2
Nc4+
Nb5
Nd5
Nc8
Ng8
Ne6
Nxb7+
Bf1
Be2
Bd3
Bc4
Bab5
Ba6xb7
Ba4
Bcb5
Bd5
Bcxb7
Bxd7
Ba8xb7
Rf1
Rg1
R1h2
R1h3
Re2
Re3
Ref4
Reg4
Rxe5+
R4h2
R4h3
Rhf4
Rhg4
R4h5
R6h5
Rh6xh7
Rg7
Rfxh7
Rff8
Rh8xh7
Re8
Rhf8
Rg8
Qa1
Qb1
Qc1
Qd1
Qf1
Qg1
Qe2
Qf2
Qe3
Qg3
Kg5
Kh5
Kxh7
But that code has just been written, so is quite possibly buggy!

Note that this is a SAN-generation test, not a move-gen test. So this doesn't test for possible move-gen bugs, if they aren't likely to cause SAN-generation bugs.
Alas, the position is illegal. When you add the white pawns, + white pieces, white can never have more than 16 pieces on the board total, counting the king. Yours has 20 white pieces.
It is indeed. I decided to violate that rule because I don't think any engine actually cares about that, and I don't think it's possible to test for so many things with just 16 pieces.
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
mar
Posts: 2559
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: SAN test position

Post by mar »

Thanks! I fixed a bug in my SAN output thanks to your post.

I believe the correct output is this:

Code: Select all

B8xb7
Ba4
Ba6xb7
Bab5
Bc4
Bcb5
Bcxb7
Bd3
Bd5
Be2
Bf1
Bxd7
Kg5
Kh5
Kxh7
Nb1
Nb5
Nc2
Nc4+
Nc8
Nd5
Ne6
Ng8
Nxb7+
Qa1
Qb1
Qc1
Qd1
Qe2
Qe3
Qf1
Qf2
Qg1
Qg3
R1h2
R1h3
R4h2
R4h3
R4h5
R6h5
R6xh7
R8xh7
Re2
Re3
Re8
Ref4
Reg4
Rf1
Rff8
Rfxh7
Rg1
Rg7
Rg8
Rhf4
Rhf8
Rhg4
Rxe5+
c8=B
c8=N
c8=Q
c8=R
cxb8=B
cxb8=N
cxb8=Q
cxb8=R
d3+
d5
dxe5
fxe6
g3
g4
Note B8xb7 is better than Ba8xb7 because it already resolves the ambiguity (and so on).
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: SAN test position

Post by matthewlai »

mar wrote: Note B8xb7 is better than Ba8xb7 because it already resolves the ambiguity (and so on).
Indeed! You just found a bug in my program :).
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
matthewlai
Posts: 793
Joined: Sun Aug 03, 2014 4:48 am
Location: London, UK

Re: SAN test position

Post by matthewlai »

My disambiguation logic now looks like this:

Code: Select all

bool needX = false;
bool needY = false;
bool needEither = false;

if (StripColor(pt) == P && isCapture)
{
	// pawn captures always require source file
	needX = true;
}

MoveList allMoves;
GenerateAllLegalMoves<ALL>&#40;allMoves&#41;;

for &#40;const auto &possibleMove &#58; allMoves&#41;
&#123;
	if &#40;GetToSquare&#40;possibleMove&#41; != to || GetPieceType&#40;possibleMove&#41; != pt || GetFromSquare&#40;possibleMove&#41; == from&#41;
	&#123;
		continue;
	&#125;
	else
	&#123;
		// we have another piece that can move to the same square
		if &#40;GetX&#40;GetFromSquare&#40;possibleMove&#41;) != GetX&#40;from&#41; && GetY&#40;GetFromSquare&#40;possibleMove&#41;) != GetY&#40;from&#41;)
		&#123;
			// if this can be resolved using either file or rank, we make a note that we need one of the two
			// we cannot just choose to use file here, because it's possible that another disambiguation for this
			// move requires rank, in which case we don't need to add file
			needEither = true;
		&#125;
		else if &#40;GetX&#40;GetFromSquare&#40;possibleMove&#41;) != GetX&#40;from&#41;)
		&#123;
			needX = true;
		&#125;
		else if &#40;GetY&#40;GetFromSquare&#40;possibleMove&#41;) != GetY&#40;from&#41;)
		&#123;
			needY = true;
		&#125;
		else
		&#123;
			assert&#40;false&#41;;
		&#125;
	&#125;
&#125;

if &#40;needEither && !needX && !needY&#41;
&#123;
	// if we can pick, use file
	needX = true;
&#125;
I didn't have needEither before, and just used needX (due to preference for file disambiguation) when either works. But that's wrong because it's possible that the move already requires rank disambiguation with another move, and if that's the case, there's no need to add file.

SAN output is more complicated than it looks on the surface!
Disclosure: I work for DeepMind on the AlphaZero project, but everything I say here is personal opinion and does not reflect the views of DeepMind / Alphabet.
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: SAN test position

Post by bob »

matthewlai wrote:
bob wrote:
matthewlai wrote:I have just added SAN support to Giraffe, and found it to be a bit harder than I had imagined because of all the corner cases and rules.

So I made a test position with as many corner cases and likely bugs as possible. Here it is -
[d]Bn1N3R/ppPpNR1r/BnBr1NKR/k3pP2/3PR2R/N7/3P2P1/4Q2R w - e6 0 1

Things it tests:
  • * Castling must be done with a king (Qg1 is not castling!)
    * En-passant must be done with a pawn (Ne6 is not en passant)
    * Rank and file disambiguations, with and without captures (rooks on the left)
    * Must prefer using file to disambiguate (Rfxh7, not R7xh7, even though both are unambiguous)
    * No need to disambiguate with opponent piece or other piece types (Nd4, not Nad4 - knight on b6 is black, and bishop on a6 is a bishop)
    * Pawn captures must include file, even if there's no ambiguity (dxe5)
    * No disambiguation needed if the other candidate move is illegal because it's pinned (Nd5, not Ned5).
    * Cases where both rank and file disambiguations are required (Ba6xb7)
    * Capture promotions and normal promotions (cxb8=X, c8=X)
Feel free to modify the position to add more tests, but make sure it still tests all those things!

Not tested:
  • * Castling (conflicts with #1)
I believe the correct solution is:

Code: Select all

d3+
g3
g4
d5
dxe5
fxe6
cxb8=R
cxb8=N
cxb8=B
cxb8=Q
c8=R
c8=N
c8=B
c8=Q
Nb1
Nc2
Nc4+
Nb5
Nd5
Nc8
Ng8
Ne6
Nxb7+
Bf1
Be2
Bd3
Bc4
Bab5
Ba6xb7
Ba4
Bcb5
Bd5
Bcxb7
Bxd7
Ba8xb7
Rf1
Rg1
R1h2
R1h3
Re2
Re3
Ref4
Reg4
Rxe5+
R4h2
R4h3
Rhf4
Rhg4
R4h5
R6h5
Rh6xh7
Rg7
Rfxh7
Rff8
Rh8xh7
Re8
Rhf8
Rg8
Qa1
Qb1
Qc1
Qd1
Qf1
Qg1
Qe2
Qf2
Qe3
Qg3
Kg5
Kh5
Kxh7
But that code has just been written, so is quite possibly buggy!

Note that this is a SAN-generation test, not a move-gen test. So this doesn't test for possible move-gen bugs, if they aren't likely to cause SAN-generation bugs.
Alas, the position is illegal. When you add the white pawns, + white pieces, white can never have more than 16 pieces on the board total, counting the king. Yours has 20 white pieces.
It is indeed. I decided to violate that rule because I don't think any engine actually cares about that, and I don't think it's possible to test for so many things with just 16 pieces.
There is at least ONE engine that won't accept it. :)

And probably several that will crash if they use the same sort of piece list approach we used in Cray Blitz as the max # of pieces in the array was 16.