Hacker News new | past | comments | ask | show | jobs | submit login
ASCII animated donut in obfuscated C (a1k0n.net)
81 points by yogsototh on July 9, 2011 | hide | past | favorite | 32 comments



His entire blog is a pure obfuscated goldmine. Just look at his footer:

    _=0;k;main(){while(_<641){for(k=0;"##K#8(38D-##C]L5870.X7\\M_b;90\\"
    "MC-M/NZGB6Q,I0VGB6a0FbN<VG.6Q\\bNb7^@`X=N@`XQaOVX:^]NX=:Z8PY]B`:>P"
    "NY8^$#SM):XA"[_/6]-35>>_++%6&1;k++);putchar(k[" _/\\\n,`)('<-"]);}}


(also, click on it)


It's really cool. This should be at about +8 to +12 IMO.

(Sorry I hate this sort of thing but without comment scores, yes I'm still riding that horse, no one can see that anyone agrees with you).


Haha. I don't get why this particular one attracts so much attention, as it was my first one and is pretty amateurish compared to this one:

http://a1k0n.net/2006/09/20/obfuscated-c-donut-2.html


Dude, I'm gonna have to decipher that one too??


Well, since you seem to have signed up to HN just for this (and have done an admirable job already)... I'm afraid so.


This one actually has a bug in it - you evaluate _/6 and _++ without an intervening sequence point.


Here's a more readable version:

https://gist.github.com/1073922


I've updated the code to be even more readable, and as an aside, made it more optimal.

sin(A) and sin(B) were being computed repeatedly in the most inner of inner loops, which I pulled out, because A and B don't change in the middle. sin(j) and cos(j) was being computed for every i, even though j wasn't changing. And the intensity was being computed even if the pixel was going to appear beyond the buffer (or wasn't going to appear at all because of a closer pixel in the z buffer).

I also refactored parts of it, and gave names to some constants which you can tweak for fun. DELTA_I can be increased without apparent detriment to image quality. I can't think of good names for the two constants in the middle of the code though: 2 and 5. Are those the minor and major radius of the donut?


Re: repeatedly computing sin(A)/sin(B)/etc: I don't remember why I did that, as it was five years ago, but it was probably intentional just to move code around so it fit within the shape. Or it was an oversight.

IIRC, yes, 5 and 2 are the major and minor radii, meaning 5 is the radius of the ring at the center of the donut (a torus of thickness 0, as it were) and 2 is the thickness -- the radius of the circle extruded around the central ring.


I kind of want someone to write some readable code that displays a spinning 3D representation of said readable code.


A 3D quine? I've considered it. I have a blog post about something completely unrelated to obfuscated C coming up, but maybe after that I'll make an attempt. If nobody beats me to it.


If this is the kind of thing you're into, check out the The International Obfuscated C Code Contest at http://www.ioccc.org

Some of the submissions are absolutely fiendish. Fabrice Bellard's are particularly interesting, just like all his other work.

See if you can work out what westley.c from 1988 does:

    #define _ -F<00||--F-OO--;
    int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
    {
                _-_-_-_
           _-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_-_-_-_-_
      _-_-_-_-_-_-_-_-_-_-_-_-_-_
     _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
     _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
     _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
     _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
      _-_-_-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_-_-_-_-_
            _-_-_-_-_-_-_-_
                _-_-_-_
    }


On my machine, this comes up with 2.50.


On mine it's 0.250


You need to change the #define to

#define _ F-->00||F-OO--;

See http://ioccc.org/1988/westley.hint


I can't compile this. What am I doing wrong? Here is the output:

$ gcc -o donut donut.c -lm

donut.c:1: warning: data definition has no type or storage class

donut.c: In function ‘main’:

donut.c:4: warning: incompatible implicit declaration of built-in function ‘printf’

donut.c:5: warning: incompatible implicit declaration of built-in function ‘memset’


They're just warnings. I got the same ones but it still created a perfectly good executable. (you kind of have to assume warnings with obfuscated code :)


You need to go add:

  #include <stdio.h> 
  #include <string.h>
and then add int directly before the k on the first line of the donut.


No, he does not. All he needs is:

  ./donut
:)


Using your suggestions, and compiler errors, I got this:

http://pastebin.com/Sg5RhUiJ

TBH, this is beautiful.


That did it. Thanks!


I'm totally impressed that

b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];

is allowed in C.


I had no idea that printing something like "\x1b[2J\x1b[H" can clear the screen - with a bit of googling I found out that this is not that unusual a command, but it still took a little reading to figure it out! [1]

Looks like the rest of the code is for generating the donut. Quite a bit of math involved - too much for a sunday morning to look into and attempt to decipher!

[1] http://www.codeguru.com/forum/archive/index.php/t-311110.htm... explains it quite well:

Those are so-called ANSI terminal escape sequences - there is a whole list of them. Hex 1B is the escape character, the characters after that determine the action (clearing the screen, changin text color, moving the cursor etc.) Well, and "[2J" is the sequence for clearing the screen.


<ESC>[H ("home") moves the cursor to the upper-left hand corner, so I initially clear the screen and then use "home" to do the animation.

An alternative method to animate ascii stuff, used in the Yahoo logo, is just to cursor-up 25 lines (or whatever) with <ESC>[25A between frames. That way it doesn't have to clear the screen and you can see your command history, etc.


It's not at all complicated. Most of it is a string literal.

This is an assignment to index o of the array b

    b[0] =
this is the string

     ".,-~:;=!*#$@"
and this is indexing into the string literal with N, or 0 if N is less than 0.

     [N > 0 ? N : 0]


Wow. Just wow. Compile this. Run this. Just wow.


that was cool for a saturday afternoon. extra points for creative packaging.


Very cool - I've never played with sending special terminal characters before to clear the screen and the like; like others, I will definitely add this to my repertoire!

My suggestion for deciphering it - let's not forget about `indent`!

Both iterations of the donut.c program: https://gist.github.com/1074316


How does one go about making stuff like this?


I imagine it requires an absolute mastery of, but also a certain profound contempt for the C language.

If preprocessors were people, the organisers of the IOCCC would be up before the hague.


It's really not that hard, once you have some idea of the kind of program you're writing. Write it out normally, and start eliminating everything that can be eliminated (in particular, whitespace, braces, if statements that can be conditionals, subroutines, variables, etc). Takes some ingenuity with loops but it's mostly straightforward.

In this particular example the only really odd thing about it is the lighting calculation, which is an algebraically simplified dot product of the light direction (0, 1, -1) IIRC with the surface normal of a torus (gradient of the equation defining the shape). The derivation of that is long gone but it wasn't hard math.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: