[Tex/LaTex] Automatically generated BINGO cards

random numberstikz-pgf

I am trying to automatically generate a series of BINGO cards. So, I need a 5 x 5 table with square cells that allow text wrapping. I also want to randomly assign a word to each cell from a "bank" of 30 different words, and I need the center cell to, of course, be labeled "FREE". This means that for each card, 6 words from the bank will not be used. I know that this can be accomplished with tikz and some macros, but I am not familiar with the macros needed to randomly select the words, and I am having trouble finding adequate help on the web. Can someone please help?

Thanks!


I mostly figured out my own question; however, I still have one major problem as stated after the MWE:

\documentclass[10pt]{article}

\usepackage{xstring}
\usepackage{tikz}
\usetikzlibrary{calc}

\newcommand*{\random}[5]{%
\pgfmathparse{random(5)}%
\ifcase\pgfmathresult\relax
\or#1\or#2\or#3\or#4\or#5
\fi%
}

\newcommand*{\randomtwo}[6]{
    \pgfmathparse{random(6)}
    \ifcase\pgfmathresult\relax
    \or#1\or#2\or#3\or#4\or#5\or#6
\fi%
}

\newcommand*{\randomthree}[6]{
    \pgfmathparse{random(6)}
    \ifcase\pgfmathresult\relax
    \or#1\or#2\or#3\or#4\or#5\or#6
\fi%
}

\newcommand*{\randomfour}[6]{
    \pgfmathparse{random(6)}
    \ifcase\pgfmathresult\relax
    \or#1\or#2\or#3\or#4\or#5\or#6
\fi%
}

\newcommand*{\randomfive}[6]{
    \pgfmathparse{random(6)}
    \ifcase\pgfmathresult\relax
    \or#1\or#2\or#3\or#4\or#5\or#6
\fi%
}

\newcommand*{\randomsix}[6]{
    \pgfmathparse{random(6)}
    \ifcase\pgfmathresult\relax
    \or#1\or#2\or#3\or#4\or#5\or#6
\fi%
}

\def\NumOfColumns{5}%
\def\Sequence{1, 2, 3, 4, 5}%

\newcommand{\Size}{3.25cm}
\tikzset{Square/.style={
inner sep=0pt,
text width=\Size, 
minimum size=\Size,
draw=black,
align=center,
}
}

\begin{document}

\begin{tikzpicture}[draw=black, thick, x=\Size,y=\Size]
\foreach \row in \Sequence{%
    \foreach \col in \Sequence {%
        \pgfmathtruncatemacro{\value}{\col+\NumOfColumns*(\row-1)}
        \def\NodeText{\random{\randomtwo{radioactivity}{$\alpha$ particle}{$\gamma$-ray}{in--situ leaching}{half--life, $t_{\frac{1}{2}}$}{GM counter}}{\randomthree{Manhattan Project}{$3-5$ \%}{$^{232}$Th}{breeder}{greenhouse gas}{ICP--MS}}{\randomfour{NRC}{conversion}{solid}{Rocky Flats}{smoke detector}{Trinity}}{\randomfive{START}{Yucca Mountain}{Chart of the Nuclides}{Chernobyl}{Savannah River Site}{Hanford Site}}{\randomsix{PUREX}{UO$_2$}{Y--12}{Bq}{LSC}{gaseous diffusion}}}
        \pgfmathsetmacro{\ColRowProduce}{\col*\row}
        \IfEq{\ColRowProduce}{9}{% If is center square
            \node [] at ($(\col,-\row)-(0.5,0.5)$) {FREE};
        }{
            \node [Square] at ($(\col,-\row)-(0.5,0.5)$) {\large \NodeText};
        }
    }
}    
\end{tikzpicture}                       
\end{document}

The biggest problem is that I get repeat words on my BINGO card. Can I set up a loop to prevent this? Also, if anyone has a way of cleaning up this code, I would be grateful.

Best Answer

Here's something that works. I'm not sure about its efficiency however. The code below selects random items from a user entered list of items and from these builds a randomized sequence of 24 items (25 less the center square) without duplicates. When \NodeText is called, an item is removed from the randomized list and placed in the node. An error is issued when too few items are provided (i.e. less than 24) and there isn't an upper limit on the number of items that can be supplied. For each new bingo card that you want to generate, you just call \setItems and regenerate a card. Comments/suggested improvements on the code are welcomed!

enter image description here

\documentclass[10pt]{article}

\usepackage{xstring}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{xparse}

\input{random.tex}
\newcount\randomnum
\ExplSyntaxOn

\seq_new:N \g_my_items_seq
\seq_new:N \l_my_tmp_items_seq
\seq_new:N \g_my_randomized_seq
\int_new:N \l_tmp_int
\msg_new:nnnn {bingo} {Too~few~items!} {Provide~at~least~24~items!}{}

\cs_generate_variant:Nn \seq_item:Nn {Nx}
\cs_generate_variant:Nn \seq_remove_all:Nn {Nx}

\NewDocumentCommand {\myItems} {m}
    {
      \seq_clear:N \g_my_items_seq % clear item list 
      \seq_gset_split:Nnn \g_my_items_seq {;} {#1} % put item list in seq
      \int_compare:nNnT {\seq_count:N \g_my_items_seq} < {24} {\msg_error:nn {bingo} {Too~few~items!}} % check whether there are enough items
    }

\NewDocumentCommand{\setItems}{}
{
\seq_set_eq:NN \l_my_tmp_items_seq \g_my_items_seq % put in temp seq so that multiple cards can be produced
\prg_replicate:nn {24} %generate random list of 24 items
    {
        \int_set:Nn \l_tmp_int {\seq_count:N \l_my_tmp_items_seq}% set current length of list
        \setrannum{\randomnum}{1}{\int_use:N \l_tmp_int} % choose random num up to length of seq
        \seq_put_right:Nx \g_my_randomized_seq {\seq_item:Nn \l_my_tmp_items_seq {\the\randomnum}}% grab corresponding item and put in tmp seq
        \seq_remove_all:Nx \l_my_tmp_items_seq {\seq_item:Nn \l_my_tmp_items_seq {\the\randomnum}}%delete that item from temp seq
    }
\seq_clear:N \l_my_tmp_items_seq %clear temp seq when done
}

\NewDocumentCommand {\NodeText}{}
    {
        \seq_gpop_right:NN \g_my_randomized_seq \l_tmpa_tl %pop item from randomized seq into token list
        \tl_use:N \l_tmpa_tl %use that item.
    }


\ExplSyntaxOff

\def\NumOfColumns{5}%
\def\Sequence{1, 2, 3, 4, 5}%

\newcommand{\Size}{3.25cm}
\tikzset{Square/.style={
inner sep=0pt,
text width=\Size, 
minimum size=\Size,
draw=black,
align=center,
}
}

\begin{document}
%\myItems{this;will;produce;an;error;because;there;aren't;enough;items}

\myItems{radioactivity;$\alpha$ particle;$\gamma$-ray;in--situ leaching;half--life, $t_{\frac{1}{2}}$;GM counter;Manhattan Project;$3-5$ \%;$^{232}$Th;breeder;greenhouse gas;ICP--MS;conversion;solid;Rocky Flats;smoke detector;Trinity;START;Yucca Mountain;Chart of Nuclides;Chernobyl;Savannah River Site;Hanford Site;PUREX;UO$_2$;Y--12;Bq;LSC;gaseous diffusion}

\setItems

\begin{tikzpicture}[draw=black, thick, x=\Size,y=\Size]
\foreach \row in \Sequence{%
    \foreach \col in \Sequence {%
        \pgfmathtruncatemacro{\value}{\col+\NumOfColumns*(\row-1)}
        \pgfmathsetmacro{\ColRowProduce}{\col*\row}
        \IfEq{\ColRowProduce}{9}{% If is center square
            \node [] at ($(\col,-\row)-(0.5,0.5)$) {FREE};
        }{
            \node [Square] at ($(\col,-\row)-(0.5,0.5)$) {\large \NodeText};
        }
    }
}    
\end{tikzpicture}

\setItems

\begin{tikzpicture}[draw=black, thick, x=\Size,y=\Size]
\foreach \row in \Sequence{%
    \foreach \col in \Sequence {%
        \pgfmathtruncatemacro{\value}{\col+\NumOfColumns*(\row-1)}
        \pgfmathsetmacro{\ColRowProduce}{\col*\row}
        \IfEq{\ColRowProduce}{9}{% If is center square
            \node [] at ($(\col,-\row)-(0.5,0.5)$) {FREE};
        }{
            \node [Square] at ($(\col,-\row)-(0.5,0.5)$) {\large \NodeText};
        }
    }
}    
\end{tikzpicture}                 
\end{document}
Related Question