Chess.jl icon indicating copy to clipboard operation
Chess.jl copied to clipboard

fen(...) returns incomplete FEN

Open avysk opened this issue 4 years ago • 7 comments

The fen() function returns incomplete FEN, missing fields 5 ("halfmove clock") and 6 ("fullmove number"). For example:

julia> b = fromfen("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")
Board (rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3):
 r  n  b  q  k  b  n  r
 p  p  p  p  p  p  p  p
 -  -  -  -  -  -  -  -
 -  -  -  -  -  -  -  -
 -  -  -  -  P  -  -  -
 -  -  -  -  -  -  -  -
 P  P  P  P  -  P  P  P
 R  N  B  Q  K  B  N  R

julia> fen(b)
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3"

The halfmove clock is easy to fix by using board.r50, but fullmove number requires adjustments to Board structure. I can provide a patch, but I'm not sure if the current behaviour is intentional (if it is, it would be nice to have it documented somewhere).

avysk avatar Jun 27 '21 10:06 avysk

Actually, the current behaviour, ignoring fields 5 and 6 in FEN, might potentially lead to bugs. Let's consider FEN of the position appearing after 1. Nf3 Nc6 2. Nf1 Nb8: it is rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 4 3.

julia> b = fromfen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 4 3")
Board (rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -):
 r  n  b  q  k  b  n  r
 p  p  p  p  p  p  p  p
 -  -  -  -  -  -  -  -
 -  -  -  -  -  -  -  -
 -  -  -  -  -  -  -  -
 -  -  -  -  -  -  -  -
 P  P  P  P  P  P  P  P
 R  N  B  Q  K  B  N  R

julia> b.r50
0x00  # This is incorrect!

avysk avatar Jun 27 '21 10:06 avysk

The r50 is of unsigned type and it is printed in hex. b.r50 contains a correct value.

Example:

julia> b = startboard();

julia> domoves!(b, "d4", "Nf6");

julia> b
Board (rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq -):
 r  n  b  q  k  b  -  r
 p  p  p  p  p  p  p  p
 -  -  -  -  -  n  -  -
 -  -  -  -  -  -  -  -
 -  -  -  P  -  -  -  -
 -  -  -  -  -  -  -  -
 P  P  P  -  P  P  P  P
 R  N  B  Q  K  B  N  R

julia> Int(b.r50)
1

Agree with your observation, the current fen() outputs an epd.

What I did was reconstruct the correct fen from current fen(), r50 and ply().

Example:

julia> g = Game();

julia> domoves!(g, "d4", "Nf6");

julia> g
Game:
 1. d4 Nf6 *

julia> b = board(g);

julia> correctfen = "$(fen(b)) $(b.r50) $(Int(ceil(ply(g)/2)))";

julia> correctfen
"rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2"

fsmosca avatar Jun 27 '21 14:06 fsmosca

The r50 is of unsigned type and it is printed in hex. b.r50 contains a correct value.

No, it does not. In the example above I've loaded FEN of a position that arose after moves 1. Nf3 Nc6 2. Nf1 Nb8. The value of r50 for this position is 4 (as correctly indicated in FEN I'm loading); however, after loading the position b.r50 is zero instead of 4.

Reconstruction of FEN from r50 and ply is possible if and only if the game is started from starting position, not if it's loaded from FEN. If the game is loaded from FEN with non-zero values of halfmove clock and fullmove number (as in example above: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 4 3) then r50 and ply produce incorrect values, because fen loading logic ignores fields 5 and 6 in FEN.

julia> gm = @simplegame f1f3 b8c6 f3f1 c6b8
SimpleGame:
 Bf3 Nc6 Bf1 Nb8 *

julia> ply(gm), gm.board.r50
(5, 0x04)  # CORRECT values: 4 half-moves played, r50 count is 4

julia> gm2 = SimpleGame("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 4 3")  # FEN indicates that r50 count is 4 and it's third move by White, i.e. 4 half-moves played
SimpleGame:
 *

julia> ply(gm2), gm2.board.r50
(1, 0x00)  # INCORRECT values, should be (5, 0x04)

avysk avatar Jun 27 '21 15:06 avysk

I understand that, when you create a game from fen like below,

gm2 = SimpleGame("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 4 3")

the r50 is wrong currently as it is not implemented yet. What is implemented is that the value of r50 is based on the actual moves in the game, but in the example above there are no moves. In my example I built a position from start position with moves to show that r50 is ok.

fsmosca avatar Jun 27 '21 15:06 fsmosca

Yes, we are talking about the same :) I can do a patch along the following lines:

  • add fen(gm::Game) method to emit full FEN, with r50 and move count
  • modify Game(startfen::String) to load r50 and move count, if given
  • (not sure?) rename fen(b::Board) to epd(b::Board) and add @deprecate fen(b::Board) epd(b::Board)

Would the author accept something like this, or will it go against the architecture the author has in mind?

avysk avatar Jun 27 '21 15:06 avysk

The r50 is one of the fields of the Board. The board is created from the input fen/epd/startfen string at fromfen(), looks like the r50 in the board has to be updated when there is an hmvc value in the input fen string. So perhaps fromfen() has to be modified.

The current master does not support fmvn but it has a ply() where fmvn can be derived. The game ply is initially set at 1. An input fen can have a value that is more than 1. The ply is one of the fields of the SimpleGame and GameNode. A game is created from the board. The board is created from the input fen at fromfen(), so perhaps the ply has to be defined also in fromfen() and in the Board to make sure that the ply is correctly initialized based from the input fen.

Yes the current fen() has to be replaced with epd() because it outputs like a string consistent with epd format. Perhaps also add fullepd() to output the hmvc and fmvn opcode values like, position startpos moves d2d4 g8f6, rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - hmvc 0; fmvn 2;

from the fen: rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq - 1 2

and for epd() rnbqkb1r/pppppppp/5n2/8/3P4/8/PPP1PPPP/RNBQKBNR w KQkq -

Ref: pgn standard on fen/epd sections 16.1 and 16.2 respecitvely.

fsmosca avatar Jun 27 '21 20:06 fsmosca

I am not sure if fmvn (unlike hmvc) should really belong to a Board and not to a Game. I think full move count is rather an attribute of "a position in a game", not just "a position".

avysk avatar Jun 28 '21 06:06 avysk