Hello broken keyboard
Hello broken keyboard が終了した。近年まれに見る盛り上がりだった。
C
トップ shinh さん@12.336B。
a;m;t;main(){ a++;a++;a++;m++;t++; n(++t+a+t*a*t*a);i(t);i(a*a);i(a*a);i(a*t*t);n(t*t+a*a);n(m); i(t+a*t*a);i(a*t*t);i(a*(t+a));i(a*a);i(m);n(t); } n(i){ ((int(*)())( ( t*t*(t*t+a*a)*t*t*(t*t+a*a)*(m+a*a*a*t*t)+ // A **(int**)( // B ((t+t*(t+a)*a*t*t)*(t*t+a*a*(t+a*a))+a)* // C ((t+t*a*t*t*t)*(t+t*t*t*a+a*a*a*a)+m)* // D t // E ) // F ) ) )(i+t*t+a*a*a); } i(i){n(t+t*a*(t+a*a)+i);}
少しはみやすくなるように適当に改行を入れた。
まず、ぱっと見、変数 t は mm など二文字変数にすればいいような。それはいいとして、main() 本体は、i() や n() で "Hello, world!" を一文字ずつ出力しているということだな。i(a*a) が "l"(エル) だ。
i() は、n() を呼んでいるが、ここは、単に文字数を減らすための計算だけのよう。
中心は、n() 。ここも、括弧の対が余分なような。まぁ、それはいいとして、問題は A 〜 Fの 6 行だ。関数へのポインターにキャストしてその関数を呼んでいるので、計算しているものは、なんかの関数のアドレスだ。
t=2, a=3, m=1 か。A と C, D, E は単に数値計算だ。B は、int へのポインターへのポインターにキャストして、その int の内容を dereference している。この間接参照はなんなんだ?そして、その int の内容に、A を足したものがなんかの(出力)関数のアドレスなのか。あとで、もう少し詳しく見てみよう。ただ、今の勘だと、関数のアドレスが呼出しごとに変ってしまうので、どこかの固定値からの相対値として参照しているということなんだろうと思う。でも、** をするのはなぜなんだ?直接値を計算するのや、* 一個ではだめなのか?ちょっと不明。
まぁ、でもだいたい構造は分かった。
あぁ、変数 t は、(int(*)()) で使っていたからいいのか。これ、int は省けないのかな?グローバル変数にするとか。だめか、アドレスがリテラルにならないな。
#また後で考えよう。
2位 nai さん@13.486B。
mm;ma;mi;mn;mp;am;aa;n;an;m;a;ap;im;ia; (*pp)();*p; na(){} main(i){ p=na(); mm--;ma=mm-i;mi=ma-i;mn=mi-i;mp=mn-i;am=mp-i;aa=am-i;n=aa-i;an=n-i; p-=am*n; *p--=pp; p--; *p--=m=mp*aa-i; *p--=-aa; *p=na; pp=p=mmap(); p-=aa; *p--=-ma*mp*(a=m*ma-aa)*(a*mn-aa)*(a*(a-mm)-i); im=a-ma; *p--=m*am*an*(m*(-m-aa)-mp-am)*(ap=m*(i-m)-i); *p--=(ia=-am-aa)*(a*a-ma*mi*mi)*(m*(-ap-n)-mp); *p--=im*im*n*n*mn*mi*mp*aa*ia; *p--=m*ia-mm; *p--=-mn; *p--=-n*n*n*n*n*n*n*n*n*an; *p=(((m-a-mn)*a-aa)*ap-a-aa-aa)*m-mi; pp(pp-(am-ia)); }
こちらは難解だな。p=na() は、スタックのアドレスが得られるようだ。そのアドレスからいくつか(am*n)引いたところに、pp を入れている。これは、ゼロで初期化か?そこから、*p-- にいくつか値を入れて言っている。これは、文字列 "Hello, world!" を作っていることなのかな?
と考えていたら、nai さん自身がブログ(id:nai_62:20120913)に書いていた。機械語を生成しているのだとか。
はじめの方にある、(*pp)(); の宣言。関数へのポインターの入れ物だ。ここに、main() の中で値をセットして、呼び出している。これを、先の、shinh さん解に応用すれば、1B 減るんじゃないかな?
awk
他の(私の理解できる)言語では、teebee さん@16.199 がユニークだと思う。
END{ printf"%c%c%c%c%c,%c%c%cr%c%c%c", 1111111E111%111, 11E11%111, 1111E111%1111%111, 1111E111%1111%111, 111, 1111E111%11E11%111, 1E111%1111111111111%1111, 111, 1111E111%1111%111, 1E11%111, 1111E111%111E111%111 }
awk は、入力が無い場合、END{} で処理することになる。この解は、END の E と printf の % と 1 を使って、うまいこと文字コードを作っている。
9/13追記
shinh さん、nai さんの解をベースに "int" の "t" を取り除くことをやってみたが、大失敗。代わりに "=" が入って文字数減らなかった。かっこわる。
しばらく、ゴルフ自粛しよ。
あまりにかっこ悪いので、とりあえず、縮めておいた。これは、この方針でまだまだ縮むな。
9/14追記
kik さんが 1B 縮めた。
(*mm)();...ii(i){(mm+n*n...)(...);}...
あぁ、確かに。mm はゼロだし、関数型は持っているから足し算でいいんだ。かっこいいな。
9/14追記
clock さんの vi@7.227 もおもしろいな。
:h h y :x p:h p y :x p:h hp (以下省略)
":h h" は、":help h" のことで、つまり、"h" コマンドのヘルプを開いているということ。その後、空白と改行でカーソルを動かし、ヘルプテキスト中の "H" の文字を "y" コマンドでコピーして、":x" でヘルプを終了して、"p" でペーストしている、ということだ。つまり、"Hello, world!" の文字をヘルプから一文字ずつコピーしてきているということ。