17

I would like to typeset some easy polyomino puzzles and therefore need a suitable LaTeX package. I could not find an easy to use package (other than using TikZ of course). Is there any approach other than using TikZ/PSTricks?

For anybody not aware of what polyominos are: http://en.wikipedia.org/wiki/Polyomino

Edit: With a polyomino puzzle, I mean something like this: http://www.virtualimage.co.uk/html/polyomino.html

So my intention is to put a set of polyominos into a given shape.

13
  • Any such package would in turn use TikZ/PSTricks, and probably have a very TikZ/MP-like interface. If you can make a polyomino with TikZ, then I suggest you simply write such a package, as one does not appear to exist. Commented Apr 23, 2013 at 13:21
  • 1
    @SeanAllred I believe that this is more a question of a good user interface than of the programming layer. Many LaTeX packages could witness that.
    – yo'
    Commented Apr 23, 2013 at 13:25
  • @tohecz than perhaps, in the complete absence of any extant package, a ideal user interface should be defined. I find it difficult to envision a consistent interface that would cover all the different configurations of every polyomino. Commented Apr 23, 2013 at 13:26
  • @SeanAllred; I think you are probably right. Since there are already 35 free forms for a polyomino of 6 blocks, it will be difficult to provide some intuitive way of specifying them with single commands. Would be an interesting idea for a package though.
    – Mythio
    Commented Apr 23, 2013 at 13:57
  • 1
    Have a look at Donald Knuth's metapost program for drawing polyominos.
    – user10274
    Commented Apr 25, 2013 at 10:50

5 Answers 5

20

Code 1

\documentclass[tikz,border=2pt]{standalone}
\usetikzlibrary{matrix}
\makeatletter
\newcounter{qrr@tikz@omino}
\newcounter{qrr@tikz@omino@up}
\newcounter{qrr@tikz@omino@right}
\tikzset{
    omino/.style={/tikz/omino/.cd,#1},
    omino/distance/.initial=1,
    omino/radius/.initial=.5,
    omino/at/.style={/tikz/shift={(#1)}},
    omino/rotate/.style={/tikz/rotate=#1},
    omino/s/.code=
        \setcounter{qrr@tikz@omino}{0}%
        \setcounter{qrr@tikz@omino@up}{0}%
        \setcounter{qrr@tikz@omino@right}{0}%
        \pgfkeysalso{/tikz/insert path={(0,0) node[/tikz/omino/nodes/.try,/tikz/omino/node normal/.try,/tikz/omino/node start/.try] {\qrr@tikz@omino@text@start}}},
    omino/u/.code=%
        \stepcounter{qrr@tikz@omino}%
        \stepcounter{qrr@tikz@omino@up}%
        \pgfkeysalso{/tikz/insert path={
            to[/tikz/omino/how] ++(up:#1)
            node[/tikz/omino/nodes/.try,/tikz/omino/node normal/.try,/tikz/omino/node up/.try]{\qrr@tikz@omino@text@up}}},
    omino/d/.code=%
        \stepcounter{qrr@tikz@omino}%
        \addtocounter{qrr@tikz@omino@up}{-1}%
        \pgfkeysalso{/tikz/insert path={
            to[/tikz/omino/how] ++(down:#1)
            node[/tikz/omino/nodes/.try,/tikz/omino/node normal/.try,/tikz/omino/node down/.try]{\qrr@tikz@omino@text@down}}},
    omino/l/.code=%
        \stepcounter{qrr@tikz@omino}%
        \addtocounter{qrr@tikz@omino@right}{-1}%
        \pgfkeysalso{/tikz/insert path={
            to[/tikz/omino/how] ++(left:#1)
            node[/tikz/omino/nodes/.try,/tikz/omino/node normal/.try,/tikz/omino/node left/.try]{\qrr@tikz@omino@text@left}}},
    omino/r/.code=%
        \stepcounter{qrr@tikz@omino}%
        \stepcounter{qrr@tikz@omino@right}%
        \pgfkeysalso{/tikz/insert path={
            to[/tikz/omino/how] ++(right:#1)
            node[/tikz/omino/nodes/.try,/tikz/omino/node normal/.try,/tikz/omino/node right/.try] {\qrr@tikz@omino@text@right}}},
    omino/u/.default=\pgfkeysvalueof{/tikz/omino/distance},
    omino/d/.default=\pgfkeysvalueof{/tikz/omino/distance},
    omino/l/.default=\pgfkeysvalueof{/tikz/omino/distance},
    omino/r/.default=\pgfkeysvalueof{/tikz/omino/distance},
    omino/how/.style=,
    omino/reset/.code=
        \pgfutil@in@_{#1}%
        \ifpgfutil@in@
            \qrr@tikz@omino@split#1\relax
        \else
            \edef\pgf@tempa{\csname qrr@tikz@omino@coords@#1\endcsname}%
            \expandafter\qrr@tikz@omino@split\pgf@tempa\relax
        \fi
        \pgfkeysalso{/tikz/insert path={(omino-n-#1.center) node[/tikz/omino/nodes/.try, /tikz/omino/node reset/.try] {\qrr@tikz@omino@text@reset}}},
    omino/do/.code={\@tfor\@next:=#1\do{\pgfkeysalso{/tikz/omino/\@next}}},
    omino/node reset/.style={draw=none,fill=none},
    omino/node normal/.style={
        name=omino-n-\number\c@qrr@tikz@omino,
        alias=omino-n-\number\c@qrr@tikz@omino@right_\number\c@qrr@tikz@omino@up,
        omino/@store coords
    },
    omino/@store coords/.code=
        \expandafter\xdef\csname qrr@tikz@omino@coords@\arabic{qrr@tikz@omino}\endcsname
        {\number\c@qrr@tikz@omino@right_\number\c@qrr@tikz@omino@up},
    omino/Text/.code 2 args=\expandafter\edef\csname qrr@tikz@omino@text@#1\endcsname{#2},
    omino/Text={up}{},omino/Text={down}{},omino/Text={left}{},omino/Text={right}{},omino/Text={start}{},omino/Text={reset}{}
}
\def\qrr@tikz@omino@split#1_#2\relax{\setcounter{qrr@tikz@omino@right}{#1}\setcounter{qrr@tikz@omino@up}{#2}}

\tikzset{
    omino/x mirror/.style={/tikz/cm={-1,0,0,1,(0,0)}},
    omino/y mirror/.style={/tikz/cm={1,0,0,-1,(0,0)}}
}

\tikzset{fun/.code={\pgfmathtruncatemacro\@fun{\number\c@qrr@tikz@omino/4*100}\pgfkeysalso{fill=blue!\@fun!red}}}
\makeatother
\tikzset{
    tetris/.style={/tikz/tetris/.cd,#1},
    tetris/1/.style={/tikz/omino={do=suuu}},
    tetris/2/.style={/tikz/omino={do=suur}},
    tetris/2'/.style={/tikz/omino/x mirror, 2},
    tetris/3/.style={/tikz/omino={do=suru}},
    tetris/3'/.style={/tikz/omino/x mirror, 3},
    tetris/4/.style={/tikz/omino={do=surd}},
    tetris/5/.style={/tikz/omino={s,u,u,reset=1,r}}
}
\makeatletter

\makeatother
\newcommand*{\thesamepictureeverywhere}{\matrix[column sep=.5cm, row sep=.5cm, ampersand replacement=\&] {
\path [tetris=1];  \&
\path [tetris=2']; \& \path [tetris=2]; \\
\path [tetris=3];  \& \path [tetris=3']; \&
\path [tetris=4];  \&
\path [tetris=5];\\};}
\begin{document}
\begin{tikzpicture}[
    every path/.style=draw,
    omino/how/.style={bend left},
    omino/nodes/.style={circle, inner sep=+0pt, minimum size=+4pt, fill=black, outer sep=+0pt}]
\thesamepictureeverywhere
\end{tikzpicture}
\begin{tikzpicture}[omino/nodes/.style={rectangle, inner sep=+0pt, minimum size=+1cm, draw}]
\thesamepictureeverywhere
\end{tikzpicture}

\begin{tikzpicture}[omino={
        nodes/.style={circle, inner sep=+0pt, minimum size=+1cm, draw},
        Text={left}l,Text={right}r,Text={up}u,Text={down}d,Text={start}s
        }]
\thesamepictureeverywhere
\end{tikzpicture}

\begin{tikzpicture}[omino/nodes/.style={shape=circle, inner sep=+0pt, minimum size=+1cm, fun}]
\thesamepictureeverywhere
\end{tikzpicture}

\tikzset{
    tetris/1/.prefix style={/tikz/omino/nodes/.append style={fill=black,text=white}},
    tetris/2/.prefix style={/tikz/omino/nodes/.append style={fill=blue}},
    tetris/3/.prefix style={/tikz/omino/nodes/.append style={fill=green!70!blue}},
    tetris/4/.prefix style={/tikz/omino/nodes/.append style={fill=yellow!80!black}},
    tetris/5/.prefix style={/tikz/omino/nodes/.append style={fill=red!70!black}},
    omino/Text={start}{.}
}
\begin{tikzpicture}[omino/nodes/.style={shape=rectangle, rounded corners, inner sep=+0pt, minimum size=1cm-2\pgflinewidth}]
    \path [tetris=1];
    \path [omino/at=0:1] [tetris=2];
    \path [omino/at=0:2] [tetris=3];
    \path [omino={at=0:3, rotate=-90, x mirror}][tetris=5];
    \path [omino={at={5,1}, rotate=-90}][tetris=3];
    \path [omino={at={5,2}, rotate=-90}][tetris=2];
    \path [omino={at={4,2}, x mirror}][tetris=5];
    \path [omino={at={1,3}}][tetris=4];
\end{tikzpicture}
\end{document}

Output 1

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

Code 2

\documentclass[tikz,border=2pt,convert=false]{standalone}
\usetikzlibrary{matrix}
\makeatletter
\tikzset{
    omino/.style={/tikz/omino.cd,#1},
    omino/row sep/.initial=1cm,
    omino/column sep/.initial=1cm}
\newcounter{qrr@tikz@omino@y}
\newcounter{qrr@tikz@omino@x}
\def\qrr@tikz@omino@empty{\stepcounter{qrr@tikz@omino@x}}
\def\qrr@tikz@omino@full{%
    \stepcounter{qrr@tikz@omino@x}
    \node[
        every omino node/.try,
        every omino row \number\c@qrr@tikz@omino@y/.try,
        every omino column \number\c@qrr@tikz@omino@x/.try,
        every omino cell \number\c@qrr@tikz@omino@x-\number\c@qrr@tikz@omino@y/.try,
        at={(\c@qrr@tikz@omino@x*\pgfkeysvalueof{/tikz/omino/column sep},\c@qrr@tikz@omino@y*\pgfkeysvalueof{/tikz/omino/row sep})}
        ]{};}
\def\qrr@tikz@omino@nextrow{\setcounter{qrr@tikz@omino@x}{0}\stepcounter{qrr@tikz@omino@y}}
\def\qrr@tikz@omino@final{\endgroup\@gobble}
\def\qrr@omino{%
    \afterassignment\qrr@@omino\let\qrr@next@token=}
\def\qrr@@omino{%
    \let\qrr@next\relax
    \ifx\qrr@next@token.%
        \let\qrr@next\qrr@tikz@omino@empty
    \else
        \ifx\qrr@next@token*%
            \let\qrr@next\qrr@tikz@omino@full
        \else
            \ifx\qrr@next@token:
                \let\qrr@next\qrr@tikz@omino@nextrow
            \else
                \ifx\qrr@next@token;
                    \let\qrr@next\qrr@tikz@omino@final
                \fi
            \fi
        \fi
    \fi
    \qrr@next\qrr@omino
}
\newcommand*{\omino}[1][]{\begingroup\tikzset{#1}\setcounter{qrr@tikz@omino@x}{0}\setcounter{qrr@tikz@omino@y}{0}\qrr@omino}
\makeatother

\tikzset{every omino node/.style={circle, inner sep=+0pt, minimum size=+1cm}}
\begin{document}
\begin{tikzpicture}[every omino node/.append style={rectangle, fill,draw}]
\matrix[column sep=.5cm]{
\omino *:*:*:*; & \omino **:*:*; & \omino *:**:*; & \omino .*:**:*; \\};
\end{tikzpicture}
\begin{tikzpicture}[
    every omino row 0/.style={fill=blue},
    every omino row 1/.style={fill=blue!33!red},
    every omino row 2/.style={fill=blue!67!red},
    every omino row 3/.style={fill=red}]
\matrix[column sep=.5cm]{
\omino *:*:*:*; & \omino **:*:*; & \omino *:**:*; & \omino .*:**:*; \\};
\end{tikzpicture}
\end{document}

Output 2

enter image description here

enter image description here

7
  • can you insert onimo/A/.style={/tikz/insert path={coordinate[/tikz/onimo/how](#1)}}, into the tikzset? That way, one can easily define an anchor via A=anchorname, and jump back to it via reset?
    – Toscho
    Commented Apr 23, 2013 at 17:17
  • @Toscho In my updated answer, nodes are named consecutively omino-n-<counter> and omino-n-<right>-<up> where <counter> increments from 0, and <right> and <up> are the position in relation to the first node. These names can be used for reset (either 1 or 0_1 in the last case). Commented Apr 23, 2013 at 17:47
  • This definitely exceeds my Tikz skills :-) The only thing left is a grid to actually place the polyominos (so it is a puzzle or a tetris game).
    – user26372
    Commented Apr 23, 2013 at 19:50
  • @user26372 I have extended my first code and added an alternative. In both solutions, you can place these polyominos with at (first solution, uses shift internally) or shift (both solutions). In the first solution you can re-define the path, so that the start coordinate lies more in the middle tiles … It ultimately comes down to how you want to input these figures and where you want to use them … Commented Apr 23, 2013 at 21:23
  • I edited my question to address that a little better. Hope you get what I mean. But your solution (especially the last example of your first piece of code) is already very close to what I was hoping for.
    – user26372
    Commented Apr 23, 2013 at 22:08
15

enter image description here

\documentclass{article}

\makeatletter
\def\omino#1{{%
\unitlength10\p@
\@tempcnta\z@
\@tempcntb\@ne
\count@\z@
\xomino#1\relax
\fbox{\begin{picture}(\@tempcnta,\@tempcntb)(0,-\@tempcntb)%
\@tempcnta\z@
\@tempcntb\@ne
\count@\z@
\xxomino#1\relax
\end{picture}}%
}%
}

\def\xomino#1{%
\ifx\relax#1%
\else
\ifx\\#1%
\ifnum\count@>\@tempcnta \@tempcnta\count@\fi
\advance\@tempcntb\@ne
\count@\z@
\else
\advance\count@\@ne
\fi
\expandafter\xomino
\fi}

\def\xxomino#1{%
\ifx\relax#1%
\else
  \ifx\\#1%
    \advance\@tempcntb\@ne
    \count@\z@
  \else
    \advance\count@\@ne
    \ifx*#1%
      \put(\count@,-\@tempcntb){\kern-10pt\rule{10pt}{10pt}}%
    \fi
  \fi
\expandafter\xxomino
\fi}


\makeatother

\begin{document}

\omino{
*\\
*\\
*\\
*\\
*}

\omino{
**\\
.*\\
.*\\
.*}

\omino{
**\\
*\\
*\\
**}
\end{document}
8
  • Wow. You're very quick. :-) But this doesn't allow you to interlock multiple polyominos into a puzzle (with visually distinct pieces). I put my thoughts on a possible interface for that in a comment above. I don't have the time to actually implement it, though.
    – mhelvens
    Commented Apr 23, 2013 at 16:22
  • 2
    Addendum: click here and enjoy. :) Commented Apr 23, 2013 at 16:23
  • @mhelvens it's just picture mode so the pieces can be placed in interlocking position (the \fbox around the outside is just for debugging really) , if you want thin gaps to show the original squares then you can reduce the size of the rule from 10pt so it is smaller than the coordinate spacing. Commented Apr 23, 2013 at 16:31
  • I very much like the way how you create the polyominos. Is it possible to put them into a puzzle (grid), too?
    – user26372
    Commented Apr 23, 2013 at 19:46
  • 1
    A solution without loading any packages; very nice! Commented Apr 23, 2013 at 22:07
4

David's solution converts ascii art into graphics. Here is a derived version that does a similar trick.

\documentclass{article}
\usepackage{color}

\newdimen\omsq  \omsq=20pt
\newdimen\omrule    \omrule=2pt
\newdimen\omint

\newif\ifvth    \newif\ifhth    \newif\ifomblank
\def\OMINO#1{%
    \vthtrue \hthtrue
    \vbox{ \offinterlineskip\parindent=0pt \OM#1\relax\vskip1pt}}


\def\OM#1{%
    \omint=\omsq    \advance\omint-\omrule
    \ifx\relax#1%
    \else
      \ifx\\#1 \newline\null \hthtrue \ifvth\vthfalse\else\vskip-\omrule\vthtrue\fi
      \else%
        \ifx .#1\hskip\ifhth \omrule\else \omint\fi
        \else%
          \ifx +#1\def\colour{black}\fi%
          \ifx -#1\def\colour{black}\fi%
          \ifx |#1\def\colour{black}\fi%
          \ifx @#1\def\colour{black}\fi%
          \ifx r#1\def\colour{red}\fi%
          \ifx g#1\def\colour{green}\fi%
          \ifx b#1\def\colour{blue}\fi%
          \ifx y#1\def\colour{yellow}\fi%
          \ifx m#1\def\colour{magenta}\fi%
          \ifx c#1\def\colour{cyan}\fi%
          \textcolor{\colour}{\rule{\ifhth\omrule\else\omsq\fi}{\ifvth\omrule\else\omsq\fi}}%
          \ifhth\else\hskip -\omrule\fi%
        \fi%
        \ifhth\hthfalse\else\hthtrue\fi%
      \fi%
    \expandafter\OM%
    \fi}

\makeatother

\begin{document}

\OMINO{
+-+-----+\\
|g|@|r.r|\\
|.+-+.+-+\\
|g.g|r|.|\\
+-+.|.|.|\\
|c|g|r|.|\\
|.+-+-+.|\\
|c.c.c|.|\\
+-----+-+
}

\OMINO{
+-+.......+-----+\\
|.|.......|r.r.r|\\
|.+-+-----+---+.|\\
|...|y.y.y.y.y|r|\\
+-+.+-+-----+-+.|\\
..|...|.....|r.r|\\
..+---+.....+---+
}

\end{document}

enter image description here

5
  • Love it. So many great answers!
    – user26372
    Commented Apr 25, 2013 at 15:10
  • I tried putting one of these into a figure environment, but the last row becomes displaced when the figure is centered. I can add an extra line so the row is in the correct position, but even then the figure won't be properly centered.
    – ViHdzP
    Commented Oct 4, 2020 at 19:22
  • @URL this is strange. The \vbox{} at the outermost level of \OMINO should stop any higher-level environments messing with anything. Can you post a MWE? Commented Oct 6, 2020 at 23:12
  • @AndrewKepert MWE is simply the above code, but one of the OMINOs is enclosed in a figure environment. The figure is then centered via the \centering command.
    – ViHdzP
    Commented Oct 6, 2020 at 23:20
  • Ah OK I see what is going on - \OMINO is all typeset as though it's a paragraph, and assumes consistent left margins — in particular \leftskip should be non-stretchy. But \centering which uses stretchy glue for both \leftskip and \rightskip. The last line gets broken by how TeX finishes paragraphs. The disaster is not limited to the last line, in that if you have an \OMINO pattern with lines of different lengths, then they will individually be centred. Fix: \begin{figure}\begin{centering}\begin{minipage}{7cm}\OMINO{... }\end{minipage}\end{centering}\end{figure} Commented Oct 18, 2020 at 11:51
3

May be something like this in crude form

enter image description here

    \documentclass{article}
    \begin{document}
    \begin{picture}(200,200)(120,120)
    \setlength{\unitlength}{1mm}
    \rule[0\unitlength]{10\unitlength}{20\unitlength}\rule[0\unitlength]{20\unitlength}{10\unitlength}
    \rule[0\unitlength]{10\unitlength}{20\unitlength}\rule[10\unitlength]{10\unitlength}{20\unitlength}
    \rule[0\unitlength]{20\unitlength}{10\unitlength}\rule[10\unitlength]{20\unitlength}{10\unitlength}
    \rule[0\unitlength]{20\unitlength}{10\unitlength}\rule[0\unitlength]{10\unitlength}{30\unitlength}
    \rule[0\unitlength]{10\unitlength}{30\unitlength}\rule[10\unitlength]{10\unitlength}{10\unitlength}
    \rule[0\unitlength]{10\unitlength}{20\unitlength}\rule[-20\unitlength]{10\unitlength}{20\unitlength}
    \rule[0\unitlength]{10\unitlength}{20\unitlength}\rule[10\unitlength]{10\unitlength}{10\unitlength}\rule[0\unitlength]{10\unitlength}{20\unitlength}
    \end{picture}
    \end{document}
0

Proposal for an interface:

d is a digit from 1 to 9 coordinate=d_nd_{n-1}d_{n-2}…d_2d_1 is a number out of digits (without 0) pattern=number is a number, in which each digit may appear at most once and 0 not at all

A polyomino is given by a dictionary with coordinates as keys and patterns as value. What does it mean?

Imagine a keyboard number pad or a telephone number pad. The digits 1-9 are arranged in a 3x3 grid. The digits above all identify one such cell of a 3x3 grid. The pattern is a number. Each digit occuring in the number means, that the identified cell is part of the polyomino.

Now polyominos can be larger than a 3x3-pattern. So we can arrange 3x3 of these 3x3-patterns into a 3x3 meta-pattern. The position of the pattern in this meta-pattern is specified by the digit d_1 of the coordinate. To get even larger, we can put 3x3 meta-patterns into a meta-meta-pattern. The position inside this meta-meta-pattern is specified by d_2. And so on.

Shorthands: coordinate=0 means, the last digit in coordinate is not d_1 but d_0 specifying the cell inside the pattern located at the coordinate created from the previous digits of coordinate.

0=pattern means, that the whole polyomino fits into one 3x3-pattern and is described by pattern.

So some examples: (with the base pattern being 789 456 123)

  • f-pentomino is 0=24589
  • N-pentomino is (1=1478,4=2)
  • I-dekomino is (11=123,12=123,13=123,21=1)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .