2

In my previous question, How to set macro for looping with minipage?, I was suggested using a foreach loop to solve the problem. After testing, the loop works correctly as show in page 1 pdf. However, when more FENs are added, loop gives wrong format as shown in pdf 2 and 3. Any suggestion on how to fix the foreach loop is appreciated.

\documentclass{article}
\usepackage{xskak,chessboard}
\usepackage[margin=3cm]{geometry}
\begin{document}

%**** WORKSHEET 1 IS CORRECT; WORKSHEET 2 AND 3 IS WRONG ******
%********************************************************
%******CORRECT WORKSHEET USING FOREACH LOOP *******

\foreach \X/\Y in 
{basic/{3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
%******* PAWN LEVEL *****************
pawn/{8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
%************ 2 PAWN LEVEL *******************
2 pawns/{8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
%************ MINOR + PAWN LEVEL ********************
Minor + Pawns/{4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
 8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1}}
{\noindent\tikz{\node[rotate=90,font=\bfseries,align=center,text width=4cm]{\X};}
\foreach \Z in \Y
{\begin{minipage}[b]{0.35\linewidth}
\chessboard[smallboard,setfen=\Z]
\end{minipage}}

}
%****** WHEN NEW FENS ARE ADDED, FOREACH LOOP GETS WRONG. WORKSHEET IS NOT SAME AS **
%**** PREVIOUS WORKSHEET *****
\foreach \X/\Y in 
{basic/{3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
basic/{3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
%******* PAWN LEVEL *****************
pawn/{8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
pawn/{8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
%************ 2 PAWN LEVEL *******************
2 pawns/{8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
2 pawns/{8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
%************ MINOR + PAWN LEVEL ********************
Minor + Pawns/{4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1},
Minor + Pawns/{4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1}}
{\noindent\tikz{\node[rotate=90,font=\bfseries,align=center,text width=4cm]{\X};}
\foreach \Z in \Y
{\begin{minipage}[b]{0.35\linewidth}
\chessboard[smallboard,setfen=\Z]
\end{minipage}}
\par
}
\end{document}
  • The code runs through without an error on my TeXLive 2019 installation when compiled with pdflatex. Any chance you mean by error "undesired output"? If so, what would you like to have changed? – marmot Jun 22 at 20:31
  • if you want to repeat the first page you should better store it in a box and reuse that box. That would be faster. But beside this: if you have lots of positions if would imho cleaner to store them first in some data structure, eg. an expl3-sequence and then to loop over this sequence. – Ulrike Fischer Jun 22 at 20:43
  • @Marmot - your code works correctly. As stated, the ideal output is page 1. Your codes produce page 2 and 3 which is not like page 1. The sheet must pull data from each of the 4 group and lay them out like in page 1. – user92127 Jun 23 at 0:09
  • @UlrikeFischer - I am trying to learn about expl3. Unfortunately, there's no "Expl3 for dummies" so I am stuck. I am looking for an example which I can test. I don't see examples done with chess before. Any suggestion is appreciated. – user92127 Jun 23 at 0:12
  • Like marmot, I don't understand what the problem is with pages 2 and 3. Can you say more clearly what you mean by "page 2 and 3 which is not like page 1"? – Andrew Jun 23 at 0:23
3

Here is one solution using expl3:

\documentclass{article}
\usepackage{chessboard}
\usepackage{xskak}
\usepackage[margin=2cm]{geometry}
\usepackage{tikz}
\usepackage{xparse}

\ExplSyntaxOn

\msg_new:nnn { mychess } { groups-have-different-number-of-series }
  { all~groups~must~have~the~same~number~of~series~ (found~\exp_not:n {#1}~
    and~\exp_not:n {#2}). }

\seq_new:N \g_mychess_groups_seq

\cs_new_protected:Npn \mychess_define_groups:n #1
  {
    \seq_gset_from_clist:Nn \g_mychess_groups_seq {#1}
  }

% #1: group name (A, B, C, ...)
% #2: diagram data for this group (a comma list)
\cs_new_protected:Npn \mychess_set_group_diagrams:nn #1#2
  {
    \seq_gclear_new:c { g_mychess_group_#1_seq }
    \seq_gset_from_clist:cn { g_mychess_group_#1_seq } {#2}
  }

% Expand to the number of series declared for group #1
\cs_new:Npn \mychess_nb_series_for_group:n #1
  {
    \seq_count:c { g_mychess_group_#1_seq }
  }

\cs_generate_variant:Nn \mychess_nb_series_for_group:n { V }
\cs_generate_variant:Nn \msg_error:nnnn { nnVx }

\tl_new:N \l__mychess_a_group_tl
\int_new:N \l__mychess_nb_series_int

\cs_new_protected:Npn \mychess_print_all_diagrams:
  {
    % Get the name of the first group and the number of series declared for
    % this group (any other group would do, as they must have the same number
    % of series---which we are going to check right now as a safety measure
    % against invalid user input).
    \seq_get_left:NN \g_mychess_groups_seq \l__mychess_a_group_tl
    \int_set:Nn \l__mychess_nb_series_int
      { \mychess_nb_series_for_group:V \l__mychess_a_group_tl }

    % Check that the same number of series was defined for each group, and
    % print an error message if this is not the case.
    \seq_map_inline:Nn \g_mychess_groups_seq
      {
        \int_compare:nNnF
          { \mychess_nb_series_for_group:n {##1} }
          =
          { \l__mychess_nb_series_int }
          {
            \msg_error:nnVx { mychess }
              { groups-have-different-number-of-series }
              \l__mychess_nb_series_int
              { \mychess_nb_series_for_group:n {##1} }
          }
      }

    % Print all diagrams in series order, then in group order inside each
    % series.
    \int_step_function:nN { \l__mychess_nb_series_int }
      \mychess_print_diagrams_for_series:n
  }

\seq_new:N \l__mychess_print_one_diagram_set_tmp_seq

% #1: group name
% #2: data (a comma list: of rounds? Not sure about what the data represents)
\cs_new_protected:Npn \mychess_print_one_diagram_set:nn #1#2
  {
    \par \noindent
    \tikz { \node[rotate=90,font=\bfseries,align=center,text~width=4cm] {#1}; }
    \seq_set_from_clist:Nn \l__mychess_print_one_diagram_set_tmp_seq {#2}
    \seq_map_inline:Nn \l__mychess_print_one_diagram_set_tmp_seq
      {
        \chessboard[smallboard,setfen=##1]
        \skip_horizontal:n { \fill }
      }
    \par
  }

\cs_generate_variant:Nn \mychess_print_one_diagram_set:nn { nx }

% #1: 1 for the first series, 2 for second, etc.
\cs_new_protected:Npn \mychess_print_diagrams_for_series:n #1
  {
    \seq_map_inline:Nn \g_mychess_groups_seq
      {
        \mychess_print_one_diagram_set:nx
          {##1}                 % group name
          { \seq_item:cn { g_mychess_group_##1_seq } {#1} }
      }
  }

% Processing the “big structure”
\seq_new:N \g__mychess_rosfg_discovered_groups_seq

% #1: group name
% #2: data (a comma list: of rounds? Not sure about what the data represents)
\cs_new_protected:Npn \mychess_read_one_series_for_group:nn #1#2
  {
    \seq_if_in:NnF \g__mychess_rosfg_discovered_groups_seq {#1}
      { % First time we see group #1 in the “big structure”
        \seq_gput_right:Nn \g__mychess_rosfg_discovered_groups_seq {#1}
        % Clear all data for this group
        \seq_gclear_new:c { g_mychess_group_#1_seq }
      }

    % Append the data to what we already have for this group
    \seq_gput_right:cn { g_mychess_group_#1_seq } {#2}
  }

\cs_generate_variant:Nn \mychess_read_one_series_for_group:nn { VV }

\cs_new_protected:Npn \mychess_read_big_structure:n #1
  {
    \seq_gclear:N \g__mychess_rosfg_discovered_groups_seq

    \foreach \group / \data in {#1}
      { \mychess_read_one_series_for_group:VV \group \data }

    \seq_gset_eq:NN \g_mychess_groups_seq
                    \g__mychess_rosfg_discovered_groups_seq
  }

% #1: comma list of group names
\NewDocumentCommand \definegroups { m }
  {
    \mychess_define_groups:n {#1}
  }

% #1: group name
% #2: data for this group (a comma list containing one item per series, each
% item being itself a comma list describing the diagrams for the (group,
% series) combination).
\NewDocumentCommand \setgroupdiagrams { m m }
  {
    \mychess_set_group_diagrams:nn {#1} {#2}
  }

\NewDocumentCommand \readbigstructure { m }
  {
    \mychess_read_big_structure:n {#1}
  }

\NewDocumentCommand \printalldiagrams { }
  {
    \mychess_print_all_diagrams:
  }

\ExplSyntaxOff

\begin{document}

% Load the data as provided in the question
\readbigstructure{%
basic/{3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
basic/{3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
%******* PAWN LEVEL *****************
pawn/{8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
pawn/{8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
%************ 2 PAWN LEVEL *******************
2 pawns/{8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
2 pawns/{8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
%************ MINOR + PAWN LEVEL ********************
Minor + Pawns/{4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1},
Minor + Pawns/{4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1}}

% Print it
\printalldiagrams

\newpage
% Alternate input syntax written for the duplicate question:
% <https://tex.stackexchange.com/questions/497019/foreach-looping-error>
\definegroups{A, B, C, D}
\setgroupdiagrams{A}{
  {3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
   8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
   1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
  {3k4/8/2Q5/4K3/8/8/8/8 w - - 0 1,
   8/8/2k5/7R/6R1/8/5K2/8 w - - 0 1,
   1k6/7R/3K4/8/8/8/8/8 w - - 0 1},
}
\setgroupdiagrams{B}{
  {8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
   8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
   8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
  {8/8/8/8/8/1k6/7P/4K3 w - - 0 1,
   8/8/8/8/4k3/4p3/4K3/8 w - - 0 1,
   8/1k6/8/8/1PK5/8/8/8 w - - 0 1},
}
\setgroupdiagrams{C}{
  {8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
   8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
   k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
  {8/8/8/4p1K1/2k1P3/8/8/8 w - - 0 1,
   8/8/8/p7/P7/7k/7P/6K1 w - - 0 1,
   k7/P1K5/6p1/7p/7P/8/8/8 w - - 0 1},
}
\setgroupdiagrams{D}{
  {4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
   8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
   8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1},
  {4BK2/6P1/5k2/8/8/1b6/8/8 w - - 0 1,
   8/8/4k3/2p4n/2P1K3/8/7B/8 w - - 0 1,
   8/p7/P5p1/1P4P1/5P2/3kb3/8/3K4 w - - 0 1},
}
\printalldiagrams

\end{document}

Page 1

Page 1

(page 2 has the same diagrams as page 1 because the input data is the same)

Page 3

Page 3

(page 4 has the same diagrams as page 3 because the input data is the same)

Notes:

  • The alternate input syntax used the second time (see end of the document) declares group names A, B, C and D as in this duplicate question.

  • You can use \readbigstructure, \definegroups and \setgroupdiagrams before or after \begin{document}, it doesn't matter (these commands don't produce any output, contrary to \printalldiagrams).

If you want to learn expl3, the best starting point I know is expl3.pdf found here, then l3styleguide.pdf1 and finally the main reference, interface3.pdf. In any case, reading the TeXbook before will be of great help, because many essential TeX concepts2 are underlying everywhere in the LaTeX3 language. For instance, once you understand \edef, you'll know pretty well what x argument specifiers do and why they can't be used in such and such situations.


Footnotes:

  1. Very short; you may skip the Documentation style section at this point.

  2. In particular, character and category codes, tokens, how characters from the input are turned into tokens and how the resulting stream of tokens is processed (part of which is the concept of expansion), \edef, \csname and \string.

  • 1
    The awkward \cs_if_exist:cF construction should be replaced by \seq_clear_new:c { l_mychess_group_#1_seq } – egreg Jun 23 at 9:20
  • @egreg Thanks for the hint, this is indeed nicer! I've updated the code to also be able to handle the precise input syntax used in the question (tailored for pgffor's \foreach, all the data declared in one block). – frougon Jun 23 at 10:13
  • @frougon - Thank you so much for your codes. Greatly appreciated. Can you recommend resources so I can learn about this feature. I never know that Latex is also about programming. – user92127 Jun 24 at 0:43
  • I was out of the house and am now back. I compile the code you provided and overleaf shows 86 error. Here's the project: overleaf.com/2711293339kwchqmcgjcwg – user92127 Jun 24 at 1:09
  • @user92127 Okay, sorry but I have no account at overleaf. You can paste the log at for instance pastebin.com but since it all works fine here, I'd say it isn't likely to reveal a problem in the code. But at least it could allow us to rule out some kinds of “user error”. :-) Learning LaTeX programming is a long process. First you need to learn how to use LaTeX (LaTeX book by Leslie Lamport [useful when starting but a it dated], parts of the LaTeX Companion, online tutorials—see here)... – frougon Jun 24 at 5:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.