http://rtpg.co/2023/03/02/quines-for-dummies.html
Raphael
About Feed
Writing The World's Most Boring Quine
2023-03-02 * Raphael
Quines are programs that, when you run, will print out its own source
text.
You can read this very long article about how to write one in C. Here
we're going to do it in Python and skip a lot of the details and
theory.
You can, of course, "just" read the data from disk:
import sys
print(open(sys.argv[0], 'r').read())
This is obviously cheating though. It only works if we call it the
right way, and is dependent on the command line to work. Really we
want something pure that can be done in most programming languages.
Something solid.
Firstly... we want a program that prints out a result. So we'll need
at least one print statement.
print()
This prints out one empty line, but establishes a first thing: we
would like to print out print!
print("print()")
So this prints out print(). But now we're going from two prints down
to one.
Right now we have a bit of a problem. If we add a character to the
string we print, then our output gets 1 character longer, but so does
our target.
We need our output to be the same as the code. So we need the length
of our output to catch up to the length of the code.
It's, of course, very easy to make our output much longer than the
length of the source code by doing things like:
for \_ in range(100):
print("print()")
This will make our output much longer than the source code, though
also not very close to the source code.
We want to compress our output data so that we have some chance to
catch up.
We have at least two prints in the existing code, so let's start
there.
p = "print"
print("%s()" % p)
(some_str % parameters is Python's equivalent to printf, where the
string parameters get placed at %s)
This feels like we are going in the wrong direction! Not only have we
added more characters, but now there's not even two print statements,
so our compression idea is failing!
But we're actually on the right track, because we have decoupled our
output length from the code length, at least a bit.
Let's first try to fix our print statement to at least print this new
first statement.
As a note, "->%r" % "abc"gives "->'abc'", we are rendering Python
values with repr instead of str.
p = "print"
print("p = %r\n%s()" % (p, p))
The above percentage soup gives us, in output:
p = 'print'
print()
Now we seem to still have a problem with this print statement. But
where as before we were having trouble reproducing the outermost
print, now our problem is about generating the innards.
If we try to write the innards of the print statement, we're going to
run into an endless recursion
print("p = %r\n%s(?)" % (p, p))
print("p = %r\n%s('p = %%r\n%%s(?)')" % (p, p))
Tryin to fill in the ? with itself is getting us nowhere fast.
But notice how we don't have to write the print statement within the
print statement anymore. Sure, this was a dead end, but the idea is
sound. How can we avoid mentioning the template again?
Let's factor out this template string into a value called t.
p = "print"
t = "p = %r\n%s()"
print(t % (p, p))
From here, again, we want to print out this new assignment by adding
t = %r
p = "print"
t = "p = %r\nt = %r\n%s()"
print(t % (p, t, p))
This gets us to the following output:
p = 'print'
t = 'p = %r\nt = %r\n%s()'
print()
We still do not have the innards of the print call!
The important thing that just happened though is that our attempts to
"catch up" to the end of the code has gotten us to a point where we
are no longer needing to infinitely expand our template.
We can add new text to the value of t, and that will be nicely
reproduced in our end result because we are doing t % (p, t, p), i.e.
injecting the template into the end result.
By typing just one character into t, we can extend our output by 2
characters!
p = "print"
t = "p = %r\nt = %r\n%s(t)"
print(t % (p, t, p))
This program outputs
p = 'print'
t = 'p = %r\nt = %r\n%s(t)'
print(t)
We have finally caught our own tail, and now are simply starting to
type the innards of the (now no longer infinitely recursive) print
statement.
Let's finish typing things up, and fix the quotes to match what repr
does to our strings
p = 'print'
t = 'p = %r\nt = %r\n%s(t %% (p, t, p))'
print(t % (p, t, p))
The above is a quine. You run it, and the output is the same as the
input. We meandered a bit but finally got there.
Here p isn't really pulling its weight, let's clean that up.
t = 't = %r\nprint(t %% t)'
print(t % t)
This is, honestly, a bit of an unexciting quine. At this point
modifying the source simply involves adding the source, and updating
the template.
There are no more real mysteries or challenges to reproducing the
exact code itself. The magic is gone, but in its place the minimal
quine can help serve as a piece of a larger puzzle.
You could make your quine change over time. The simplest example of
this would be with a run count:
run_count = 0
t = 'run_count = %s\nt = %r\nprint(t %% (run_count + 1, t))'
print(t % (run_count + 1, t))
But seeing the bones of the quine too easily removes some of the
magic. The point of quines is that they are unintelligable, abusing
some weird mechanism.
Instead of using this code directly, you can take this core idea of
injecting a template into itself, to think of ways of chaining
together interesting behavior. Obfuscate in interesting ways, with
weird layout. But mostly try to have fun.
Previously:
* Ideas For A Javascript Stricter Mode
* Augmenting Existing Struct APIs with Rust Traits
* A Couple Lines To Improve Print-Based Debugging In Loops
* rtpg
* rtpg_