# Foreach loop inside the Tikz picture - Creation of memory cards using Tikz

This question regards creating a memory game using Tikz.I would like to ask for help with creating a loop that will place memory cards on the page.

There are many standards, I believe, but let us pick one:

1. memory game is printed on three pages, consisting of 32x2 square cards
2. two pages contain 4x6 cards, the third page contains 16 cards (the image bellow shows the layout)

The question is: How to place the cards in foreach loop, passing several arguments in array(s) of values? The array(s) of arguments will be: the name of the image file, the text, the colour value.

Pseudo foreach loop will look like this: Place first image in array, place first title in array and use first colour in array; shift position by xshift=71pt, place second ... After fourth image shift by yshift...

Minor update: Using this loop way it will be easy to create back of the cards passing the same value of image file. On the other hand all needs to be centred.

\documentclass[12pt]{article}

% ############################## geometry
\usepackage{geometry}
\geometry{
hmarginratio =  1:1,
vmarginratio = 1:1,
bindingoffset = 0cm,
onecolumn,
a3paper,
layoutwidth = 220 mm,
layoutheight = 320 mm,
layouthoffset=\dimexpr(\paperwidth-\csname Gm@layoutwidth\endcsname)/2\relax,
layoutvoffset=\dimexpr(\paperheight-\csname Gm@layoutheight\endcsname)/2\relax,
showcrop
}
\usepackage[icelandic, latin, czech]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{calc}
\definecolor{title}{RGB}{16, 13, 32}
\usepackage{mwe}
\usepackage{XCharter}

% ############################### Document

\newcommand{\czHyphen}{\rule[.45ex]{.2em}{.11ex}}
\def\cropmarkgap{1}% mm

\makeatletter
\def\Gm@cropmark(#1,#2,#3,#4){% #1 = x direction, #2 = y direction, #3 & #4 no longet used
\begin{picture}(0,0)
\setlength\unitlength{1truemm}%
\linethickness{0.25pt}%
\put(\the\numexpr #1*\cropmarkgap\relax,0){\line(#1,0){\the\numexpr 20-\cropmarkgap}}%
\put(0,\the\numexpr #2*\cropmarkgap\relax){\line(0,#2){\the\numexpr 20-\cropmarkgap}}%
\end{picture}}%
\makeatother

\makeatletter
\def\parsecomma#1,#2\endparsecomma{\def\page@x{#1}\def\page@y{#2}}
\tikzdeclarecoordinatesystem{page}{
\parsecomma#1\endparsecomma
\pgfpointanchor{current page}{north east}
% Save the upper right corner
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
% save the lower left corner
\pgfpointanchor{current page}{south west}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
% Transform to the correct placement
\pgfmathparse{(\pgf@xc-\pgf@xb)/2.*\page@x+(\pgf@xc+\pgf@xb)/2.}
\expandafter\pgf@x\expandafter=\pgfmathresult pt
\pgfmathparse{(\pgf@yc-\pgf@yb)/2.*\page@y+(\pgf@yc+\pgf@yb)/2.}
\expandafter\pgf@y\expandafter=\pgfmathresult pt
}
\makeatother

\usepackage{eso-pic}
\usepackage{tikzpagenodes}
\newcommand{\shiftleft}{\hspace*{-0.55\dimexpr\csname Gm@layoutwidth\endcsname-\textwidth\relax}}
\newcommand{\shiftup}{\vspace*{-0.13\dimexpr\csname Gm@layoutheight\endcsname-\textwidth\relax}}
\begin{document}
\thispagestyle{empty}
\begin{tikzpicture}[remember picture, overlay]
\tikzset{p_title/.style={text centered, minimum height=0.6cm, minimum width=5cm, font=\bfseries}}
\tikzset{p_title_line/.style={ultra thin, color=violet}}

\node[inner sep=0pt] (A) at (page cs:-0.5045,0.595)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(A.north west)$)  rectangle ($(A.south east)$);
\node [p_title] (AA) at ($(A.north west)+(2.5,-0.30)$) {{title text}};
\draw [p_title_line](AA.south west) -- (AA.south east);

\node[inner sep=0pt] (B) at ([xshift=71pt]A.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(B.north west)$)  rectangle ($(B.south east)$);
\node [p_title] (BB) at ($(B.north west)+(2.5,-0.30)$) {{amma}};
\draw [p_title_line](BB.south west) -- (BB.south east);

\node[inner sep=0pt] (C) at ([xshift=71pt]B.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(C.north west)$)  rectangle ($(C.south east)$);
\node [p_title] (CC) at ($(C.north west)+(2.5,-0.30)$) {{banka}};
\draw [p_title_line](CC.south west) -- (CC.south east);

\node[inner sep=0pt] (D) at ([xshift=71pt]C.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(D.north west)$)  rectangle ($(D.south east)$);
\node [p_title] (DD) at ($(D.north west)+(2.5,-0.30)$) {{strætó}};
\draw [p_title_line](DD.south west) -- (DD.south east);

% second line
\node[inner sep=0pt] (E) at  ([yshift=-71pt]A.south)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(E.north west)$)  rectangle ($(E.south east)$);
\node [p_title] (EE) at ($(E.north west)+(2.5,-0.30)$) {{bíll}};
\draw [p_title_line](EE.south west) -- (EE.south east);

\node[inner sep=0pt] (F) at ([xshift=71pt]E.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(F.north west)$)  rectangle ($(F.south east)$);
\node [p_title] (FF) at ($(F.north west)+(2.5,-0.30)$) {{strætó}};
\draw [p_title_line](FF.south west) -- (FF.south east);

\node[inner sep=0pt] (G) at ([xshift=71pt]F.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(G.north west)$)  rectangle ($(G.south east)$);
\node [p_title] (GG) at ($(G.north west)+(2.5,-0.30)$) {{banka}};
\draw [p_title_line](GG.south west) -- (GG.south east);

\node[inner sep=0pt] (H) at ([xshift=71pt]G.east)
{\includegraphics[width=5cm]{example-image}};
\draw[black, ultra thick] ($(H.north west)$)  rectangle ($(H.south east)$);
\node [p_title] (HH) at ($(H.north west)+(2.5,-0.30)$) {{autobus}};
\draw [p_title_line](HH.south west) -- (HH.south east);

%third line
\end{tikzpicture}
\clearpage

\end{document}


• I'd suggest using tcbraster from tcolorbox. It easily produces distribution of boxes like yours and it's possible to use a foreach loop inside it. Some examples tex.stackexchange.com/a/251710/1952, tex.stackexchange.com/a/214787/1952. Feb 19, 2016 at 8:22
• I will definitely have a look at it. Thank you for suggestion. Feb 19, 2016 at 8:35
• Using tcbraster can I position and align text over the image? I seems to me that I need image (with text and line) already ready as a whole. Is that right? Feb 19, 2016 at 9:35

Here is an option using the standard TeX "array" approach (an example): use \csname - \endcsname (or in this case the LaTeX wrappers \@namedef and \@nameuse) to define control sequences with numbers (i.e., the array index).

For your project, this requires three "arrays" one for the image name, the card title, and the card color. Here is the set up for the interface:

%%% Define "Array" interface
\makeatletter
\newcounter{imgs}
\setcounter{imgs}{0}
%#1 is the image
%#2 is the title
%#3 is the color
\stepcounter{imgs}%
\@namedef{imgimage\theimgs}{#1}%
\@namedef{imgtitle\theimgs}{#2}%
\@namedef{imgcolor\theimgs}{#3}}
\newcommand{\getimage}[1]{\expandafter\@nameuse\expandafter{imgimage#1}}%
\newcommand{\gettitle}[1]{\expandafter\@nameuse\expandafter{imgtitle#1}}%
\newcommand{\getcolor}[1]{\expandafter\@nameuse\expandafter{imgcolor#1}}%
\makeatother


"Elements" are added to the "arrays" through the \addimg command. For the 32 unique cards, this would look like:

%%% Define Cards


"Elements" are accessed from their respective "arrays" through the appropriate "get" commands. This allows the for a single for loop to be used. Parameterizing the lengths and number of images per row:

%%% Global Setup
\newcommand\xspacing{71pt}%<== space between the images
\newcommand\yspacing{71pt}%<== vertical space between rows
\newcommand\imgperrow{4}%<== number of images per row
\tikzset{p_title/.style={text centered, minimum height=0.6cm, minimum width=5cm, font=\bfseries}}
\tikzset{p_title_line/.style={ultra thin, color=violet}}

%%% Define primary for loop
\newcommand{\forloop}[2]{%
\foreach \x in {#1,...,#2}%<==loop for each image in the array
{
\edef\gonode{\noexpand\node[inner sep=0pt] (B) at (A) {\noexpand\includegraphics[width=5cm]{\getimage{\x}}};}%<==Edit to expand the file name
\gonode%\node [inner sep=0pt] (B) at (A) {\includegraphics[width=5cm]{\getimage{\x}}};%
\draw [black, ultra thick] ($(B.north west)$) rectangle ($(B.south east)$);%
\node [p_title] (AA) at ($(B.north west)+(2.5,-0.30)$) {\gettitle{\x}};%
\draw [p_title_line, color=\getcolor{\x}](AA.south west) -- (AA.south east);%
\pgfmathparse{Mod(\x,\imgperrow)==0?1:0};%
\ifnum\pgfmathresult>0
\coordinate (left) at ([yshift=-\yspacing]left);
\path let \p1=(left),\p2=(B.south) in coordinate (A) at (\x1,\y2-\yspacing);
\else
\coordinate (A) at ([xshift=\xspacing]B.east);%
\fi
}}
%%% Define the for loop for 2 images per row on third page
\newcommand{\forlooptwo}[2]{%
\begingroup
\def\imgperrow{2}
\let\originalxspacing\xspacing
\def\xspacing{5*\originalxspacing}
\forloop{#1}{#2}\endgroup}


The \xshift and \yshift are determined by computing the modulo of the count and \imgperrow. \forlooptwo is used for the four rows on the third page where two spaces are skipped. This is achieved by redefining \imgperrow and \xspacing.

The three pages are then created with:

\begin{document}
\pagestyle{empty}
%first page
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{1}{24}
\end{tikzpicture}

%second page
\clearpage
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{25}{32}
\forloop{1}{16}
\end{tikzpicture}

%third page
\clearpage
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{17}{20}
\forlooptwo{21}{28}
\forloop{29}{32}
\end{tikzpicture}
\end{document}


To yield:

The total MWE:

\documentclass[12pt]{article}
% ############################## geometry
\usepackage{geometry}
\geometry{
hmarginratio =  1:1,
vmarginratio = 1:1,
bindingoffset = 0cm,
onecolumn,
a3paper,
layoutwidth = 220 mm,
layoutheight = 320 mm,
layouthoffset=\dimexpr(\paperwidth-\csname Gm@layoutwidth\endcsname)/2\relax,
layoutvoffset=\dimexpr(\paperheight-\csname Gm@layoutheight\endcsname)/2\relax,
showcrop
}
\usepackage[icelandic, latin, czech]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{calc}
\definecolor{title}{RGB}{16, 13, 32}
\usepackage{mwe}
\usepackage{XCharter}

% ############################### Document

\newcommand{\czHyphen}{\rule[.45ex]{.2em}{.11ex}}
\def\cropmarkgap{1}% mm

\makeatletter
\def\Gm@cropmark(#1,#2,#3,#4){% #1 = x direction, #2 = y direction, #3 & #4 no longet used
\begin{picture}(0,0)
\setlength\unitlength{1truemm}%
\linethickness{0.25pt}%
\put(\the\numexpr #1*\cropmarkgap\relax,0){\line(#1,0){\the\numexpr 20-\cropmarkgap}}%
\put(0,\the\numexpr #2*\cropmarkgap\relax){\line(0,#2){\the\numexpr 20-\cropmarkgap}}%
\end{picture}}%
\makeatother

\makeatletter
\def\parsecomma#1,#2\endparsecomma{\def\page@x{#1}\def\page@y{#2}}
\tikzdeclarecoordinatesystem{page}{
\parsecomma#1\endparsecomma
\pgfpointanchor{current page}{north east}
% Save the upper right corner
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
% save the lower left corner
\pgfpointanchor{current page}{south west}
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
% Transform to the correct placement
\pgfmathparse{(\pgf@xc-\pgf@xb)/2.*\page@x+(\pgf@xc+\pgf@xb)/2.}
\expandafter\pgf@x\expandafter=\pgfmathresult pt
\pgfmathparse{(\pgf@yc-\pgf@yb)/2.*\page@y+(\pgf@yc+\pgf@yb)/2.}
\expandafter\pgf@y\expandafter=\pgfmathresult pt
}
\makeatother

\usepackage{eso-pic}
\usepackage{tikzpagenodes}
\newcommand{\shiftleft}{\hspace*{-0.55\dimexpr\csname Gm@layoutwidth\endcsname-\textwidth\relax}}
\newcommand{\shiftup}{\vspace*{-0.13\dimexpr\csname Gm@layoutheight\endcsname-\textwidth\relax}}

%%% Define "Array" interface
\makeatletter
\newcounter{imgs}
\setcounter{imgs}{0}
%#1 is the image
%#2 is the title
%#3 is the color
\stepcounter{imgs}%
\@namedef{imgimage\theimgs}{#1}%
\@namedef{imgtitle\theimgs}{#2}%
\@namedef{imgcolor\theimgs}{#3}}
\newcommand{\getimage}[1]{\expandafter\@nameuse\expandafter{imgimage#1}}%
\newcommand{\gettitle}[1]{\expandafter\@nameuse\expandafter{imgtitle#1}}%
\newcommand{\getcolor}[1]{\expandafter\@nameuse\expandafter{imgcolor#1}}%
\makeatother

%%% Define Cards

%%% Global Setup
\newcommand\xspacing{71pt}%<== space between the images
\newcommand\yspacing{71pt}%<== vertical space between rows
\newcommand\imgperrow{4}%<== number of images per row
\tikzset{p_title/.style={text centered, minimum height=0.6cm, minimum width=5cm, font=\bfseries}}
\tikzset{p_title_line/.style={ultra thin, color=violet}}

%%% Define primary for loop
\newcommand{\forloop}[2]{%
\foreach [count=\i] \x in {#1,...,#2}%<==loop for each image in the array
{
\edef\gonode{\noexpand\node[inner sep=0pt] (B) at (A) {\noexpand\includegraphics[width=5cm]{\getimage{\x}}};}%<==Edit to expand the file name
\gonode%\node [inner sep=0pt] (B) at (A) {\includegraphics[width=5cm]{\getimage{\x}}};%
\draw [black, ultra thick] ($(B.north west)$) rectangle ($(B.south east)$);%
\node [p_title] (AA) at ($(B.north west)+(2.5,-0.30)$) {\gettitle{\x}};%
\draw [p_title_line, color=\getcolor{\x}](AA.south west) -- (AA.south east);%
\pgfmathparse{Mod(\i,\imgperrow)==0?1:0};%
\ifnum\pgfmathresult>0
\coordinate (left) at ([yshift=-\yspacing]left);
\path let \p1=(left),\p2=(B.south) in coordinate (A) at (\x1,\y2-\yspacing);
\else
\coordinate (A) at ([xshift=\xspacing]B.east);%
\fi
}}
%%% Define the for loop for 2 images per row on third page
\newcommand{\forlooptwo}[2]{%
\begingroup
\def\imgperrow{2}
\let\originalxspacing\xspacing
\def\xspacing{5*\originalxspacing}
\forloop{#1}{#2}\endgroup}

\begin{document}
\pagestyle{empty}
%first page
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{1}{24}
\end{tikzpicture}

%second page
\clearpage
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{25}{32}
\forloop{1}{16}
\end{tikzpicture}

%third page
\clearpage
\begin{tikzpicture}[remember picture, overlay]
\coordinate (A) at (page cs:-0.5045,0.595);
\coordinate (left) at (A);
\forloop{17}{20}
\forlooptwo{21}{28}
\forloop{29}{32}
\end{tikzpicture}
\end{document}

• First of all - this is exactly what I needed. Thank you also for explaining the code itself! I have already implied the code to my recent version, though I have trouble now with loading the image. It says LaTeX Error: File auto.jpg' not found. It works well with mwe image and also when I directly write auto.jpg inside the \forloop command. I set \graphicspath usual way (\graphicspath{{/path/}{/path2/}}) Feb 24, 2016 at 5:33
• I have found out that I have to write down the name of the image file without the extension. F.e. \addimg{auto}{one}{blue}% without .jpg. Everything works :) Feb 24, 2016 at 5:49
• @chejnik: You can expand the file name prior to declaring the node by using \edef\gonode{\noexpand\node[inner sep=0pt] (B) at (A) {\noexpand\includegraphics[width=5cm]{\getimage{\x}}};}\gonode. I have updated the answer.
– Guho
Feb 24, 2016 at 6:09
• Ok, I left the previous code, loading images without file extension. I have added similar code for the back of the cards. I will post here a link to github complete code for creation of memory game cards, if you agree. Feb 24, 2016 at 8:22
• @chejnik: Happy to hear that this worked for you. Please, post a link. I'd be interested to see the result.
– Guho
Feb 24, 2016 at 19:06

This is just a simple example with tcbraster. You can fix columns number and distance between rows and columns, color of background boxes, titles, ...

\documentclass{article}
\usepackage[most]{tcolorbox}

\begin{document}
\begin{tcbraster}[raster columns=4, raster equal height=rows, size=fbox, colframe=red!50!black, center title]
\tcbincludegraphics[title=first]{example-image}
\tcbincludegraphics[title=second]{example-image-A}
\tcbincludegraphics[title=third]{example-image-B}
\tcbincludegraphics[title=fourth, colback=green]{example-image-C}
\tcbincludegraphics[title=first]{example-image}
\tcbincludegraphics[title=first, colframe=blue]{example-image}
\tcbincludegraphics[title=third]{example-image-B}
\tcbincludegraphics[title=second]{example-image-A}
\tcbincludegraphics[title=fourth]{example-image-C}
\tcbincludegraphics[title=second]{example-image-A}
\tcbincludegraphics[title=third]{example-image-B}
\tcbincludegraphics[title=first]{example-image}
\tcbincludegraphics[title=second]{example-image-A}
\tcbincludegraphics[title=fourth]{example-image-C}
\tcbincludegraphics[title=third]{example-image-B}
\tcbincludegraphics[title=fourth]{example-image-C}

\end{tcbraster}
\end{document}


• I've set [raster columns=4, raster rows=6, raster width = 220mm, raster left skip=10mm, raster right skip = 10mm, raster height=300mm, raster before skip=10mm, raster after skip = 10mm, raster column skip = 0mm, raster row skip = 0mm, colframe=red!50!black, center title, arc=0mm, sharp corners] with the geometry settings from my MWE. It seems there is no space to display all 6 rows. Feb 19, 2016 at 12:41
• When I use word 'hjól' with -j- letter, title area is bigger than areas of other titles. Using adjusted title option seems not to help. Is there no height of title area? Feb 19, 2016 at 13:26
• @chejnik For the problem with j you must use adjusted title=... in all boxes even those without j, this way all boxes in same row will adjust their title's height. Another solution could be keep using title=... in all boxes but including before title=\strut in tcbraster options. Feb 19, 2016 at 14:03
• @chejnik I didn't look at your geometry settings, but if you look where page number is shown, you'll see how it's impossible to fit 6 rows. I think there's something wrong in your page definition. Feb 19, 2016 at 14:05
• I see, the geometry settings was originally for extbook` class, but nevertheless both classes sets it incorrectly. We will definitely find the solution:) Feb 19, 2016 at 14:28