19

I'm working on a character sheet for the Fate RPG, which rates its skills with a numerical scale that corresponds to an adjective scale. I've been trying to write a macro that will take \skillAdj{4} and turn it into "Great" in a more elegant way than with these conditionals.

Ideally, I'd be able to do lookup in both directions, but I'm more concerned with going from number to adjective.

\usepackage{calc}
\usepackage{ifthen}
%
\newcounter{skillcounter}
\set{skillcounter}{0}
%
\newcommand[1]{\skillAdj}{\ifthenelse{\equal{#1}{8}
Legendary
}
{\ifthenelse{\equal{#1}{7}
Epic
}
{\ifthenelse{\equal{#1}{6}
Fantastic
}
{\ifthenelse{\equal{#1}{5}
Superb
}
{\ifthenelse{\equal{#1}{4}
Great
}
{\ifthenelse{\equal{#1}{3}
Legendary
}
{\ifthenelse{\equal{#1}{2}
Legendary
}
{\ifthenelse{\equal{#1}{1}
Legendary
}
{\ifthenelse{\equal{#1}{0}
Mediocre
}
{\ifthenelse{\equal{#1}{-1}
Poor
}
{\ifthenelse{\equal{#1}{-2}
Terrible
}
{\ifthenelse{\equal{#1}{-3}
Awful
}
{\ifthenelse{\equal{#1}{-4}
Abysmal
}
{Error}}}}}}}}}}}}}

This is the scale.

+8 Legendary
+7 Epic
+6 Fantastic
+5 Superb
+4 Great
+3 Good
+2 Fair
+1 Average
+0 Mediocre
-1 Poor
-2 Terrible
-3 Awful
-4 Abysmal

I'd really appreciate any helpful advice people have. I haven't found anything in the documentation of any of the packages that seem like they might provide this function.

18

There are several ways to accomplish your need. Here's one with expl3:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\skillAdj}{m}
 {
  \int_case:nnn { #1 }
   {
     {8}{Legendary}
     {7}{Epic}
     {6}{Fantastic}
     {5}{Superb}
     {4}{Great}
     {3}{Good}
     {2}{Fair}
     {1}{Average}
     {0}{Mediocre}
    {-1}{Poor}
    {-2}{Terrible}
    {-3}{Awful}
    {-4}{Abysmal}
   }
   {Error}
 }
\ExplSyntaxOff

\begin{document}

X is \skillAdj{8}

Y is \skillAdj{5}

Z is \skillAdj{-4}

\texttt{egreg} is \skillAdj{10}

Come on! How can it be?

\end{document}

enter image description here

You can also say \skillAdj{\value{skillcounter}}, if you want and have defined the counter.


Notice that in your definition you have some glitches:

\set{skillcounter}{0}

should be

\setcounter{skillcounter}{0}

while

\newcommand[1]{\skillAdj}{\ifthenelse{\equal{#1}{8}...

should be

\newcommand{\skillAdj}[1]{\ifthenelse{\equal{#1}{8}...

A very similar solution using xstring:

\documentclass{article}

\usepackage{xstring}
\newcommand{\skillAdj}[1]{%
  \IfEqCase{#1}{%
     {8}{Legendary}%
     {7}{Epic}%
     {6}{Fantastic}%
     {5}{Superb}%
     {4}{Great}%
     {3}{Legendary}%
     {2}{Legendary}%
     {1}{Legendary}%
     {0}{Mediocre}%
    {-1}{Poor}%
    {-2}{Terrible}%
    {-3}{Awful}%
    {-4}{Abysmal}%
   }[Error]
 }

\newcounter{skillcounter}

\begin{document}

\setcounter{skillcounter}{8}

X is \skillAdj{\theskillcounter}

Y is \skillAdj{5}

Z is \skillAdj{-4}

\texttt{egreg} is \skillAdj{10}

Come on! How can it be?

\end{document}

Notice that here you have to use \theskillcounter as \value{skillcounter} wouldn't work.

  • Thanks @egreg. Your solution looks like it will be easier to manage. – JakeO May 13 '13 at 17:04
  • @JakeO I added a version using xstring; but I'd prefer the first one that, however, requires a very up-to-date TeX distribution. – egreg May 13 '13 at 17:10
10

enter image description here

One more TeX \ifcase version, without extra ifnum:

\def\skillAdj#1{%
\count255=8\advance\count255by-#1 %
\ifcase\the\count255 %
Legendary%
\or Epic%
\or Fantastic%
\or Superb%
\or Great%
\or Good%
\or Fair%
\or Average%
\or Mediocre%
\or Poor%
\or Terrible%
\or Awful%
\or Abysmal%
\else Error%
\fi%
}

\def\test#1{#1 \skillAdj{#1}}
\obeylines\tt
\test{+8}
\test{+7}
\test{+6}
\test{+5}
\test{+4}
\test{+3}
\test{+2}
\test{+1}
\test{+0}
\test{-1}
\test{-2}
\test{-3}
\test{-4}
\test{-7}
\test{+9}

\newcount\cnt\cnt5

\test{\the\cnt}

\bye
10

You could also use TeX's in-built \ifcase construct as follows- no packages necessary!

screenshot

% arara: pdflatex
\documentclass{article}

\newcommand{\skillAdj}[1]{%
    \ifcase\numexpr#1+4\relax
            Abysmal      % -4
            \or Awful    % -3
            \or Terrible % -2
            \or Poor     % -1
            \or Mediocre  % 0
            \or Average   % 1
            \or Fair      % 2
            \or Good      % 3
            \or Great     % 4
            \or Superb    % 5
            \or Fantastic % 6
            \or Epic      % 7
            \or Legendary % 8
    \else 
       Error
    \fi
}

\newcounter{skillcounter}

\begin{document}


\setcounter{skillcounter}{-5}

\loop
\theskillcounter: \skillAdj{\theskillcounter}
\stepcounter{skillcounter}\par
\ifnum\value{skillcounter}<10 \repeat

\end{document}

If your smallest value (-4 in the above) changes, just update the line

\ifcase\numexpr#1+4\relax

accordingly.

  • Saver is \ifcase\numexpr(#1)+4\relax. The final \relax is removed automatically by \numexpr and prevents that \numexpr looks for a continuation of the expression in the next tokens. – Heiko Oberdiek May 14 '13 at 15:55
6

Other answers have exploited the numeric character of one of the member of the correspondance. Here is a method which establishes a bidirectional correspondance. It does use the numeric character of the levels to allow some flexibility in the input, like using a count register, or having arithmetic operations on them (uses the e-TeX \numexpr)

The code uses LaTeX and some of its internal macros (with @'s), as I guess the majority of people here do not use Plain TeX.

\documentclass{article}


\makeatletter
% because we will be using \@namedef, \@nameuse, \@ifundefined from the LaTeX
% kernel. 

% but our own private macros are defined *without* @ signs:
%    1. having @ signs everywhere gives headaches
%    2. *not* using @ signs is the safer road to avoid conflicts with packages,
%    as they traditionally do use @ signs in their private macro names. 

\def\gobtilstop #1\stop {}
\def\gobtilz    #1\z {}

\def\SetUpTwoWays #1{\gobtilstop #1\gobtilz\stop\setuptwoways #1\z}

\def\setuptwoways #1,#2\z 
{%
    \@namedef {SkillToAdj\the\numexpr#1}{#2}%
    \@namedef {AdjToSkill#2}{#1}%
    \SetUpTwoWays
}

% we used \the\numexpr to allow the argument of \SkillToAdj to be a count
% register, or something like 2+3 for example.

\def\SkillToAdj #1%
{%
    \@ifundefined{SkillToAdj\the\numexpr#1}
                 {error}
                 {\@nameuse{SkillToAdj\the\numexpr#1}}%
}

\def\AdjToSkill #1%
{%
    \@ifundefined{AdjToSkill#1}
                 {1000000000} % needs to be a number if the output should be
                              % used as input for \SkillToAdj
                 {\@nameuse{AdjToSkill#1}}%
}
\makeatother

% Removing the \the\numexpr above would allow bidirectional correspondances
% between any kind of data.

% Here, the first entry is numeric, but the list given here could be in an
% arbitrary order, doesn't have to be decreasing or increasing, and there could
% be gaps too.

\SetUpTwoWays
    {+8,Legendary}{+7,Epic}{+6,Fantastic}{+5,Superb}
    {+4,Great}{+3,Good}{+2,Fair}{+1,Average}{+0,Mediocre}
    {-1,Poor}{-2,Terrible}{-3,Awful}{-4,Abysmal}\stop

\def\AdjSucc #1{\SkillToAdj{\AdjToSkill{#1}+1}}

\begin{document}

\tt 
\newcount\cnta
\cnta -5
\loop
\the\cnta{} 
   $\longrightarrow$ \SkillToAdj{\cnta}
   $\longrightarrow$ \AdjToSkill{\SkillToAdj{\cnta}}
   $\longrightarrow$ \SkillToAdj{\AdjToSkill{\SkillToAdj{\cnta}}}\endgraf
\ifnum\cnta<10 \advance\cnta 1 \repeat


Terrible (badly input): \AdjToSkill{ Terrible}

Level $3*2$ is \SkillToAdj{3*2}

Level $5-7$ is \SkillToAdj{5-7}

Terrible is worse than \AdjSucc{Terrible} which is worse than \AdjSucc{\AdjSucc{Terrible}}


\end{document}

output

  • Thank you very much for this, the bidirectional hookup is useful. So far all of the answers people have given have been beyond my understanding--I've only used LaTeX in the past to format papers and such. When I have more time I'll try to truly understand how the code works, but until then these solutions are all very helpful. – JakeO May 14 '13 at 17:28
  • Glad it helps! the idea is to automatize the creation of macros such as \SkillToAdj2 which expands to Fair. But to get 2 inside the macro name one uses the \csname primitive, here hidden in LaTeX \@namedef. Conversely \AdjToSkillFair is defined to expand to +2. It could have been done simply by \def\AdjToSkillFair {+2}. But I used also \@namedef as there were various other strings to use, besides Fair. As the +2 may be input in various ways, like 2 or 1+1, it was filtered through \numexpr. Text strings should also be filtered for more robust code allowing spaces. – user4686 May 14 '13 at 17:57
5

The following is a mild modification to PGF/TikZ: How to store strings in array? The adjustment accommodates for negative values (excluding a count of the elements since it may not be required):

enter image description here

\documentclass{article}
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\newcounter{listtotal}\newcounter{listcntr}%
\newcommand{\skilladj}[1]{% \skilladj{<number>}
  \setcounter{listcntr}{-5}% Start from -4
  \renewcommand*{\do}[1]{\stepcounter{listcntr}\ifnum\value{listcntr}=#1\relax##1\fi}%
  \expandafter\docsvlist\expandafter{\skilladjarray}% Process list again
}
% Skill Adjustment array
\newcommand{\skilladjarray}{Abysmal,Awful,Terrible,Poor,Mediocre,Average,Fair,Good,Great,Superb,Fantastic,Epic,Legendary}%
\begin{document}
\verb|\skilladj{-2}:|\ \skilladj{-2} \par
\verb|\skilladj{2}:|\ \skilladj{2} \par
\verb|\skilladj{0}:|\ \skilladj{0} \par
\verb|\skilladj{5}:|\ \skilladj{5} \par
\verb|\skilladj{-5}:|\ \skilladj{-5} \par
\verb|\skilladj{9}:|\ \skilladj{9} \par
\verb|\skilladj{8}:|\ \skilladj{8}
\end{document}
  • Thank you very much @Werner, this is exactly what I need! – JakeO May 13 '13 at 16:56

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.