Most GIFted expressions

もっとも天才的な式

受賞者:Philip Blakely

引用元:https://www.ioccc.org/2012/blakely/blakely.c

審査員・作者による説明:https://www.ioccc.org/2012/blakely/hint.html

動作

逆ポーランド記法で書いた式をコマンドライン引数に与えると、その式を3次元の曲面としてレンダリングした図をアニメGIFとして出力する。

$ gcc -o blakely blakely.c -lm

$ ./blakely "xx*yy*+" 64 > paraboloid.gif
放物面(z=x^2+y^2)を描画するアニメGIF
図:放物面(z=x^2+y^2)を描画するアニメGIF

第2引数は画像のサイズ。次のコマンドは20分くらいかかった。

$ ./blakely xy* 250 > saddle.gif
鞍形(z=x*y)
図:鞍形(z=x*y)

次のコマンドは1時間以上。

$ ./blakely xx*yy*1++d5*ct/ 250 > ripple.gif
波形のような形
図:波形のような形

解説

IOCCCでは比較的おなじみのネタ(逆ポーランド記法、アニメGIF生成、レイトレーシング)を1つにまとめたというネタ。

式の指定で使えるのは、四則演算、指数関数、sin/cos、定数、dup、swap、x座標、y座標。 3つの座標がすべて-1から1に入っている範囲だけが描画される。

コード形状は、非常に荒く横長なドットで、”x” “y” “z”を表現している。 賞名の”GIFted”は、gifted(天賦の才のある)とGIFをかけている。


いくつか作者からの読者への挑戦的な質問がある。

逆ポーランド記法のところを除いて、sin()cos()を使っていない。どうやって視点を回転しているか?

次は、フレームごとに実行されるコードをまともな見た目にしたもの。

      N = S;
      E_ = 1 - J * J / 2;
      T = J * (1 - J * J / 6);
      S = S * E_ + C * T;
      C = C * E_ - N * T;
      A = -(C * C - S * S);
      P = -A * C;
      E = A * S;
      G = S * C * 2;
      I = -G * S;
      F = G * C;

Jは (3.1415 / フレーム数 * 2) という値。 ポイントは最初の5行程度。

このようにして、フレームごとに角度をJずつ変えている。

アニメーションは何回繰り返すか?

フレーム数のことなら、サイズに指定した回数だけ生成するもよう。

解像度の上限が250x250なのはどこから来ているか?

これは無圧縮GIFの出力方法に依存した微妙な話だと思う。 ちゃんとは確認していないのでざっくり説明する。 256色のGIFは初期状態で258要素のテーブル(256個の色コードと、2つの制御コード)を持っていて、1ピクセル出力するごとにこのテーブルは長くなる。 このテーブル長が511未満ならば1ピクセル出力するのに8ビット、つまりちょうど1バイトで表現できるのだが、このしきい値を超えると桁上りして9ビット必要になり、ビットストリームを扱う必要が起きてしまう。 そこで、しきい値を超える前に「クリアコード」という制御コードを出力することで、伸びたテーブルを縮める。 これが無圧縮GIFを出力する基本技法。 おそらくクリアコードを1行表示し終えたら出力するようにしているので、横幅がこれを超えるとビットのずれが生じてしまうのだろう。 詳細はGIFのファイルフォーマットや、その圧縮アルゴリズムのLZWを調べてほしい。

b[0]のポイントはなにか?

これはよくわからない。 b[0]b[1]にはそれぞれ画像サイズ(16ビット)の上位バイトと下位バイトが入っているが、前述の通り250以下なので、b[0]はつねに0となる。 そして、char型の変数Zに対してb[0] = Z >> 9で初期化しているように見えるので、未定義動作になっているような気もする。

ソースの中に隠された”NETSCAPE”と”GIF”の語はどこにあるか?

上記の、cos()sin()を近似計算しているコード。代入されている変数名が順にNETSCAPEGIFになっている。