Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm writing a package to assist typesetting documents for some special Pen & Paper Fantasy RPG.

In this fantasy setting, there are three types of coins: gold, silver and copper pieces, which convert 10:1 each. However, many groups play with a different conversion factor (100:1). Because I want to make things as flexible as possible, I define a command \gold that will just take the number of copper pieces and convert into the appropriate pricing string.

However, as an additional gimmick, I want to support writing out the data of all purchasable items into an external file (using \addcontentsline) which can then be re-read to produce a table with all the pricing information collected in one spot. Here is my solution:

\documentclass{article}

\usepackage{etoolbox}

\makeatletter

\def\makedatalines#1{
  \AtEndDocument{
    \clearpage
    \if@filesw
    \expandafter\newwrite\csname tf@#1.data\endcsname
    \immediate\openout\expandafter\csname tf@#1.data\endcsname%
    \jobname.#1.data\relax
    \fi
  }
}
\def\adddataline#1#2{\expandafter\addtocontents{#1.data}{#2}}

\newcommand{\copperpiecesAlias}{CP}
\newcommand{\silverpiecesAlias}{SP}
\newcommand{\goldpiecesAlias}{GP}

\numgdef{\CopperPerSilver}{10}
\numgdef{\SilverPerGold  }{10}
\newcommand {\setCopperPerSilver}[1]{\numgdef{\CopperPerSilver}{#1}}
\newcommand {\setSilverPerGold}[1]{\numgdef{\SilverPerGold}{#1}}
\newcommand{\gold}[1]{%
  \numdef{\copperpieces}{#1}%
  \ifnumless{\copperpieces}{\CopperPerSilver}{%
    \numdef{\silverpieces}{0}%
  }{%
    \numdef{\silverpieces}{(10*\copperpieces/\CopperPerSilver - 5)/10}%
    \numdef{\copperpieces}{\copperpieces - (\CopperPerSilver * \silverpieces)}%
  }%
  \ifnumless{\silverpieces}{\SilverPerGold}{%
    \numdef{\goldpieces}{0}%
  }{%
    \numdef{\goldpieces}{(10*\silverpieces /\SilverPerGold - 5)/10}%
    \numdef{\silverpieces}{\silverpieces - (\SilverPerGold * \goldpieces)}%
  }%
  \ifnumgreater{\goldpieces  }{0}{%
    \goldpieces\,\goldpiecesAlias%
    \ifnumgreater{\silverpieces}{0}{ }{%
      \ifnumgreater{\copperpieces}{0}{ }{}%
    }%
  }{}%
  \ifnumgreater{\silverpieces}{0}{%
    \silverpieces\,\silverpiecesAlias%
    \ifnumgreater{\copperpieces}{0}{ }{}%
  }{}%
  \ifnumgreater{\copperpieces}{0}{%
    \copperpieces\,\copperpiecesAlias%
  }{}%
}

\makedatalines{prices}

\newcommand\purchasable[2]{\textit{#1} (#2)\adddataline{prices}{#1: #2}}

\makeatother

\begin{document}

\purchasable{Longsword}    {\gold{1000}}
\purchasable{Broadsword}   {\gold{500}}
\purchasable{Magic Staff}  {\gold{50000}}
\purchasable{Loaf of Bread}{\gold{5}}
\purchasable{Chicken}      {\gold{10}-\gold{50}}

\end{document}

The conversion works nicely - however: the external file now contains lines like

Longsword: \numdef {0}{1000}\numdef {0}{0}\numdef {10}{0}10\,GP
Broadsword: \numdef {0}{500}\numdef {0}{0}\numdef {5}{0}5\,GP
Magic Staff: \numdef {0}{50000}\numdef {0}{0}\numdef {500}{0}500\,GP
Loaf of Bread: \numdef {5}{5}\numdef {0}{0}\numdef {0}{0}5\,CP
Chicken: \numdef {0}{10}\numdef {5}{0}\numdef {0}{0}5\,SP-\numdef {0}{50}\numdef {5}{0}\numdef {0}{0}5\,SP 

which is clearly not what I was hoping for (please note especially how messed-up the last line is - these are not even the correct numbers). I would need the external file to contain lines like

Longsword: 10\,GP
Broadsword: 5\,GP
Magic Staff: 500\,GP
Loaf of Bread: 5\,CP
Chicken: 1\,SP-5\,SP

After a bit of research, I concluded that the problem is that the \gold command is not fully expandable. Can anybody help me write a fully expandable variant of the \gold, or else help me to achieve what I want by other means and enlighten my quest to write this package? Also, I would be very greatful for a more compact and elegant way to write the \gold-function, which looks quite bloated to me.

share|improve this question
1  
I'm not sure I understand why you want to have the items and their prices defined in latex, then dumped to a file and then read from the file into a latex table again. Wouldn't it be easier to maintain an external text file with prices and load it into a table straight away? Additionally, what is Chicken: 1\,SP-5\,SP? A range of prices? –  rook Aug 13 '13 at 15:00
    
I personally find it easier to maintain large documents if all information contained in the text at the location where it's mentioned and not spread over multiple files. I say 'multiple' here because item pricing is only one application for this mechanism of data-management, which I was hoping to use for other game data as well. However, I will consider a working database-like solution as a "useful suggestion for alternatives or workarounds" (see bounty message). And yes, Chicken: 1\,SP-5\,SP is a price range, dependent on e.g. the size of the chicken, the location in the game world, etc. –  carsten Aug 13 '13 at 15:22
1  
My preference would be quite the other way round with large files. :) The bigger they are, the better it is to keep data with different responsibility separate. That's a different topic, though. Solution depends on whether you prefer to stick to TeX (which I'm not particularly good at) or just make it work. If it's the latter, I'd consider using the filecontents package to create sort of a database with tab-separated entries. Then read it and process accordingly to your needs with the python package or perltex, which can also be embedded in your single document. –  rook Aug 13 '13 at 16:58
    
I'm happy with any solution that works with pdflatex. I'll give your suggestion a shot next time I have a free afternoon. –  carsten Aug 13 '13 at 17:03

1 Answer 1

up vote 5 down vote accepted
+50

Internally, the command \numdef from etoolbox uses \numexpr which is fully expandable, so you can just use it instead.

I've simplified the code a bit by making auxiliary \iquo and \irem functions which compute the quotient and remainder of integer division. For more clarity I've also separated \gold into a \gold and a \printgold which only prints the amount.

\documentclass{article}

\usepackage{etoolbox}

\makeatletter
\def\makedatalines#1{
  \AtEndDocument{
    \clearpage
    \if@filesw
    \expandafter\newwrite\csname tf@#1.data\endcsname
    \immediate\openout\csname tf@#1.data\endcsname%
    \jobname.#1.data\relax
    \fi
  }
}
\makeatother

\def\adddataline#1#2{\addtocontents{#1.data}{#2}}

\newcommand{\copperpiecesAlias}{CP}
\newcommand{\silverpiecesAlias}{SP}
\newcommand{\goldpiecesAlias}  {GP}

\numgdef{\CopperPerSilver}{10}
\numgdef{\SilverPerGold  }{10}
\newcommand{\setCopperPerSilver}[1]{\numgdef{\CopperPerSilver}{#1}}
\newcommand{\setSilverPerGold}  [1]{\numgdef{\SilverPerGold}{#1}}

\newcommand{\iquo}[2]{% integer quotient of #1 divided by #2
  \romannumeral-`\.% for 2-step expansion
  \ifnum#1=0
    \expandafter 0%
  \else
    \the\numexpr(#1-\numexpr(#2-\ifodd \numexpr#2\relax 1 \else 0\fi)/2\relax)/(#2)\expandafter\relax
  \fi
}
\newcommand{\irem}[2]{% integer remainder of #1 divided by #2
  \the\numexpr #1-#2*\iquo{#1}{#2}\relax}

\newcommand{\gold}[1]{% compute GP/SP/CP
  \printgold{\iquo{#1}{\CopperPerSilver*\SilverPerGold}}% Gold pieces
            {\iquo{\irem{#1}{\CopperPerSilver*\SilverPerGold}}{\CopperPerSilver}}% Silver pieces
            {\irem{\irem{#1}{\CopperPerSilver*\SilverPerGold}}{\CopperPerSilver}}% Copper pieces
  }

\newcommand{\printgold}[3]{% Prints GP/SP/CP
  % #1 = number of gold pieces
  % #2 = number of silver pieces
  % #3 = number of copper pieces
  \ifnum #1 > 0 
    #1\,\goldpiecesAlias
  \fi
  \ifnum \numexpr #1*#2 > 0 
    \ %
  \fi
  \ifnum #2 > 0 
    #2\,\silverpiecesAlias
  \fi
  \ifnum \numexpr #2*#3 > 0 
    \ %
    \else \ifnum \numexpr #1*#3 > 0 
      \ %
    \fi
  \fi
  \ifnum #3 > 0 
    #3\,\copperpiecesAlias
  \fi
}

\makedatalines{prices}

\newcommand\purchasable[2]{\textit{#1} (#2)\adddataline{prices}{#1: #2}}

\begin{document}

\purchasable{Longsword}    {\gold{1000}}

\purchasable{Broadsword}   {\gold{500}}

\purchasable{Magic Staff}  {\gold{50000}}

\purchasable{Loaf of Bread}{\gold{5}}

\purchasable{Chicken}      {\gold{10}-\gold{50}}

\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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