This is my first of two winning entries in 2024 IOCCC (28th). I spent
many hours on this, in fact every day for two whole months to the day I spent many
hours. I was exhausted by the end of it. It was very well worth it and I
had a lot of fun. The judges, I know, did too, as they said that
exactly.
The judges did something really cool this time - a video showing all
the entries. Although I think YouTube ads ruin things I'll link to it
anyway as I know Landon will appreciate it. One could always download it
if they wished to not be interrupted:
IOCCC Prize in diabolical logistics - A Pact With the Devil On the Oregon Trail.
A few comments of interest. With the submission code that has
134 149 gotos I played it once from start to finish and the lines changed a
total of 985 1416 (!!) times, counting function calls in the
program (that is, prog.c functions, not libc functions); when only considering the line
changes in main() it was still a huge total of 776 1131 (!!)
times! This really IS diabolical.
With blank lines, lines with just a brace and lines without code
removed, there are only 59 58 (!!) lines. And yet somehow it changed lines
well over 1000 times!
Flex and bison do not come close to this. I have never seen anything as
sinister as this. The award title is very fitting, even without the
theme of the game.
Something that made me proud, somehow, is when Landon said on the
video that playing it is almost as diabolical as trying to trace the
code.
Another fun thing: it uses a SINGLE FLATchar * as a
char **. Yes. You read that right! There is no char **;
there is just a flat char *that is used like one. I wrote a program which creates a data file with
all the messages and this data file (three versions depending on which
one you play) has a blocksize which allows for easy accessing of
strings. Each string has an embedded NUL byte and the function
getdelim() is used with the delimiter EOF. This allows the entire
char * to be read in, NUL bytes, padding and valid text
combined. This means that when I index a string (based on the blocksize)
it will print the string but it will not print the padding because the
NUL byte will stop it. This saves a huge amount of bytes. There would be
no way this could have existed as an IOCCC entry if it was in the code.
Even if it was not too large (but the strings are way too large) it
would remove the tricks that I used so it would not be worth it.
The data file has emojis. In the version that did not win, the
encrypted version, the emojis were also encrypted - and not decrypted
yet somehow they were still printed fine, as was all the text. I was
disappointed that version did not win as the key was used to obtain the
key by encrypting a character (known to be at a specific place in the
file - with beautiful numerology) and then based on that location and
pointer arithmetic, calculate the key.
Nonetheless, I had at first little doubt this one would win. Only
when they had a record number of finalists did I start to get worried.
But the judges also said something that sounded very much like this:
Landon said that some of the finalists need special compilation steps
and that is something mine does need, as it builds the data files (and
other things but the important part is the data files). I knew then that
it HAD to be a finalist. But there is always that lingering fear. Even
so it seemed incomprehensible that this would not win.
Something else worth noting is that the entirety of main() is inside
a single switch(1) (!). Every single part of it. Yes you can put labels that
do not belong to the switch value in a switch. I abused this heavily
but I abused labels heavily outside of switch too, as I get to next.
Anyway: the C standard says, that for goto, the label must be in the
same function (in fact if it's not it should be a compiler error), and
more importantly, if you want to avoid UB (undefined behaviour), you
MUST NOT skip initialisation!
As everything is global and as when I need certain
values before a jump are initialised (see below on this) there are no
problems. As you can see, if you have a label in a switch before a case
or default, that label is skipped! It is only reached if one does a goto to it.
The way I designed this program is truly horrible. Everything you
know about C flow control no longer applies. It is so diabolical that
two for() loops actually have the code of the OTHER for
loop! What's more, they actually act on the same array with the same
iterator but in a different way! There is so much more like that
though...blocks of code are no longer what you believed. There is code
that is dead but also isn't dead. There is an if() that jumps through
several labels only to jump right back to after the initial jump - doing
absolutely nothing other than leading you on a wild goose chase!
One switch statement (an inner switch - the events) has a polymorphic
case (in fact it has several forms)! A case in the events code actually jumps to part of another case and then exits in
the correct path, but it can take multiple different events first. The jumping is ludicrous. You have to look at the code
to see it. Try running it through a debugger - if you dare!
I had to do EXTENSIVE testing because of all the
gotos. One false jump and everything would come crashing down. In fact
earlier on I discovered a mistake I made when extra tired. It caused the
entire game loop to go through without any prompting. In other words the
game was over in a few seconds after it got to that specific event!
Thankfully I found it quickly. If I had not, because of all the gotos, I
would have had to revert much more: events as well as the obfuscation,
for a fun thing about obfuscation is once it's obfuscated you can fit in
more bytes - it just is how it works.
The real joy though comes from the many jokes, the story, with dark
and rude jokes included (rude in the British English definition), even
cannibalism in hilarious ways. The code took a long time to create but
the data file took just as much time if not more time. The title of the
award is truly fitting in so many ways, not just the game itself but the
tracing of the code.
But the winning entries this year - as everyone else (not me as I
explain elsewhere) had almost five years to work on submissions, where I
only had a few months - were so amazing that it makes it even more
amazing that it won - and that holds for all the winners, not just this
one or my other one.