6

The following code origines from a joke in German, visualizing the answer "50 grays of Schade." (see below). The code:

  • puts 7 x 7 tiles + 1 bigger background tile
  • distributing in a cyclic way the characters S c h a d e .
  • changes each tiles gray-fill randomly in a predefined way
  • puts out a row of tiles using \foreach via \msrow{}{} (from refactoring)
  • leaving the call to the 7 rows unrefactored

(modified) Random sequence without replacement to obtain different shades (from R: sample(1:100,50,replace=FALSE)) with one error from copying, which you may or may not use as input:

37 32 80 12 55  6 17 
41 44 95  9 97 88 18 
 8  8 14 42 11 47 68 
99 73  1  5 84 81 40 
21 59 43 39 64 82 38 
24 85 34 89 50 20 92 
65 53 67 76 23 70 30 

If possible, this code should become even more compact, without sacrifycing legibility. But feel free to take different routes, too.

\documentclass[10pt,border=3mm,tikz]{standalone}

\newcommand\msrow[2]{%
    \foreach \g/\t [count=\i] in {#2}%
        \node[bx,fill=black!\g]     at (\i,#1) {\t};}

\begin{document}
 \begin{tikzpicture}[
%   bx/.style={minimum size=1cm,text=red,font={\bf}},% see cfr's remark
    bx/.style={minimum size=1cm,text=red,font={\bfseries}},
 ]  % ~~~ background ~~~~~~~~~~~~~~~~~~~~~~~~~~
    \node[fill=black!50, minimum size=8cm] at (4,4) {};

    % ~~~ all those tiles ~~~~~~~~~~~~~~~~~~~~~
    \msrow{7}{37/S, 32/c, 80/h, 12/a, 55/d,  6/e, 17/.}
    \msrow{6}{41/c, 44/h, 95/a,  9/d, 97/e, 88/., 18/S}
    \msrow{5}{ 8/h,  8/a, 14/d, 42/e, 11/., 47/S, 68/c}
    \msrow{4}{99/a, 73/d,  1/e,  5/., 84/S, 81/c, 40/h}
    \msrow{3}{21/d, 59/e, 43/., 39/S, 64/c, 82/h, 38/a}
    \msrow{2}{24/e, 85/., 34/S, 89/c, 50/h, 20/a, 92/d}
    \msrow{1}{65/., 53/S, 67/c, 76/h, 23/a, 70/d, 30/e}
    
 \end{tikzpicture}
\end{document}

result


Background, the joke in German:

"Kannst du mir sagen, was neun auf Englisch heißt?" - Can you please tell, what's nine in English?

"Nine." - Sounds exactly like: "No" (Nein)

"Schade." - Too bad (looks like "shade", and sounds a bit similar, too)

50 grays of S(c)hade ...

4
  • 1
    the background suits my mood of the season. Commented Dec 22, 2024 at 0:08
  • 1
    now you invalidated my answer ... ;) Commented Dec 22, 2024 at 7:26
  • Sorry, blame on me ... ;) Commented Dec 22, 2024 at 7:46
  • 1
    after I spent so much time on it, too. Commented Dec 22, 2024 at 8:22

4 Answers 4

5

This answer stays with tikz partly to avoid duplicating something with plain rules. Since we're using tikz, we might as well use it to generate the random elements we need.

Overview

This answer provides a tikz library, scrambler, which creates a board with lettered tiles using colours picked randomly from a colour series. Most aspects are now configurable using standard pgfkeys syntax.

This makes the code longer rather than shorter. However, the code required in the document body is extremely minimal and the library can be reused as required. For example, the following document produces the three illustrations shown below:

\documentclass[10pt,border=3mm,tikz,rgb]{standalone}
\usetikzlibrary{scrambler}
\begin{document}
\ms{S, c, h, a, d,  e, .}
\ms{Ll,w,y,d,-,f,e,l,-,y,r,-,a,s,y,n,.}
\ms[shade picker={12}{colourseries},board size=8x3,letter colour=white]{R,a,i,n,b,o,w,!}
\end{document}

A library is usually where I end up when I clean up tikz code: I have libraries for cats, cauldrons, trams, tortoises, spheres and so on.

Details

I use a colour series with 50 greys. So far, so not random. We pick them randomly using a tikz key, pick shade, which sets lliw to the result1. While greys are used by default, this can easily be changed using the shade picker={<number>}{<colorseries>} key.

I've randomised the grey for the background since otherwise there are only 49. On some compilations, this means the background may be white or nearly white. Recompile a few times if you don't like the results.

Syntax

The tikz key board size=<width>x<depth> allows you to configure the board size. The default is 7x7.

The background node adapts to the board size, allowing a margin of 5mm.

letter colour=<colour> is red by default and sets the colour of the letters.

bx and board are tikz styles which can be changed to modify the style of the tiles and/or background respectively. To maintain randomness, scrambler/pick shade,fill=lliw should be included. If you prefer non-randomness, you may, of course, change this freely.

\ms[<options>]{<list>} takes a comma separated list of things to go in the squares. Letters in this case. The optional argument can be used for configuration.

Examples

\ms{S, c, h, a, d,  e, .}

randomised result

With a phrase separated by hyphens:

\ms{Ll,w,y,d,-,f,e,l,-,y,r,-,a,s,y,n,.}

variation

The phrase is from a rhyme in which every line has the form ' like the '. 'Llwyd fel yr asyn' means 'grey like the donkey'2.

With an oblong board, more colour and white letters:

\ms[shade picker={12}{colourseries},board size=8x3,letter colour=white]{R,a,i,n,b,o,w,!}

rainbow variation

Code

\begin{filecontents}[overwrite]{tikzlibraryscrambler.code.tex}
\usetikzlibrary{backgrounds,fit}
% addaswyd o gwestiwn MS-SPO: https://tex.stackexchange.com/q/733354/
% ateb: https://tex.stackexchange.com/a/733378/
\definecolorseries{greyseries}{rgb}{last}[gray]{1}[gray]{0}
\resetcolorseries[50]{greyseries}
\definecolorseries{colourseries}{hsb}{grad}[hsb]{.575,1,1}{.987,-.234,0}
\resetcolorseries[12]{colourseries}
\newcount\scramblerboardwidth
\newcount\scramblerboarddepth
\tikzset{%
  scrambler/.search also={/tikz},
  scrambler/.cd,
  pick shade/.code 2 args={%
    \pgfmathparse{random(1,#1)}%
    \colorlet{lliw}{#2!![\pgfmathresult]}%
  },
  pick shade/.default={50}{greyseries},
  shade picker/.code 2 args={%
    \pgfkeys{/tikz/scrambler/pick shade/.default={#1}{#2}}%
  },
  board size/.code args={#1x#2}{%
    \scramblerboardwidth=#1 \relax
    \scramblerboarddepth=#2 \relax
  },
  board size/.default=7x7,
  letter colour/.code={%
    \colorlet{lettercolour}{#1}%
  },
  letter colour=red,
  bx/.style={minimum size=1cm,text=lettercolour,font={\bfseries},scrambler/pick shade,fill=lliw},
  board/.style={scrambler/pick shade,fill=lliw,inner sep=5mm},
}
\NewDocumentCommand\ms{+O{}+m}{%
  \begin{tikzpicture}
    \pgfkeys{/tikz/scrambler/.cd,board size,#1}%
    \def\tempa{#2}%
    \def\cycleaux##1,##2\null{##1}%
    \def\cyclerem##1,##2\null{##2}%
    \foreach \j [remember=\tempa] in {1,...,\scramblerboarddepth}
    {%
      \foreach \i [remember=\tempa,remember=\tempc]  in {1,...,\scramblerboardwidth} {%
        \edef\tempb{\expandafter\cycleaux\tempa\null}%
        \node[scrambler/bx] at (\i,\j) {\tempb};
        \edef\tempc{\expandafter\cyclerem\tempa\null}%
        \edef\tempa{\tempc,\tempb}%
      }%
      \edef\tempb{\expandafter\cycleaux\tempa\null}%
      \edef\tempc{\expandafter\cyclerem\tempa\null}%
      \edef\tempa{\tempc,\tempb}%
    }%
    \begin{scope}[on background layer]
      \node [scrambler/board, fit=(current bounding box)] at (current bounding box.center) {};
    \end{scope}%
  \end{tikzpicture}%
}
\end{filecontents}
\documentclass[10pt,border=3mm,tikz,rgb]{standalone}
\usetikzlibrary{scrambler}
\begin{document}
\ms{S, c, h, a, d,  e, .}
\ms{Ll,w,y,d,-,f,e,l,-,y,r,-,a,s,y,n,.}
\ms[shade picker={12}{colourseries},board size=8x3,letter colour=white]{R,a,i,n,b,o,w,!}
\end{document}

1Because I bet David can't say it, even if he can gŵgl it.a

2Yes, I know they aren't all grey. Look, I didn't write it, OK? Besides, 'grey or brownish or tan-coloured like the donkey' wouldn't scan.

aNo, Germans can't say it either. No, we didn't name their country or capital with it. You're confusing Germany with England.

6
  • 1
    Wow! Almost a package inside 👍 Commented Dec 22, 2024 at 8:39
  • @MS-SPO updated to make it a single command in the document. not sure it meets your more concise desideratum, though. Commented Dec 22, 2024 at 9:08
  • 1
    Thank you: I'm impressed. // That's a tough question. Because so much functionality is provided by your code, from a refactoring point of view, it would be an option to absorb it all by a style or package (i.e. move it out). This way the main code will be very concise. After all, all those packages we use do just this: provide complexity (ideally) without pain. Commented Dec 22, 2024 at 9:40
  • 2
    @MS-SPO I'd make it a library. please see above. this is usually where I end up when I clean up tikz code because it means I can reuse it. however, it generally makes it longer because it takes more code to define a flexible interface for configuration. Commented Dec 22, 2024 at 15:56
  • 1
    an alternative would be to make it usable inside a tikzpicture (or to allow both), but this seemed a bit less plausible. so possibly a package would be better, actually, than a library. Commented Dec 22, 2024 at 15:58
13

You don't need R to calculate random numbers or any packages for loops or color, LaTeX has all those functions built in to the format.

enter image description here

\documentclass{article}

\begin{document}
\ExplSyntaxOn
\seq_new:N \l_hmm_word_seq
\seq_set_from_clist:Nn\l_hmm_word_seq{S,c,h,a,d,e,.}

\smash{\rlap{{\hspace{-.5\baselineskip}
               \color_select:nn{gray}{0.5}{
                 \rule[-6.8\baselineskip]{8\baselineskip}{8\baselineskip}}}}}
\int_step_inline:nn{7} {
  \seq_map_inline:Nn\l_hmm_word_seq
  {
    \leavevmode
    \rlap{{\exp_args:Nne\color_select:nn{gray}{\fp_eval:n{rand()}}
        \rule[-\dp\strutbox]{\baselineskip}{\baselineskip}}}
     \makebox[\baselineskip]{\strut\color_select:nn{rgb}{1,0,0}##1}
  }
  \par\nointerlineskip
  \seq_shuffle:N\l_hmm_word_seq
}
\ExplSyntaxOff

\end{document}
8
  • Shouldn't you use \color_select:ne instead of \use:e{\exp_not:N...}? But I'd expect \color_select:nn to expand the second argument anyway, so I'd assume that \color_select:nn { gray } { \fp_eval:n { rand() } } works as well. Commented Dec 21, 2024 at 21:44
  • 1
    Also, OP asked for cyclic permutation of "Schade" not random order. Commented Dec 21, 2024 at 21:45
  • 1
    @Skillmon for first comment see chat (I had asked Joseph about that) second comment, sorry my German is better than my English, I missed that. Commented Dec 21, 2024 at 21:54
  • 1
    @MS-SPO background added, so you get your 50 shades Commented Dec 22, 2024 at 11:16
  • 1
    @MS-SPO yes mine would be a bit shorter if l3color provided colorbox for the backgrounds but currently it doesn't so I had to explicitly color an \rlap rule each time which looks a bit messy. Although it would be a bit longer again if I fixed it to cycle rather than random shuffle as that's not pre-defined either. Commented Dec 23, 2024 at 12:23
2

This will make your code longer, but will avoid relying on code which has been deprecated for decades and will result in compilation failures if transplanted into some other classes.

bx/.style={minimum size=1cm,text=red,font={\bfseries}},
1
  • 1
    You're absolutely right. Thanks for pointing out. Commented Dec 22, 2024 at 7:15
1

In an attempt to replace all those manual inputs for the 7 \msrow{}{} calls when continuing refactoring on the posted code I built on these conceptual ideas from Davids and cfrs (the version from a few days ago, now moved into library scrambler) solutions:

  • David: use expl3
  • cfr: provide a cycling-mechanism

cycling-related

Cycling through a set of data usually can be done, e.g. via/by:

  • using several temporary variables (as cfr nicely showed)
  • some pop_left and push_right operations e.g. on a sequence
  • using some kind of modulo approach
  • etc.

However, I decided to apply some concepts from Inventive Thinking. During learning this is a quite linear mental process, while after that it often becomes some kind of parallel processing. So my 3 conceptual beacons are/were:

  • simplify by total replacement
  • make a copy (i.e. duplicate something)
  • Ideality: the variable ITSELF, nothing else, provides cycling

One realization:

  • a word sequence (hm, should have been a sequence of characters for the name)
  • simple index calculation
\seq_new:N              \l_word_seq
\seq_set_from_clist:Nn  \l_word_seq {S,c,h,a,d,e,.,S,c,h,a,d,e,.}% dupl.
%                                    1 2 3 4 5 6 7 8 9 0 1 2 3 4
...
    \node ...  {\seq_item:Nn \l_word_seq {7-#1+\i}};% "cycling" by index

This made the second parameter to pass obsolete in \msrow{}.

expl3-related

To guarantee "50 different grays" I contemplated using expl3 to:

  • initialize a sequence from 1 to 100
  • shuffle it
  • pop_left inside the \foreach loop, i.e. picking just 49 different values from it

However, I struggled retrieving a command for the first list item, though I thought I read it in the manual. So I decided to take the lazy route by just using my pre-run R values, even with the one error included:

\seq_new:N              \l_gray_seq
\str_new:N              \str_gray
\seq_set_from_clist:Nn  \l_gray_seq {37,32,80,12,55,6,17,41,44,95,9,97,88,18,8,8,14,42,11,47,68,99,73,1,5,84,81,40,21,59,43,39,64,82,38,24,85,34,89,50,20,92,65,53,67,76,23,70,30}
...
 \foreach ... \seq_gpop_left:NN \l_gray_seq \str_gray% get next gray value
              \node[bx,fill=black!\str_gray]%          use it

Outlook

cfr provided more ideas to further generalize towards more or less arbitrary lengths of words. Still worth a closer consideration.

While David still wins the shortest-lines-of-code contest ...


\documentclass[10pt,border=3mm,tikz]{standalone}

\ExplSyntaxOn
\seq_new:N              \l_word_seq
\seq_set_from_clist:Nn  \l_word_seq {S,c,h,a,d,e,.,S,c,h,a,d,e,.}% dupl.
%                                    1 2 3 4 5 6 7 8 9 0 1 2 3 4
\seq_new:N              \l_gray_seq
\str_new:N              \str_gray
\seq_set_from_clist:Nn  \l_gray_seq {37,32,80,12,55,6,17,41,44,95,9,97,88,18,8,8,14,42,11,47,68,99,73,1,5,84,81,40,21,59,43,39,64,82,38,24,85,34,89,50,20,92,65,53,67,76,23,70,30}

% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\newcommand\msrow[1]{%  one row of tiles
    \foreach \i in {1,2,...,7}{%
        \seq_gpop_left:NN \l_gray_seq \str_gray% get next gray value
        \node[bx,fill=black!\str_gray]%          use it
            at (\i,#1)%                          place node
            {\seq_item:Nn \l_word_seq {7-#1+\i}};% "cycling" by index
    }}
\ExplSyntaxOff

% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\begin{document}
 \begin{tikzpicture}[bx/.style={minimum  size=1cm,text=red,font={\bfseries}}]
    % ~~~ background ~~~~~~~~~~~~~~~~~~~~~~~~~~
    \node[fill=black!50, minimum size=8cm] at (4,4) {};
    % ~~~ all those tiles ~~~~~~~~~~~~~~~~~~~~~
    \foreach \r in {7,6,5,...,1} \msrow{\r};
 \end{tikzpicture}
\end{document}

result

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.