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!
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.
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.
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.
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.
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!
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 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!
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 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.
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>(allMoves);
for (const auto &possibleMove : allMoves)
{
if (GetToSquare(possibleMove) != to || GetPieceType(possibleMove) != pt || GetFromSquare(possibleMove) == from)
{
continue;
}
else
{
// we have another piece that can move to the same square
if (GetX(GetFromSquare(possibleMove)) != GetX(from) && GetY(GetFromSquare(possibleMove)) != GetY(from))
{
// 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;
}
else if (GetX(GetFromSquare(possibleMove)) != GetX(from))
{
needX = true;
}
else if (GetY(GetFromSquare(possibleMove)) != GetY(from))
{
needY = true;
}
else
{
assert(false);
}
}
}
if (needEither && !needX && !needY)
{
// if we can pick, use file
needX = true;
}
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.
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!
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.