IOCCCの名作を読み解いてみた

IOCCC、すごいよねえ。
公式サイトに行けば、昔からの入賞作品を見ることができる。

名作揃いだと思うのだが、個人的に特に好きなのが、1992年のBrian Westleyさん(aka Merlyn LeRoyさん)の作品。(ソースコード

           main(l
      ,a,n,d)char**a;{
  for(d=atoi(a[1])/10*80-
 atoi(a[2])/5-596;n="@NKA\
CLCCGZAAQBEAADAFaISADJABBA^\
SNLGAQABDAXIMBAACTBATAHDBAN\
ZcEMMCCCCAAhEIJFAEAAABAfHJE\
TBdFLDAANEfDNBPHdBcBBBEA_AL\
 H E L L O,    W O R L D! "
   [l++-3];)for(;n-->64;)
      putchar(!d+++33^
           l&1);}

古い仕様のCで書かれているが、GCC 4.9.3で正常にコンパイルできることは確認している。

cc westley.c

正常にコンパイルできたら動かしてみよう。例えばこんな感じで。
(動かす際には横幅80文字の端末を使う必要があるので注意)

a.out 35 135

出力はこんな感じ↓

しばらく出力を眺めて、何が書かれているのかを理解してから、もう一度ソースコードを見返した時に衝撃を受けた。
すごいよね~

このソースコードを見やすいものに書き換えて理解してみる。

まずは普通にインデントしてみる。

main(l,a,n,d)
char**a;
{
	for(d=atoi(a[1])/10*80-atoi(a[2])/5-596;
		n="@NKA"
			"CLCCGZAAQBEAADAFaISADJABBA^"
			"SNLGAQABDAXIMBAACTBATAHDBAN"
			"ZcEMMCCCCAAhEIJFAEAAABAfHJE"
			"TBdFLDAANEfDNBPHdBcBBBEA_AL"
			" H E L L O,    W O R L D! "[l++-3];)
		for(;n-->64;)
			putchar(!d+++33^l&1);
}

これだけでも見通しが良くなるんだ。
さらにもうちょっと見やすくしてみる。

main(int argc,char** argv) {
	int n,d;

	for(d=atoi(argv[1])/10*80-atoi(argv[2])/5-596;
		n="@NKA"
			"CLCCGZAAQBEAADAFaISADJABBA^"
			"SNLGAQABDAXIMBAACTBATAHDBAN"
			"ZcEMMCCCCAAhEIJFAEAAABAfHJE"
			"TBdFLDAANEfDNBPHdBcBBBEA_AL"
			" H E L L O,    W O R L D! "[argc++-3];)
		for(;n-->64;)
			putchar(!d+++33^argc&1);
}

一番トリッキーなのは

putchar(!d+++33^argc&1);

だと思う。括弧を使って見やすくしてみる。

putchar(((!(d++)) + 33) ^ (argc&1));

面白いコードだねえ。
!(d++)の!は論理式の否定。dが0の場合には!(d++)は1、dが0以外の場合には!(d++)は0。
argc&1はargcが偶数なら0、奇数なら1。
^は排他的論理和なので、((!(d++)) + 33)の最下位ビットを反転したりしなかったりすることになる。
へえ。
ASCIIコードで32はスペース、33は!、34は"、35は#。
つまりわかりやすく書けば、

if (argc % 2 == 0) {
	putchar(d++ == 0 ? '"' : '!');
}
else {
	putchar(d++ == 0 ? '#' : ' ');
}

となる。
更にわかりやすくなるように書き換えていって、結局はこんな感じに書いてみた。

#include <stdlib.h>
#include <stdio.h>

int main(int argc,char** argv) {
	int n,d,l = 0;

	char* str = "@NKA"
			"CLCCGZAAQBEAADAFaISADJABBA^"
			"SNLGAQABDAXIMBAACTBATAHDBAN"
			"ZcEMMCCCCAAhEIJFAEAAABAfHJE"
			"TBdFLDAANEfDNBPHdBcBBBEA_AL"
			" H E L L O,    W O R L D! ";

	for (d=atoi(argv[1])/10*80 - atoi(argv[2])/5 - 596; (n=str[l]); l++) {
		for (;n>64;n--) {
			if (l % 2 == 0) {
				putchar(d == 0 ? '"' : '!');
			}
			else {
				putchar(d == 0 ? '#' : ' ');
			}

			d++;
		}
	}

	return 0;
}

@NKA…で始まるデータはもちろん地図情報。@(ASCIIで64)が0、A(ASCIIで65)が1、B(ASCIIで66)が2、…という意味がある。
それぞれの数字には、陸地あるいは海のデータが横に何個連続しているのか、という意味がある。
奇数番目は陸地のデータ、偶数番目は海のデータ。
つまり、1文字目(@)は最初に陸地のデータが何個連続しているのか(この場合はゼロ。つまり左上は海から始まることになる)、2文字目(N)はその次に海のデータが何個連続しているか、3文字目(K)がまた陸地で、…というデータになっている。

興味深いのは最後の" H E L L O, W O R L D! "の部分。スペース(ASCIIで32)は@(ASCIIで64)よりも小さいので、陸地はなし。次のH(ASCIIで72)は海が8個、次が陸地はなしで、また海があって、…ということで結局海が続いていることになる。

海を書いているのか、あるいは陸地を書いているのか、のフラグの変数がl(あるいはargc)。

また、1文字書くごとにdの値が増えていって、0になった時だけ普通の海(スペース)の代わりに「#」、陸地「!」の代わりに「"」が出力される。
マジックナンバーの596は北緯0度・東経0度の場所を意味している。つまり596番目の文字が基準点となっている。

ふーむ。ほんとによく書いてあるねえ。すごいねえ。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください