u-fischer / xskak

4 stars 0 forks source link

(x)skak & Chess960 -- castling problem #4

Open ThBrand opened 5 years ago

ThBrand commented 5 years ago

In the xskak chess960 development version castling doesn't work correktly, if the target square of the king is the starting square of the corresponding rook: In this case rook disappears, king(!) lands at rook's target square.

I suppose,

TBc960.txt

\documentclass{article} \usepackage{xskak} %development version 1.6!!

\begin{document} \makeatletter % set start position of rook for xskak: \renewcommand\xskak@king@pos{d} \renewcommand\xskak@rookshort@pos{g} \renewcommand\xskak@rooklong@pos{b} % set start position of rook for skak: \def\CastleRookFromFile#1{#1{b}{g}} \setchessboard{tinyboard} \textbf{a) Faulty short castling}

\newchessgame[setfen=nrbkqbrn/pppppppp/8/8/8/8/PPPPPPPP/NRBKQBRN, moveid=1w] \chessboard

\mainline{1.b3 e5 2.Bb2 Bc5 3.h3 Qe7 4.Ng3 O-O 5.O-O-O}

\chessboard

Black: \xskakset{moveid=4b}\xskakget{movefrom}--\xskakget{moveto}, White: \xskakset{moveid=5w}\xskakget{movefrom}--\xskakget{moveto} \par\bigskip % \textbf{b) Exchange Queens and kingside rooks -- short castling correct}

\renewcommand\xskak@rookshort@pos{e}
\def\CastleRookFromFile#1{#1{b}{e}}

\newchessgame[setfen=nrbkrbqn/pppppppp/8/8/8/8/PPPPPPPP/NRBKRBQN, moveid=1w] \chessboard

\mainline{1.b3 g5 2.Bb2 Bh6 3.h3 Qg6 4.Ng3 O-O 5.O-O-O}

\chessboard

Black: \xskakset{moveid=4b}\xskakget{movefrom}--\xskakget{moveto}, White: \xskakset{moveid=5w}\xskakget{movefrom}--\xskakget{moveto} \makeatother \end{document}

u-fischer commented 5 years ago

hm. Yes, castling works by first executing a kings move and then a rook move, and if the king replaces the rook this happens. I pushed a new version, which imho should work.

ThBrand commented 5 years ago

The new version seems to work correctly now also in this funny special case-- great job!

sorsted commented 1 year ago

It’s been three years, when will this version 1.6 be finished and added to CTAN?

sorsted commented 1 year ago

Hi Ulrike,

As agreed in the chat on TeX.SX, I will try to write some example for the documentation. The problem is that the interface has to be set up first, so I suggest that we discuss how to make this work. Obviously, it’s not the right solution to ask the user to redefine the four commands \xskak@king@pos, \xskak@rookshort@pos, \xskak@rooklong@pos, and \CastleRookFromFile, so this should be automated.

Chess960 (also known by its original name, Fischer Random Chess -- what a coincidence, Ulrike Fischer! ;-)) has 960 possible positions for the pieces, enumerated from “SP 0” to “SP 959”. So the user should probably be allowed to choose the positions both using the number (e.g. fischersp=455) and using the string (e.g. fischerpos=RNBNKQRB or fischerpos=rnbnkqrb; I suggest allowing both upper and lower case). Alternatively, we could try and make it work with one key allowing both types of syntax, e.g. fischer=455, fischer=RNBNKQRB, and fischer=rnbnkqrb. This would probably be a lot easier for the user. Of course, the key should issue an error if the argument is not either a number between 0 and 959 or one of the allowed strings of letters.

This would mean that the package would need to know each of these positions in advance. So we need a command, called something like \xskak@add@fischer, which we need to run for each of the 960 positions. You can choose whether you want to extract the king position, the short, and the long rook positions from the strings via TeX commands or want to save (probably a small amount of) time by preprocessing this.

The important thing is that the the Chess960 number has to be saved for each individual game, as the system cannot figure out how to castle without it. So it should be saved along with the game id and changed when you go between different games. So hopefully, when the interface is fully implemented, it should look something like this:

\documentclass{article}
\usepackage{xskak} %development version 1.6!!

\begin{document}

\setchessboard{tinyboard,showmover=false}

% This part is supposed to become redundant:

\makeatletter
% set start position of rook for xskak:
\renewcommand\xskak@king@pos{e}
\renewcommand\xskak@rookshort@pos{g}
\renewcommand\xskak@rooklong@pos{a}

% set start position of rook for skak:
\def\CastleRookFromFile#1{#1{g}{a}}

\makeatother

\newchessgame[
    id=main,
%   fischer=455, % this is how the interface is supposed to work
    setfen=rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Kkq - 4 11,
    moveid=11w,
]

\chessboard

\mainline{11. O-O}

\chessboard

\end{document}
u-fischer commented 1 year ago

well I don't think that I want to load 1000 positions into memory only for the case that someone perhaps wants to play a chess960 game. Something like that can be done in some external package.

What I can do is add a key "initcastling" which goes through the first line (and perhaps also last line) of the fen and update the rook/king positions for castling. Then every starting position can be set with a setfen.

sorsted commented 1 year ago

@u-fischer The problem is that, in Chess960, the castling positions cannot be unambiguously recovered from the FEN notation of a game in progress. Quoting from Wikipedia:

FEN is capable of expressing all possible starting positions of Fischer random chess; however, unmodified FEN cannot express all possible positions of a Chess960 game. In a game, a rook may move into the back row on the same side of the king as the other rook, or pawn(s) may be underpromoted into rook(s) and moved into the back row. If a rook is unmoved and can still castle, yet there is more than one rook on that side, FEN notation as traditionally interpreted is ambiguous. This is because FEN records that castling is possible on that side, but not which rook is still allowed to castle.

This is what the extension, X-FEN, is for, as documented in the next paragraph on Wikipedia.

So therefore, if you don’t feel like supporting X-FEN (and I understand why!), the system strictly needs to know which Chess960 starting position is being played. As you say, doing this in an external package could be an option. Alternatively, there is a relatively simple algorithm to generate the positions from the numbers alone. This could easily be coded in expl3 syntax. I could probably do that for you if this is the approach you prefer.

sorsted commented 1 year ago

Actually, the easiest way is probably to support arbitrary shuffle chess, of which Chess960 is a special case. As far as I can see, an arbitrary shuffle chess game can be uniquely determined by the following data:

For Chess960, the last four pieces of data never change, but they do for some other variants such as Chess480.

So all we strictly speaking need is keys for these five pieces of data. Of course, these all have to be stored for each individual game and attached to the game ID.

Then if we feel like it, we can have a key fischer which generates the Chess960 positions from a number between 0 and 959, but this is not strictly speaking necessary as the user can always make this conversion by other means, e.g. by looking it up. If such a key is defined, it should also reset the other data to their standard values in case these have been changed.

sorsted commented 1 year ago

Here is a draft of a command that converts numbers to starting positions, using the algorithm described in the link above. It is fully expandable and (hopefully) as efficient as possible. I’ve tested the function for all 960 values, and it yields the right result.

\documentclass{article}

\ExplSyntaxOn

\cs_new:Npn\__insert_bishops_and_queen:nnnn#1#2#3#4
{
    % #1 = the string
    % #2 = 2 * B2 + 1
    % #3 = 2 * (B1 + 1)
    % #4 = Q + 1
    \__insert_bishops_and_queen_auxiliary:ooon
        { \int_min:nn { #2 } { #3 } }
        { \int_max:nn { #2 } { #3 } }
        { \int_eval:n { #4 } }
        { #1 }
}

\cs_new:Npn\__insert_bishops_and_queen_auxiliary:nnnn#1#2#3#4
{
    \int_compare:nNnTF { #3 } < { #1 }
    {
        \tl_range_unbraced:nnn { #4 } { 1 } { #3 - 1 }
        Q
        \tl_range_unbraced:nnn { #4 } { #3 } { #1 - 2 }
        B
        \tl_range_unbraced:nnn { #4 } { #1 - 1 } { #2 - 3 }
        B
        \tl_range_unbraced:nnn { #4 } { #2 - 2 } { -1 }
    }
    {
        \int_compare:nNnTF { #3 } < { #2 - 1 }
        {
            \tl_range_unbraced:nnn { #4 } { 1 } { #1 - 1 }
            B
            \tl_range_unbraced:nnn { #4 } { #1 } { #3 - 1 }
            Q
            \tl_range_unbraced:nnn { #4 } { #3 } { #2 - 3 }
            B
            \tl_range_unbraced:nnn { #4 } { #2 - 2 } { -1 }
        }
        {
            \tl_range_unbraced:nnn { #4 } { 1 } { #1 - 1 }
            B
            \tl_range_unbraced:nnn { #4 } { #1 } { #2 - 2 }
            B
            \tl_range_unbraced:nnn { #4 } { #2 - 1 } { #3 - 1 }
            Q
            \tl_range_unbraced:nnn { #4 } { #3 } { -1 }
        }
    }
}

\cs_generate_variant:Nn \__insert_bishops_and_queen_auxiliary:nnnn { ooon }

\cs_new:Npn\fischer:n#1
{
    \__fischer_auxiliary_i:oo
        { \int_div_truncate:nn { #1 } { 4 } }
        { \int_mod:nn { #1 } { 4 } }
}

\cs_new:Npn\__fischer_auxiliary_i:nn#1#2
{
    \__fischer_auxiliary_ii:oon
        { \int_div_truncate:nn { #1 } { 4 } }
        { \int_mod:nn { #1 } { 4 } }
        { #2 }
}

\cs_generate_variant:Nn \__fischer_auxiliary_i:nn { oo }

\cs_new:Npn\__fischer_auxiliary_ii:nnn#1#2#3
{
    \__fischer_auxiliary_iii:oonn
        { \int_div_truncate:nn { #1 } { 6 } }
        { \int_mod:nn { #1 } { 6 } }
        { #2 }
        { #3 }
}

\cs_generate_variant:Nn \__fischer_auxiliary_ii:nnn { oon }

\cs_new:Npn\__fischer_auxiliary_iii:nnnn#1#2#3#4
{
    % #1 = N4
    % #2 = Q
    % #3 = B2
    % #4 = B1
    \int_case:nn { #1 }
    {
        { 0 } { \__insert_bishops_and_queen:nnnn { NNRKR } }
        { 1 } { \__insert_bishops_and_queen:nnnn { NRNKR } }
        { 2 } { \__insert_bishops_and_queen:nnnn { NRKNR } }
        { 3 } { \__insert_bishops_and_queen:nnnn { NRKRN } }
        { 4 } { \__insert_bishops_and_queen:nnnn { RNNKR } }
        { 5 } { \__insert_bishops_and_queen:nnnn { RNKNR } }
        { 6 } { \__insert_bishops_and_queen:nnnn { RNKRN } }
        { 7 } { \__insert_bishops_and_queen:nnnn { RKNNR } }
        { 8 } { \__insert_bishops_and_queen:nnnn { RKNRN } }
        { 9 } { \__insert_bishops_and_queen:nnnn { RKRNN } }
    }
    { 2 * #3 + 1 } { 2 * #4 + 2 } { #2 + 1 }
}

\cs_generate_variant:Nn \__fischer_auxiliary_iii:nnnn { oonn }

\cs_set_eq:NN\fischer\fischer:n

\ExplSyntaxOff

\begin{document}

\ExplSyntaxOn

\noindent
\int_step_inline:nnn { 0 } { 959 } { #1:~\fischer:n { #1 } \\ }

\ExplSyntaxOff

\end{document}