% \iffalse meta-comment
%
% Copyright (C) 2017 by Thomas Simers
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either
% version 1.3 of this license or (at your option) any later
% version. The latest version of this license is in:
%
%   http://www.latex-project.org/lppl.txt
%
% and version 1.3 or later is part of all distributions of
% LaTeX version 2005/12/01 or later.
%
% \fi
%
% \iffalse
%<package>\NeedsTeXFormat{LaTeX2e}[2005/12/01]
%<package>\ProvidesPackage{soup}[2019/04/05 v1.0.2 Package for word search puzzles.]
%
%<*driver>
\documentclass[full]{l3doc}
\usepackage[highlight]{soup}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
\DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
% \changes{v1.0}{2017/01/10}{Initial version}
% \changes{v1.0.2}{2019/04/05}{Update to work with changes to expl3 kernel.}
% \GetFileInfo{soup.sty}
%
% \DoNotIndex{\#,\$,\%,\&,\@,\\,\{,\},\^,\_,\~,\ }
% \DoNotIndex{\@ne}
% \DoNotIndex{\advance,\begingroup,\catcode,\closein}
% \DoNotIndex{\closeout,\day,\def,\edef,\else,\empty,\endgroup}
% \DoNotIndex{\noindent,\begin,\end,\ExplSyntaxOn,\ExplSyntaxOff,\equal,}
% \DoNotIndex{\par,\ProcessKeysPackageOptions,\ProcessKeysOptions,\vspace}
% \DoNotIndex{\draw,\node}
% \DoNotIndex{\RequirePackage,\NewDocumentCommand,\NewDocumentEnvironment}
%
%
% \title{The \pkg{soup} package
%  \thanks{This document corresponds to \pkg{soup}~\fileversion,
% last revised~\filedate.}}
%
% \author{Thomas Simers
%  \thanks{E-mail: \href{mailto:Simers.T@gmail.com}{Simers.T@gmail.com}}
% }
%
% \date{Released \filedate}
%
% \maketitle
%
%
% \vspace*{\fill}\noindent
% \begin{minipage}{1.8in}
% \begin{alphabetsoup}*[6][6][\sffamily]
%   \hideinsoup{2}{2}{right}{s,o,u,p}
%   \hideinsoup*{6}{2}{downleft}{s,o,u,p}
% \end{alphabetsoup}
% \end{minipage}
% \begin{minipage}{1.8in}
% \begin{numbersoup}*[6][6]{9}
%   \hideinsoup{3}{3}{right}{1,0}
%   \hideinsoup*{5}{3}{downleft}{1,0}
% \end{numbersoup}
% \end{minipage}
% \vspace*{\fill}
% \begin{minipage}{1.8in}
% \begin{homemadesoup}*[6][6]{
%     $\nearrow$, $\searrow$,   $\nwarrow$,   $\swarrow$,
%     $\uparrow$, $\downarrow$, $\leftarrow$, $\rightarrow$
% }[\small]
%   \hideinsoup{6}{2}{downleft}{$\swarrow$,$\swarrow$,$\swarrow$,$\swarrow$}
% \end{homemadesoup}
% \end{minipage}
%
% \begin{abstract}
%  The goal of \pkg{soup} is to generate the grid of letters for
%  a word search, puzzle sometimes called ``alphabet soup'' (from which
%  this package gets its name) or ``find-the-word.''
%
%  In addition to supporting classic word searches, the soup can be
%  filled with numbers or a user-defined set of glyphs.
%
%  Full functionality relies on TikZ, but limited support without TikZ
%  is available through a package option.
% \end{abstract}
%
% \vspace*{\fill}
% \clearpage{}
%
%
% \section{User Guide}
%
% The \pkg{soup} interface is rests primarily in two parts:
% The environments which determine the type of soup (alphabet, number,
% or homemade), and the shared macros for inserting and marking clues.
%
%
% \subsection{Load-Time Options}
%
%
% \begin{variable}{usetikz}
% \begin{syntax}
%   \cs{usepackage} \oarg{usetikz=false} \{soup\}
% \end{syntax}
% Usually, \pkg{soup} will use TikZ to draw the soup grid and provide
% the optional highlighting of clues.
%
% To disable this, and use a non-TikZ fallback (the \env{tabular} environment),
% pass the option |usetikz=false| when loading \pkg{soup}.
% \end{variable}
%
%
% \begin{variable}{highlight}
% \begin{syntax}
%   \cs{usepackage} \oarg{highlight=true} \{soup\}
% \end{syntax}
% As a puzzle generator, \pkg{soup} does not usually indicate the solution.
%
% To have \pkg{soup} highlight the solutions, pass the option |highlight|
% (or |highlight=true|) when loading \pkg{soup}.
%
% If TikZ is disabled, the solutions will be indicated with boldface letters.
% Note that if the the puzzle is drawn in boldface, this will hide the
% highlighting.
% \end{variable}
%
%
% \begin{variable}{highlightcolor}
% \begin{syntax}
%   \cs{usepackage} \oarg{highlightcolor=\emph{color}} \{soup\}
% \end{syntax}
% Specify the fill color to be used when highlighting solutions (TikZ only).
%
% The default color is |orange|.
%
% Color mixes are fine here, too: |green!50!white|.
% \end{variable}
%
%
% \begin{variable}{linecolor}
% \begin{syntax}
%   \cs{usepackage} \oarg{linecolor=\emph{color}} \{soup\}
% \end{syntax}
% Specify the line color to be used when highlighting solutions (TikZ only).
%
% The default color is |red|.
%
% Color mixes are fine here, too: |green!20!black|.
% \end{variable}
%
%
% \subsection{Environments}
%
%
% \begin{function}{alphabetsoup, alphabetsoup*,Alphabetsoup,Alphabetsoup*}
% \begin{syntax}
%    \cs{begin\{alphabetsoup\}}~ \oarg{width} \oarg{height} \oarg{font}
%    \cs{begin\{alphabetsoup\}}* \oarg{width} \oarg{height} \oarg{font}
%    \cs{begin\{Alphabetsoup\}}~ \oarg{width} \oarg{height} \oarg{font}
%    \cs{begin\{Alphabetsoup\}}* \oarg{width} \oarg{height} \oarg{font}
% \end{syntax}
%
% An \env{alphabetsoup} environment will build a grid of letters
% using lowercase Latin a--z, weighted for their frequence in English words.
% The \env{Alphabetsoup} environment uses uppercase A--Z.
% (For other alphabets, use a custom
% \env{homemadesoup}.)
%
% A list of clues will be included after the grid. Use the starred version
% to omit the list. (To include the list later, use |\listofclues|.)
%
% If the \meta{height} is omitted, the number of rows will
% be the same as the number of columns.
%
% If the \meta{width} is omitted, it will default to 20.
%
% Therefore, with no parameters, a 20-by-20 grid of letters will be generated.
%
% \meta{font} can be optionally used to set the size of the letters in the soup
% (e.g., |\Large|, |\scriptsize|) or other font-related commands
% (e.g., |\sffamily|, |\itshape|)
% \end{function}
%
%
% \begin{function}{numbersoup, numbersoup*}
% \begin{syntax}
%    \cs{begin\{numbersoup\}}~ \oarg{width} \oarg{height} \marg{max} \oarg{min} \oarg{font}
%    \cs{begin\{numbersoup\}}* \oarg{width} \oarg{height} \marg{max} \oarg{min} \oarg{font}
% \end{syntax}
%
% The \env{numbersoup} environment follows \env{alphabetsoup} with two
% important differences:
% \begin{itemize}
%   \item
%   The grid is filled with numbers (not letters)
%
%   \item
%   Numbers are between \meta{min} (or 0 if omitted) and \meta{max}, inclusive.
% \end{itemize}
%
% The \meta{max} must be specified.
% \end{function}
%
%
% \begin{function}{homemadesoup, homemadesoup*}
% \begin{syntax}
%    \cs{begin\{homemadesoup\}}~ \oarg{width} \oarg{height} \marg{symbols} \oarg{font}
%    \cs{begin\{homemadesoup\}}* \oarg{width} \oarg{height} \marg{symbols} \oarg{font}
% \end{syntax}
%
% Instead of filling with digits or letters, the soup will be filled
% randomly from the user-specified comma-separated list \meta{symbols}
%
% \end{function}
%
%
% \subsection{Macros}
%
%
% \begin{function}{\hideinsoup, \hideinsoup*}
%  \begin{syntax}
%    \cs{hideinsoup} \marg{x} \marg{y} \marg{dir} \marg{seq} \oarg{clue}
%  \end{syntax}
%
% Generally, an alphabetsoup will have words hidden in it.
% Other soups will have appropriate clues hidden (e.g., a number series).
%
% These are put in the soup with |\hideinsoup|.
%
% If two words overlap, and the overlapping letters (or other symbols)
% are different, \pkg{soup} will issue a warning, and it will display
% \emph{both} letters in the grid, separated by a slash.
%
% If highlighting is enabled, |\hideinsoup| will call |\highlightinsoup|.
% Use the starred version, |\hideinsoup*| to avoid this behavior.
%
% If \pkg{soup} was loaded with |usetikz=false|, the highlighting
% of hidden clues will be simple boldface. The starred version will
% have no effect on this.
% \end{function}
%
%
% \begin{function}{\highlightinsoup}
%  \begin{syntax}
%    \cs{highlightinsoup} \marg{x1} \marg{y1} \marg{x2} \marg{y2}
%  \end{syntax}
%
% Highlights the word (or sequence of symbols) between (\meta{x1},\meta{y1})
% and (\meta{x2},\meta{y2}), where (1,1) is the top left of the soup grid,
% (2,1) is to the right of the top left, and (1,2) is the first symbol in
% the second row.
%
% If \pkg{soup} was loaded with |usetikz=false|, this macro will have no effect.
% \end{function}
%
%
% \begin{function}{\listofclues}
%  \begin{syntax}
%    \cs{listofclues} \oarg{format}
%  \end{syntax}
%
%  Displays a list of all clues for the current puzzle.
%
%  The optional \meta{format} should use |\theclue| where the text of
%  the clue should appear.
%
%  Must be used after all uses of |\hideinsoup| for the current soup.
%  If included before |\end{...soup}|, the clues will appear \emph{before}
%  the soup. If includes after |\end{...soup}|, then they will appear
%  \emph{after} the soup.
%
%  A typical use might be to display the clues as an enumerated list in columns:
%
%  \begin{verbatim}
%    \begin{alphabetsoup}*
%        ...
%    \end{alphabetsoup}
%    \begin{multicols}{3}
%        \begin{enumerate}
%           \listofclues[\item \theclue]
%        \end{enumerate}
%    \end{multicols}
%  \end{verbatim}
% \end{function}
%
%
%
% \StopEventually{\clearpage{}\PrintChanges\clearpage{}\PrintIndex}
%
%
%
% \section{Implementation}
%
%
% \subsection{Dependencies}
%
%
%     \begin{macrocode}
\RequirePackage{xparse}
\RequirePackage{expl3}
\RequirePackage{l3keys2e}
%    \end{macrocode}
%
%
% \subsection{Initialization and Parameter Handling}
%
%
%    \begin{macrocode}
\ExplSyntaxOn

\msg_new:nnn{soup}{mismatch}{
    Clue~mismatch~at~#1.~Will~appear~as~#2/#3~in~the~soup.
}

\bool_new:N \g_soup_use_tikz_bool
\bool_gset_true:N \g_soup_use_tikz_bool

\bool_new:N \g_soup_highlight_bool
\bool_gset_false:N \g_soup_highlight_bool

\tl_new:N \g_soup_highlight_color
\tl_gset:Nn \g_soup_highlight_color {orange}

\tl_new:N \g_soup_line_color
\tl_gset:Nn \g_soup_line_color {red}

\keys_define:nn { soup }{
  highlightcolor .initial:n        = orange,
  highlightcolor .value_required:n = true,
  highlightcolor .code:n           = \tl_set:Nn \g_soup_highlight_color {#1},
  linecolor      .initial:n        = red,
  linecolor      .value_required:n = true,
  linecolor      .code:n           = \tl_set:Nn \g_soup_line_color {#1},
  highlight      .default:n        = true,
  highlight      .bool_set:N       = \g_soup_highlight_bool,
  usetikz        .default:n        = true,
  usetikz        .bool_set:N       = \g_soup_use_tikz_bool,
}

\ProcessKeysPackageOptions{ soup }
\IfBooleanT \g_soup_use_tikz_bool {
    \RequirePackage{tikz}
}
\clist_const:Nn \c_soup_Alphabet_clist {
    A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,
    E,T,A,O,H,N,I,S,R,D,L,U,W,M,C,G,F,Y,P,V,K,B,J,
    E,T,A,O,H,N,I,S,R,D,L,U,W,M,C,G,F,Y,P,V,K,B,
    E,T,A,O,H,N,I,S,R,D,L,U,W,M,
    E,T,A,O,H,N,I,S,
    E,T,A,O,H,
}

\clist_const:Nn \c_soup_alphabet_clist {
    a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
    e,t,a,o,h,n,i,s,r,d,l,u,w,m,c,g,f,y,p,v,k,b,j,
    e,t,a,o,h,n,i,s,r,d,l,u,w,m,c,g,f,y,p,v,k,b,
    e,t,a,o,h,n,i,s,r,d,l,u,w,m,
    e,t,a,o,h,n,i,s,
    e,t,a,o,h,
}

\prop_new:N \g_soup_data_prop
\seq_new:N \g_soup_clue_seq
%    \end{macrocode}
%
%
% \subsection{Internal Functions}
%
%
% \begin{macro}[aux]{\__soup_init:nn}
% Resets the storage in preparation for a new soup.
%    \begin{macrocode}
\cs_new:Nn \__soup_init:nn {
    \clist_clear_new:N \g_soup_symbol_clist
    \dim_gzero_new:N \g_soup_highlight_dim
    \dim_gzero_new:N \g_soup_spacing_dim
    \int_gzero_new:N \g_soup_columns_int
    \int_gzero_new:N \g_soup_number_max_int
    \int_gzero_new:N \g_soup_number_min_int
    \int_gzero_new:N \g_soup_number_range_int
    \int_gzero_new:N \g_soup_rows_int
    \int_gzero_new:N \g_soup_symbol_count_int
    \prop_clear_new:N \g_soup_data_prop
    \seq_clear_new:N \g_soup_clue_seq
    \seq_clear_new:N \g_soup_highlight_seq
    \int_gset:Nn \g_soup_columns_int {#1}
    \IfNoValueTF{#2} {
       \int_gset:Nn \g_soup_rows_int {\g_soup_columns_int}
    }{
       \int_gset:Nn \g_soup_rows_int {#2}
    }
    \dim_gset:Nn \g_soup_spacing_dim {\textwidth / (\g_soup_columns_int + 1)}
    \dim_gset:Nn \g_soup_highlight_dim {\g_soup_spacing_dim * 7 / 10}
    \tl_clear_new:N \g_soup_font_tl
    \tl_gset:Nn \g_soup_font_tl {\normalfont}
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_random_int:nn}
% Returns a pseudo-random integer between |#1| and |#2|.
%
% \url{https://en.wikipedia.org/wiki/Lehmer_random_number_generator}
%    \begin{macrocode}
\int_gzero_new:N \g__soup_random_previous_int
\int_gzero_new:N \g__soup_random_current_int
\cs_new:Nn \__soup_random_int:nn {
    \int_compare:nNnT \g__soup_random_previous_int = 0 {
        \int_gset:Nn \g__soup_random_previous_int {\time}
    }
    % A = 16807,  Q = 127773 (M / A), R = 2836 (M % A), M = 2147483647 (2^31-1)
    \int_zero_new:N \l__hi_int
    \int_zero_new:N \l__lo_int
    \int_set:Nn \l__hi_int {\g__soup_random_previous_int / 127773}
    \int_set:Nn \l__lo_int {\int_mod:nn{\g__soup_random_previous_int}{127773}}
    \int_gset:Nn \g__soup_random_previous_int {
        16807 * \l__hi_int - 2836 * \l__lo_int
    }
    \int_compare:nNnT \g__soup_random_previous_int < 1 {
        \int_gadd:Nn \g__soup_random_previous_int {2147483647}
    }
    \int_gset:Nn \g__soup_random_current_int {
        #1 + \int_mod:nn{\g__soup_random_previous_int}{#2 - #1 + 1}
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_draw_nodes:}
% Must be used inside a |tikzpicture| environment.
%
% For every node pushed, now draw a node using either the previously set value
% or one now generated by the |getrand| macro.
%
%    \begin{macrocode}
\cs_new:Nn \__soup_draw_nodes: {
    \int_step_variable:nnnNn {1} {1} {\g_soup_columns_int} \l_tmpb_int {
        \int_step_variable:nnnNn {1} {1} {\g_soup_rows_int} \l_tmpc_int {
            \exp_args:Nnx
            \prop_get:NnNTF \g_soup_data_prop {
                (\l_tmpb_int,\l_tmpc_int)
            } \l_tmpa_tl {
                \node
                    at (\l_tmpb_int,\l_tmpc_int)
                    {\l_tmpa_tl};
            }{
                \node
                    at (\l_tmpb_int,\l_tmpc_int)
                    {\__soup_show_random_symbol:};
            }
        }
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_draw_highlights:}
% Must be used inside a |tikzpicture| environment.
%
% For every previously stored highlight coords, now draw the lines.
%
%    \begin{macrocode}
\cs_new:Nn \__soup_draw_highlights: {
    \seq_map_inline:Nn \g_soup_highlight_seq {
        \draw[
            double=\g_soup_highlight_color,
            double~distance=\g_soup_highlight_dim,
            line~width=2pt,
            color=\g_soup_line_color,
            opacity=0.4,
            line~cap=round
        ] ##1;
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_draw_soup_tikz:}
% Do the actual work of drawing the soup
%
%    \begin{macrocode}
\cs_new:Nn \__soup_draw_soup_tikz: {

    \tikzset{
        every~node/.style={
            font=\g_soup_font_tl,
        },
    }
    \begin{tikzpicture}[
        x=\g_soup_spacing_dim,
        y=-\g_soup_spacing_dim,
    ]
        \draw[rounded~corners=6pt, use~as~bounding~box]
            (0.5,0)
            ++(0,0.5) rectangle +(\g_soup_columns_int, \g_soup_rows_int);
        \__soup_draw_highlights:
        \__soup_draw_nodes:
    \end{tikzpicture}
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_draw_soup_tabular:}
% Do the actual work of drawing the soup (as a table)
%
%    \begin{macrocode}
\cs_new:Nn \__soup_draw_soup_tabular: {
    \dim_zero_new:N \l_soup_colwidth_dim
    \exp_args:Nnx
    \dim_set:Nn \l_soup_colwidth_dim {\fp_to_dim:n {0.45 * \textwidth / (\g_soup_columns_int + 1)}}

    \dim_zero_new:N \l_soup_lineheight_dim
    \dim_set:Nn \l_soup_lineheight_dim {2\l_soup_colwidth_dim - \baselineskip}

    \setlength{\tabcolsep}{\l_soup_colwidth_dim}
    \vspace{0.25\g_soup_spacing_dim}\par
    \noindent
    \begin{tabular*}{\textwidth}{
        @{\extracolsep{\fill}}
        | *{\g_soup_columns_int}{c@{\hskip\l_soup_colwidth_dim}} |
    }
        \hline\rule{0pt}{\g_soup_spacing_dim}
        \int_step_inline:nnnn {1} {1} {\g_soup_rows_int } {
            \int_gset:Nn \g_tmpa_int {##1}
            \int_step_variable:nnnNn {1} {1} {\g_soup_columns_int} \l_tmpb_int {
                \exp_args:Nnx
                \prop_get:NnNTF \g_soup_data_prop {
                    (\l_tmpb_int,\the\g_tmpa_int)
                } \l_tmpa_tl {
                    \g_soup_font_tl
                    \IfBooleanTF{\g_soup_highlight_bool}{
                        {\bfseries\l_tmpa_tl}
                    }{
                        \l_tmpa_tl
                    }
                }{
                    \g_soup_font_tl\__soup_show_random_symbol:
                }
                \int_compare:nNnT \l_tmpb_int < \g_soup_columns_int {
                    &
                }
            }
            \int_compare:nNnTF \g_tmpa_int < \g_soup_rows_int {
                \\[\l_soup_lineheight_dim]
            }{
                \\[\l_soup_lineheight_dim]\hline\end{tabular*}
            }
        }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[aux]{\__soup_show_random_symbol:}
% Called for every coordinate not defined by calls to |\hideinsoup|,
% this generates a random symbol---either a number from the
% |\g_soup_number_range_int| (if nonzero) or from the list of symbols in
% |\g_soup_symbol_clist| set by |homemadesoup|, |alphabetsoup|,
% and |Alphabetsoup|.
%    \begin{macrocode}
\cs_new:Nn \__soup_show_random_symbol: {
    \int_compare:nNnTF \g_soup_symbol_count_int = 0 {
        \__soup_random_int:nn {\g_soup_number_min_int}{\g_soup_number_max_int}
        \the\g__soup_random_current_int
    }{
        \__soup_random_int:nn {1}{\g_soup_symbol_count_int}
        \clist_item:Nn \g_soup_symbol_clist {\g__soup_random_current_int}
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{User Document Functions}
%
%
% \begin{macro}{\listofclues}
% Display the list of clues. THe optional argument will be expanded with
% |\theclue| as each clue. The default is defined as |\theclue\par|.
%    \begin{macrocode}
\NewDocumentCommand \listofclues { +o } {
    \tl_clear_new:N \theclue
    \IfNoValueTF{#1}{
        \tl_set:Nn \l_tmpa_tl {\theclue\par}
    }{
        \tl_set:Nn \l_tmpa_tl {#1}
    }
    \seq_map_variable:NNn \g_soup_clue_seq \theclue {
        \l_tmpa_tl
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\highlightinsoup}
% Given the coordinates of a word (expressed as |{x1}{y1}{x2}{y2}|),
%  this will mark the word (or other sequence).
%
% This is automatically called for every clue hidden via |\hideinsoup|.
%
% This does nothing unless |highlight=true| was passed to the package.
%    \begin{macrocode}
\NewDocumentCommand \highlightinsoup { m m m m }{
  \bool_if:NT \g_soup_highlight_bool {
    \seq_gput_left:Nx \g_soup_highlight_seq {(#1, #2) -- (#3, #4)}
  }
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\hideinsoup, \hideinsoup*}
% Given a starting coordinate, a direction, a comma-separated list of symbols,
% and an optional clue, set the appropriate coordinates to these symbols.
%
% \marg{x1}, \marg{y1}, \marg{direction}, \marg{word}, \oarg{clue}
%
% The starred version will disable highlighting (if enabled) to allow
%  setting parts of the soup that are outside actual answers.
%
% If a clue is specified, insert it into the |\listofclues|
%    \begin{macrocode}
\NewDocumentCommand \hideinsoup { smmmmo } {
    \int_zero_new:N \l__soup_dx_int
    \int_zero_new:N \l__soup_dy_int

    \str_case:nn {#4} {
        {left}{
            \int_set:Nn \l__soup_dx_int {-1}
            \int_set:Nn \l__soup_dy_int { 0}
        }
        {right}{
            \int_set:Nn \l__soup_dx_int { 1}
            \int_set:Nn \l__soup_dy_int { 0}
        }
        {up}{
            \int_set:Nn \l__soup_dx_int { 0}
            \int_set:Nn \l__soup_dy_int {-1}
        }
        {upleft}{
            \int_set:Nn \l__soup_dx_int {-1}
            \int_set:Nn \l__soup_dy_int {-1}
        }
        {upright}{
            \int_set:Nn \l__soup_dx_int { 1}
            \int_set:Nn \l__soup_dy_int {-1}
        }
        {down}{
            \int_set:Nn \l__soup_dx_int { 0}
            \int_set:Nn \l__soup_dy_int { 1}
        }
        {downleft}{
            \int_set:Nn \l__soup_dx_int {-1}
            \int_set:Nn \l__soup_dy_int { 1}
        }
        {downright}{
            \int_set:Nn \l__soup_dx_int { 1}
            \int_set:Nn \l__soup_dy_int { 1}
        }
    }

    \clist_set:Nn \l__soup_clue_clist {#5}
    \int_zero_new:N \l__soup_clue_count_int
    \int_set:Nn \l__soup_clue_count_int {\clist_count:N \l__soup_clue_clist}

    \int_zero_new:N \l__soup_cx_int
    \int_zero_new:N \l__soup_cy_int
    \tl_clear_new:N \l__soup_ci_tl
    \tl_clear_new:N \l__soup_ch_tl
    \tl_clear_new:N \l__soup_nn_tl

    \int_step_variable:nnnNn {1} {1} {\l__soup_clue_count_int} \l__soup_ci_tl {
        \int_set:Nn \l__soup_cx_int
            {#2 + \l__soup_dx_int * (\l__soup_ci_tl - 1)}

        \int_set:Nn \l__soup_cy_int
            {#3 + \l__soup_dy_int * (\l__soup_ci_tl - 1)}

        \exp_args:Nnx
        \tl_set:Nn \l__soup_ch_tl
            {\clist_item:Nn \l__soup_clue_clist {\l__soup_ci_tl}}

        \exp_args:Nnx
        \tl_set:Nn \l__soup_nn_tl
            {(\the\l__soup_cx_int,\the\l__soup_cy_int)}

        \exp_args:Nnx
        \tl_set:Nn \l__soup_cv_tl
            {\exp_args:Nno \prop_item:Nn \g_soup_data_prop \l__soup_nn_tl}

        \str_if_empty:NTF \l__soup_cv_tl {
            \exp_args:Nnx \prop_gput:Noo \g_soup_data_prop {
                \l__soup_nn_tl
            } {\l__soup_ch_tl}
        }{
            \str_if_eq:NNF \l__soup_cv_tl \l__soup_ch_tl {
                \msg_warning:nnxxx{soup}{mismatch}{
                    \l__soup_nn_tl
                }{\l__soup_cv_tl}{\l__soup_ch_tl}

                \tl_put_left:Nx \l__soup_ch_tl
                    {\l__soup_cv_tl/}

                \exp_args:Nnx
                \prop_gput:Noo \g_soup_data_prop {\l__soup_nn_tl}
                    {\l__soup_ch_tl}
            }
        }
    }

    \IfBooleanF{#1}{
        \exp_args:Nnx
        \int_set:Nn \l__soup_cx_int
            {#2 + \l__soup_dx_int * (\l__soup_clue_count_int - 1)}

        \exp_args:Nnx
        \int_set:Nn \l__soup_cy_int
            {#3 + \l__soup_dy_int * (\l__soup_clue_count_int - 1)}

        \exp_args:Nnx
        \tl_set:Nn \l__soup_nn_tl
            {(\the\l__soup_cx_int,\the\l__soup_cy_int)}

        \exp_args:Nnx
        \seq_gput_left:Nx \g_soup_highlight_seq
            {(#2, #3) -- \l__soup_nn_tl}
    }
    \IfNoValueF{#6}{
        \seq_gput_left:No \g_soup_clue_seq {#6}
    }
}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Environments}
%
%
% \begin{macro}{alphabetsoup, alphabetsoup*}
% A soup environment where unspecified coordinates are fill with a--z
%
% For something else, see the |homemadesoup| environment.
%    \begin{macrocode}
\NewDocumentEnvironment{alphabetsoup}{ sO{15}oo }
{
    \par\noindent
    \__soup_init:nn {#2}{#3}
    \IfBooleanTF{#1}{
        \def\showlist{}
    }{
        \def\showlist{\par\vspace*{1em}\listofclues}
    }
    \IfNoValueF{#4}{
        \tl_gset:Nn \g_soup_font_tl {#4}
    }

    \clist_gset_eq:NN \g_soup_symbol_clist
        \c_soup_alphabet_clist

    \int_gset:Nn \g_soup_symbol_count_int
        {\clist_count:N \g_soup_symbol_clist}
}{
    \IfBooleanTF \g_soup_use_tikz_bool {
        \__soup_draw_soup_tikz:
    }{
        \__soup_draw_soup_tabular:
    }
    \showlist
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{Alphabetsoup, Alphabetsoup*}
% A soup environment where unspecified coordinates are
% A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
%
% For something else, see the |homemadesoup| environment.
%    \begin{macrocode}
\NewDocumentEnvironment{Alphabetsoup}{ sO{15}oo }
{
    \par\noindent
    \__soup_init:nn {#2}{#3}
    \IfBooleanTF{#1}{
        \def\showlist{}
    }{
        \def\showlist{\par\vspace*{1em}\listofclues}
    }
    \IfNoValueF{#4}{
        \tl_gset:Nn \g_soup_font_tl {#4}
    }

    \clist_gset_eq:NN \g_soup_symbol_clist
        \c_soup_Alphabet_clist

    \int_gset:Nn \g_soup_symbol_count_int
        {\clist_count:N \g_soup_symbol_clist}
}{
    \IfBooleanTF \g_soup_use_tikz_bool {
        \__soup_draw_soup_tikz:
    }{
        \__soup_draw_soup_tabular:
    }
    \showlist
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{homemadesoup, homemadesoup*}
% The |homemadesoup| environment builds a soup from the user-supplied
% comma-separated list of symbols.
%    \begin{macrocode}
\NewDocumentEnvironment{homemadesoup}{ sO{15}omo }
{
    \par\noindent
    \__soup_init:nn {#2}{#3}
    \IfBooleanTF{#1}{
        \def\showlist{}
    }{
        \def\showlist{\par\vspace*{1em}\listofclues}
    }
    \IfNoValueF{#5}{
        \tl_gset:Nn \g_soup_font_tl {#5}
    }

    \clist_gset:Nn \g_soup_symbol_clist
        {#4}

    \int_gset:Nn \g_soup_symbol_count_int
        {\clist_count:N \g_soup_symbol_clist}
}
{
    \IfBooleanTF \g_soup_use_tikz_bool {
        \__soup_draw_soup_tikz:
    }{
        \__soup_draw_soup_tabular:
    }
    \showlist
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{numbersoup, numbersoup*}
% Sets up a soup with all unspecified coordinates displaying numbers.
%    \begin{macrocode}
\NewDocumentEnvironment{numbersoup}{ sO{15}omO{0}o }
{
    \par\noindent
    \__soup_init:nn{#2}{#3}
    \IfBooleanTF{#1}{
        \def\showlist{}
    }{
        \def\showlist{\par\vspace*{1em}\listofclues}
    }
    \IfNoValueF{#6}{
        \tl_gset:Nn \g_soup_font_tl {#6}
    }

    \int_gset:Nn \g_soup_number_max_int
        {#4}

    \int_gset:Nn \g_soup_number_min_int
        {#5}

    \int_gset:Nn \g_soup_number_range_int
        {\g_soup_number_max_int - \g_soup_number_min_int}
}
{
    \IfBooleanTF \g_soup_use_tikz_bool {
        \__soup_draw_soup_tikz:
    }{
        \__soup_draw_soup_tabular:
    }
    \showlist
}
%    \end{macrocode}
% \end{macro}
%
%
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
% \Finale
\endinput