A Hint (My Solution is Below)
One of the key components of my solution (and one that others have asked about) is a macro that, given a single digit, produces that many stars. There are several approaches to this. One of them is to use one macro to produce another macro, and use the data given to the first macro as part of the template for the second. Here's what I mean:
\def\s#1{
\def\*##1#1##2##3.{##2}
\* 0{} 1{*} 2{**} 3{***} 4{****} 5{*****} 6{******}
7{*******} 8{********} 9{*********}.
}
With this definition, \s3
would generate ***
, while \s0
would produce no stars:
$$\require{begingroup}\begingroup
\def\s#1{
\def\*##1#1##2##3.{##2}
\* 0{} 1{*} 2{**} 3{***} 4{****} 5{*****} 6{******}
7{*******} 8{********} 9{*********}.
}
\begin{array}{ll}
\texttt{\s3}\quad&\s3\\
\texttt{\s7}\quad&\s7\\
\texttt{\s0}\quad&\s0
\end{array}
\endgroup$$
The key is to use the #1
passed into \s
when creating the macro \*
internally. The result of \s3
is to define \*#13#2#3.{#2}
, which read everything up to the first 3
(and throws it away), then reads the next argument (the collection of stars) and finally reads everything up to the .
(and throws it away). It returns the #2
, which is the proper number of stars. After defining \*
, we call it with the proper data for each digit, and \*
chooses the right one based on the value of the #1
passed to \s
; the others are discarded by \*
.
There are other ways to approach this as well that don't rely on listing stars explicitly, but generate them recursively. One thing to think about is that in the string 0123456789
, the number of characters before the digit $n$ is the number of stars you would need for that $n$.
I hope these give you some ideas for new solutions to the puzzle.
A New Solution
This is a solution that has 71 tokens, 4 static definitions, and 1 dynamic one. I have left my previous solution in the page below, since it includes an interesting use of %
as both a delimiter and a loop terminator that is not present in this solution.
$$\require{begingroup}\begingroup
\def\stars#1#2
{\D#1 \S %
\texttt {#2}}
\def\D#1 #2{#2#1}
\def\S#1#2 #3 #4{
\def\N##1#1{}
\N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\stars{#2}#4#4#4#4#4#4#4#4#4#4
}
\def\*#1#2
{#2*
}
\stars{57}
\endgroup$$
See my other solution below for a description of Horner's method, and how it applies to this problem. The new code is given below:
$$\require{begingroup}\begingroup
\def\stars#1#2
{\D#1 \S %
\texttt {#2}}
\def\D#1 #2{#2#1}
\def\S#1#2 #3 #4{
\def\N##1#1{}
\N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\stars{#2}#4#4#4#4#4#4#4#4#4#4
}
\def\*#1#2
{#2*
}
\stars{57}
\endgroup$$
The \stars
macro will be used recursively to perform Horner's method. The stars themselves will be stored directly after it, and the idea is to have \stars{ab}
duplicate the stars that follow it 10 times, then produce a
more stars, and then call \stars{b}
followed by the larger number of stars. The \stars
macro sets up a test to see if there are more digits to be processed, and arranges for the stars to be output via \texttt
if not.
This uses \D
to decide if there are more stars to be created; i.e., it detects \stars{}
where the parameter is blank using the technique that we have seen for controlling loops before. It either calls \S
if there are more digits to process, or moves the %
to the front (commenting out the \S
that will be in #1
when there are no more digits) and letting the \texttt
call end the loop and print the stars.
The \S
macro reads the next digit as #1
and any additional digits as #2
; it reads (and discards) the %
, newline, and \texttt
as #3
, and then gets the current set of stars as #4
. It then defines \N
to look for the digit in the list of digits that will follow, and throw away the higher ones; at the same time, it sets up the recursive call to \stars
with the remaining digits, and duplicates the original stars 10 times following it. The \N
call then throws away the data for the higher digits, and leaves the \*
call for the proper digit. E.g., if the digit was a 4, then \N
will remove the 9\*8\*7\*6\*5\*4
and leave \*3\*2\*1\*0
left to be processed.
The \*
macro reads (and discards) the digit that follows it, and (reading past \stars{nn}
and any existing stars) puts a star at the end of the line. Each \*
does the same, meaning that 4 new stars will be added at the end of the line, in addition to the 10 copies of the stars that had been there previously.
After the last \*
call, we have \start{nn}
followed by the updated list of stars, and the process repeats. The \D
call decides if more stars need to be added, and if there are no more digits, prints the result.
Here is a live trace version that produces 24 stars:
$$\require{begingroup}\begingroup
\let\TT=\texttt\def\texttt#1{\\&&\color{green}{\TT{#1}}}
\def\-#1#2{\scriptsize\TT{#1}& \longrightarrow&\break{#2}\\}
\def\>#1#2 #3%@{\-{#1 #2}{#3}#3%@}
\def\<#1#2 #3
#4
#5%@{\-{#1 #2}{\def\N##1#3{}
#5}#4
#5%@}
\def\break#1{\def\&{}\line.#1\endbreak\nextline
\endbreak\endline}
\def\line#1
#2\endbreak#3{#3#1
#2\endbreak#3}
\def\nextline.#1
{\&\def\&{&&}\scriptsize\TT{#1}\\\line.}
\def\endline.#1\endbreak#2\endline{\&\scriptsize\TT{#1}}
\def\stars#1#2
{\>\stars[#1][#2] \D#1 \S %
\texttt {#2}}
\def\D#1 #2{\>\D[#1][#2] #2#1}
\def\S#1#2 #3 #4{\<\S[#1][#2][#3][#4] #1
\def\N##1#1{\>\N[##1] }
\N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\stars{#2}#4#4#4#4#4#4#4#4#4#4
}
\def\*#1#2
{\>\*[#1][#2] #2*
}
\begin{array}{lcl}
&&\color{green}{\TT{\stars{24}}}\\
\stars{24}
%@
\end{array}
\endgroup$$
The initial \stars{24}
call is at the top in green. The macros and their parameters are shown on the left, followed by an arrow, and the results of the macro expansion on the right (the complete input string is shown, with line breaks where appropriate). The final 24 stars are shown in green at the bottom.
Solution Influenced by humn's Results
This is a solution that has $\require{cancel}\cancel{99}$ $\cancel{82}$ 80 tokens, $\cancel{6}$ 5 static definitions, and 1 dynamic one.
$$\require{begingroup}\begingroup
\def\stars#1{\D%#1 \T \texttt}
\def\D#1%#2#3 #4{#4{#1}%#2#3 #4}
\def\T#1%#2{\S#2#1#1#1#1#1#1#1#1#1#1%}
\def\S#1{\def\N##1#1{} \N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\D
}
\def\*#1#2
{#2
*}
\stars{57}
\endgroup$$
The idea behind this is that a number in base 10 can be considered as a polynomial evaluated at $x=10$. For example, the number $15{,}392$ can be written
$$\begin{align}
15{,}392 &= 1\cdot 10{,}000 +5\cdot 1{,}000 + 3\cdot 100 + 9\cdot 10 + 2\\
&= 1\cdot 10^4 + 5\cdot 10^3 + 3\cdot 10^2 + 9\cdot 10 + 2\\
&= 1x^4 + 5x^3 + 3x^2 + 9x + 2\qquad\text{where $x=10$.}
\end{align}$$
In general, the digits of the number are the coefficients of the terms in the polynomial. There is a computationally efficient means of evaluating a polynomial called Horner's Method, which rewrites the polynomial by factoring out $x$'s, as in:
$$\begin{align}
x^4 + 5x^3 + 3x^2 + 9x + 2 &= x\cdot(x^3 + 5x^2 + 3x + 9) + 2\\
&= x\cdot(x\cdot(x^2 + 5x + 3) + 9) + 2\\
&= x\cdot(x\cdot(x\cdot(x\cdot 1+5) + 3) + 9) + 2
\end{align}$$
so
$$
15{,}392 = 10\cdot(10\cdot(10\cdot(10\cdot 1 + 5) + 3) + 9) + 2.
$$
This says to take the first digit, 1, multiply that by 10 and add the next digit, 5. Then multiply the result by 10 and add the next digit, 3. Then multiply that result by 10 and add the next digit, 9, and finally multiply that by 10 and add the final digit, 2. This is a recursive process the involves only two things: multiplying by 10 and adding a single digit.
How does this relate to producing stars? Well, if we have a way to produce 0 through 9 stars based on the digit, and a way of duplicating the current number of stars 10 times, then we can use the recursive process to generate the proper number of stars. That is what my code does:
$$\require{begingroup}\begingroup
\def\stars#1{\D%#1 \T \texttt}
\def\D#1%#2#3 #4{#4{#1}%#2#3 #4}
\def\T#1%#2{\S#2#1#1#1#1#1#1#1#1#1#1%}
\def\S#1{\def\N##1#1{} \N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\D
}
\def\*#1#2
{#2
*}
\stars{57}
\endgroup$$
This sets up a loop like the ones we've seen before via the macro \D
. The stars are stored following the \D
up to the %
(it starts out empty, since we haven't created any stars yet). The desired number of stars is stored to the right of the %
up to the first space. Then we have the two commands \T
and \texttt
, the first being performed when there are still digits to process, and the second when we run out of digits. The \D
macro reads the stars as #1
, then the next digit as #2
and the remaining digits as #3
, and finally the command to perform as #4
. (When there are no more digits, #2
will read the \T
, the #3
will be empty, and #4
will be the \texttt
.) The \D
macro simply puts everything back in place where it was (making sure the stars have braces around them), but copies the #4
to the beginning of the line, so that it will act on the stars and digits. That is, the \D
is simply there to choose between \T
and \texttt
.
The \T
command reads the stars and the first digit of the number (and we are guaranteed there is one at this point); its job is to perform the "multiply by 10 and add the next digit" action. Since the current number of stars is in #1
, we do the "multiply by 10" by including 10 copies of #1
in the replacement text for \T
. We also insert \S
followed by the digit pulled from the number; we need to insert that many stars (to "add the digit"), and that is what \S
does.
The \S
macro uses a dynamic macro (\N
) that uses the digit as a delimiter (as discussed in the hint). We could simply have used the \*
macro from the hint, but because of all the stars and braces, that would add a lot of characters to the length of our solution. Instead, we use a macro based on humn's digit cascade approach. The \N
macro is passed the data 9\*8\*7\*6\*5\*4\*3\*2\*1\*0\D
, and it discards everything up to the digit that it was passed. For example, if we had \S4
, then \N
eats everything up to (and including) the 4, leaving \*3\*2\*1\*0\D
.
The \*
macro is the next thing to be executed. It reads the digit that follows (and throws it away), then everything up to the \D
; it puts that back, along with the \D
and adds a star after the \D
. The effect is to leave us with \*2\*1\*0\D*
. This leaves \*
as the next thing to execute, and again it removes a digit and puts a star after the \D
leaving \*1\*0\D**
. It runs two more times, producing first \*0\D***
and then \D****
. So the result is to add 4 more stars after the \D
and leave \D
as the next thing to perform. That is, \S4
adds 4 stars to the ones already following the \D
(the ten copies of what was there before) and then sets up \D
as the next thing to execute.
That puts us back to the starting point again, but with the proper number of stars between \D
and %
, and one fewer digit following the %
. The \D
macro either terminates with \textt
if there are no more digits, or calls \T
again, which duplicates the current stars 10 times and adds in the number given by the next digit. That is, \D
and \T
together perform Horner's method on the polynomial with the digits as coefficients, evaluated at $x=10$.
When all the digits are processed, the \textt
command is moved to the front. Since \D
guarantees that the stars have braces around them, this prints the final number of stars in the monospaces \tt
font. Because we used %
as the delimiter between the stars and the digits, when \texttt{...}
is complete, the %
then acts as a comment character, causing the remainder of the line to be ignored. So the loop terminates with no additional output.
Here is the live trace for \stars{24}
:
$$\require{begingroup}\begingroup
\def\X#1.{\texttt{#1}}
\let\TT=\texttt\def\texttt#1{\\\color{green}{\TT{#1}}}
\def\=#1#2{\quad\small\TT{#1} \longrightarrow\\\scriptsize\color{red}{\TT{#2}}}
\def\>#1#2 #3.{\={#1 #2}{#3}\\#3.}
\def\stars#1{\>\stars[#1] \D%#1 \T \texttt.}
\def\D#1%#2#3 #4{\>\D[#1][#2][#3][#4] #4{#1}%#2#3 #4}
\def\T#1%#2{\>\T[#1][#2] \S#2#1#1#1#1#1#1#1#1#1#1%}
\def\S#1{\>\S[#1] \def\N##1#1{\>\N[##1] } \N9\*8\*7\*6\*5\*4\*3\*2\*1\*0\D
}
\def\*#1#2
{\>\*[#1][#2] #2
*}
\begin{array}{l}
\scriptsize\color{red}{\TT{\stars{24}}}\\
\stars{24}
\end{array}
\endgroup$$
Here, the red lines represent the current $\rm\TeX$ string to be processed, and the black lines give the macros and their parameters. The final output is in green at the bottom. There is an artifact of the self-trace code in the lines that show the definition of \N
, since the definition includes the tracing code (the \>\N[##1]
, which should really not be shown), but I couldn't find a decent way to avoid it. Also, the line breaks show as spaces in the output (due to the fact that line breaks in web pages are treated as spaces).
Theoretically, the program could create an arbitrary number of stars, but the limitations imposed by MathJax on the number of macros calls allowed (in order to avoid run-away recursive macros) means we can produce 5049 stars, but not 5050. The approach could be adapted to other bases, but I haven't calculated the highest numbers possible for those.