5

I want to typeset MinMax decision trees in LaTeX, just like the one in following picture:

Sketch of what I want to achieve

I have played around a little and achieved part of my goals:

\documentclass{article}

\usepackage{tikz,tikz-qtree} 
\usetikzlibrary{backgrounds}

\newcommand{\ply}[3][black]{\footnotesize{#2 ({\color{#1}#3})}}
\newcommand{\player}[1]{\footnotesize \emph #1}

\begin{document}

\begin{tikzpicture}[framed]
\Tree [.{} \edge[opacity=0]; [.\player{MAX:} \edge[opacity=0]; 
                              [.\player{MIN:} \edge[opacity=0]; 
                               [.\player{MAX:} \edge[opacity=0]; 
                                [.\player{MIN:} ]]]]
           \edge[opacity=0]; 
            [.\ply[red]{4}{+1}
              \edge node[auto=right]{-1}; 
              [. \ply[red]{3}{-1} 
                 \edge node[auto=right]{-1}; 
                 [. \ply[red]{2}{+1}
                    \edge node[auto=right]{-1}; [.\ply{1}{+1} ] ]
                 \edge node[auto=left]{-2}; \ply{1}{-1} ]
              \edge node[auto=right]{-2}; \ply[red]{2}{-1}
              \edge node[auto=left]{-3}; \ply{1}{+1}
              ]]
\end{tikzpicture}     

\end{document}

Latex output of what I have until now

I still need to implement some features:

  • Decrease the margin on top of the picture (remove the invisible root node)
  • Make the edge label size smaller, preferably \scriptsize
  • Add the red upward arrows from the drawing
  • Make the MIN / MAX labels align right, if possible

My knowledge of tikz is not enough to do those things, so I would be very thankful if someone here could help me!

2

How brave are you feeling?

The following solution uses an experimental package based on forest. Nobody seems willing to test prooftrees, so I thought I'd try justtrees as well.

First, the result:

justtrees tree

Next, the code:

\documentclass[tikz,border=5pt,multi]{standalone}
\usepackage{justtrees}
\usepackage{cfr-lm}% for italic small-caps
\newcommand{\ply}[3][black]{\footnotesize{#2 ({\color{#1}#3})}}
\newcommand{\player}[1]{\footnotesize \emph #1}

\begin{document}

  \begin{forest}
    just tree,
    just format/.style={font=\sishape},% italic small-caps
    for tree={
      font=\tlstyle,% for tabular, lining figures with cfr-lm
      alias/.wrap 2 pgfmath args={#1-#2}{int(subtract(level(),1))}{n()}% labels the nodes systematically so we can refer to them later
    },
    my edge label/.style n args=2{% save typing when creating the edge labels
      edge label={node [midway, font=\scriptsize\tlstyle, #1] {#2}}
    },
    [, phantom
      [{\ply[red]{4}{+1}}, left just={Max:}
        [{\ply[red]{3}{-1}}, left just={Min:}, my edge label={left}{-1}
          [{\ply[red]{2}{+1}}, left just={Max:}, my edge label={left}{-1}
            [{\ply{1}{+1}}, left just={Min:}, my edge label={left}{-1}
            ]
          ]
          [{\ply{1}{-1}}, my edge label={left}{-2}]
        ]
        [{\ply[red]{2}{-1}}, calign with current, my edge label={left, pos=.75}{-2}]
        [{\ply{1}{+1}}, my edge label={right}{-3}]
      ]
    ]
    \tikzset{->}
    \draw [red] (1-3.north) [bend right] to (0-1.east);
    \draw [red] ([xshift=2.5pt]3-1.north) [bend right] to ([xshift=2.5pt]2-1.south);
    \draw [red] (2-2.north) [bend right] to (1-1.east);
  \end{forest}

\end{document}

Finally, the package (save as justtrees.sty in the same directory as your document):

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{justtrees}[2015/05/30 v0.01 justtrees]
\RequirePackage{forest}
\newcounter{justtree@countlevels}% count the levels in the just tree
\setcounter{justtree@countlevels}{0}
\newcount\justtree@lcount% count the left justifiers (on the left)
\newcount\justtree@rcount% count the right justifiers (on the right)
\forestset{
  just format/.style={font=\normalfont\normalsize},
  declare boolean={left justifiers}{0},% left justifications
  declare boolean={right justifiers}{0},% right justifications
  right justifier/.style={% creates the right justifiers on the right but does not yet specify any content
    anchor=base west,
    no edge,
    before typesetting nodes={% page 51
      TeX={\advance\justtree@rcount1},
      name/.expanded={right just \the\justtree@rcount},% name them so they can be moved
      if={\the\justtree@rcount>2}{% correct the location as for the left justifiers
        for previous={
          append/.expanded={right just \the\justtree@rcount}
        },
      }{},
    },
  },
  left justifier/.style={% creates the left justifiers on the left but does not yet specify any content
    anchor=base east,
    no edge,
    before typesetting nodes={% page 51
      TeX={\advance\justtree@lcount1},
      name/.expanded={left just \the\justtree@lcount},% name them so they can be moved
      if={\the\justtree@lcount>2}{% correct the location as for the left justifiers
        for previous={
          append/.expanded={left just \the\justtree@lcount}
        },
      }{},
    },
  },
  left justifications/.style={
    for tree={left justifiers},
  },
  no left justifications/.style={
    for tree={not left justifiers},
  },
  right justifications/.style={
    for tree={right justifiers},
  },
  no right justifications/.style={
    for tree={not right justifiers},
  },
  just tree/.style={
    for tree={
      parent anchor=south,
      delay={
        where content={}{
          shape=coordinate,
        }{}
      },
    },
    where level=0{
      for children={
        no edge,
      },
      delay={
        if={(\forestove{left justifiers}==1) || (\forestove{right justifiers}==1)}{% count the levels if necessary
          for descendants={
            if={level()>\value{justtree@countlevels}}{
              TeX={
                \stepcounter{justtree@countlevels}
              },
            }{},
          },
        }{},
        if={(\forestove{left justifiers}==1)}{% create the left justifiers if appropriate
          prepend={
            [,
              fit=rectangle,
              just format,
              left justifier,
              repeat={\value{justtree@countlevels}-1}{% most are created in the wrong place but they are moved later
                delay n={\the\justtree@lcount}{
                    append={[, left justifier, just format]}
                },
              }
            ]
          },
        }{},
        if={(\forestove{right justifiers}==1}{% create the nodes which will hold the right justifiers, if required
          append={
            [,
              fit=rectangle,
              right justifier,
              just format,
              repeat={\value{justtree@countlevels}-1}{% most are created in the wrong place but right justifier moves them later
                delay n={\the\justtree@rcount}{
                  append={[, right justifier, just format]}
                },
              }
            ]
          }
        }{},
      },
    }{},
    before packing={
      for tree={
        tier/.wrap pgfmath arg={tier ##1}{level()},
      },
    },
  },
  right just/.style={
    if={\forestove{right justifiers}==0}{
      for root={
        right justifiers,
      },
    }{},
      before packing={% puts the content of the right justifiers into the empty right justifier nodes on the right; because this is done late, the nodes need to be typeset again
        for={name/.wrap pgfmath arg={right just ##1}{level()}}{
          content={#1},
          typeset node
        },
      }
  },
  left just/.style={
    if={\forestove{left justifiers}==0}{
      for root={
        left justifiers,
      },
    }{},
    before packing={% puts the content of the left justifiers into the empty left justifier nodes on the left; because this is done late, the nodes need to be typeset again
      for={name/.wrap pgfmath arg={left just ##1}{level()}}{
        content={#1},
        typeset node
      },
    }
  },
}
\endinput
%% end justtrees.sty

I would have posted this anonymously, but that seems unfair with experimental code so I've broken my rules again. Sigh.

| improve this answer | |
  • If anybody considers using this, please ask for the current package version (0.03). – cfr Jul 3 '15 at 1:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.