TRICK 2015というのは、意味不明なキモいコードを書くプログラミングコンテストです。やったぜ。
審査員の方々、このようなわけのわからない代物を解読していただき本当にありがとうございました。
栄えなき「Doubling amphisbaena award」を受賞したRubyプログラムが、こちら。
;; ;; ;; ;;
;; ;; ;; ;;
;;eval$s =%q[i=1#
eval(%q[ xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xx xxxxxxxxxx xx xxxxxxxx
j, t, p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)# xx xx
xxxxxxxx xx xxxxxxxxxx xx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx x].gsub\
/x.*|\s/ ,"")#];;
;; ;; ;; ;;
;; ;; ;; ;;
これを実行するとこのような出力を吐き出します。
;; ;; ;; ;;
;; ;; ;; ;;
;;eval$s =%q[i=1#
eval(%q[ xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xx xxxxxxxxxx xx xxxxxxxx
j, t, p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)# xx xx
xxxxxxxx xx xxxxxxxxxx xx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx x].gsub\
/x.*|\s/ ,"")#];;
;; ;; ;; ;;
;; ;; ;; ;;
少々図形が絡み合って見づらいですが、もともとのプログラムを、位置を変えて二回書き出したものになっています。
実はこれもプログラムです。
これを実行すると今度はこうなります。
;; ;; ;; ;;
;; ;; ;; ;;
;;eval$s =%q[i=1#
eval(%q[ xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xx xxxxxxxxxx xx xxxxxxxx
j, t, p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
;; ;; |b}}unti;;l$;;f;puts(t)# xx xx
;; ;; xxxxxxxx;;xx;;xxxxxxxxxx xx xx
;;eval$sxxxxxxxx=%q[i=1#xxxxxxxx
eval(%q[xxxxxxxxxxxxxxxxxxxxxxxx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx
xxxxxxxxxxxxxxxxxxxxxxxxx].gsub\
xxxxxxxx/x.*|\s/xxxxxxxx,"")#];;
xx xx xxxxxxxxxx;;xx;;xxxxxxxx ;; ;;
j, t, p=0,[?;],";;ev;;al$s=%qx ;; ;;
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)# xx xx
xxxxxxxx xx xxxxxxxxxx xx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx x].gsub\
/x.*|\s/ ,"")#];;
;; ;; ;; ;;
;; ;; ;; ;;
また倍に増えました。
さらに実行するとこう。
;; ;; ;; ;;
;; ;; ;; ;;
;;eval$s =%q[i=1#
eval(%q[ xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xx xxxxxxxxxx xx xxxxxxxx
j, t, p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
;; ;; ;; ;; ;; ;; |b}}unti;;l$;;f;puts(t)# xx xx
;; ;; ;; ;; ;; ;; xxxxxxxx;;xx;;xxxxxxxxxx xx xx
;;eval$s =%q[i=1# ;;eval$sxxxxxxxx=%q[i=1#xxxxxxxx
eval(%q[ xxxxxxxx eval(%q[xxxxxxxxxxxxxxxxxxxxxxxx
xx xxxx xx xx xxxx xx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx
xx xxxx xx xx xxxx xx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx
xxxxxxxx xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx].gsub\
xxxxxxxx xxxxxxxx xxxxxxxx/x.*|\s/xxxxxxxx,"")#];;
xx xx xxxxxxxxxx xx xxxxxxxxxx xx xxxxxxxxxx;;xx;;xxxxxxxx ;; ;;
j, t, p=0,[?;]," ev al$s=%qxj, t, p=0,[?;],";;ev;;al$s=%qx ;; ;;
[#$s]".split*"";i,j,t=i-j,i+j,(x[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}ifft$x||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);rf|,x|=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljxs=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|xust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx|b}}unti;;l$;;f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xxxxxxxxxx;;xx;;xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$sxxxxxxxx=%q[i=1#xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[xxxxxxxxxxxxxxxxxxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxxxxxxxxxxxxxxxxxxx].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx/x.*|\s/xxxxxxxx,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx;;xx;;xxxxxxxxxx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;],";;ev;;al$s=%qxj,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}ifft$x||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);rf|,x|=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljxs=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|xust(r.size).gsub(b){r[$`.size]|x
;; ;; |b}}unti;;l$;;f;puts(t)# xx xx|b}}unti l$ f;puts(t)# xx xx
;; ;; xxxxxxxx;;xx;;xxxxxxxxxx xx xxxxxxxxxx xx xxxxxxxxxx xx xx
;;eval$sxxxxxxxx=%q[i=1#xxxxxxxx xxxxxxxx xxxxxxxx
eval(%q[xxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxxx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xx xxxx xx xx xxxx xx
xx xxxxxxxxxxxxxxxxxxxxxxxxxxxx xx xx xxxx xx xx xxxx xx
xxxxxxxxxxxxxxxxxxxxxxxxx].gsub\ xxxxxxxx x].gsub\
xxxxxxxx/x.*|\s/xxxxxxxx,"")#];; /x.*|\s/ ,"")#];;
xx xx xxxxxxxxxx;;xx;;xxxxxxxx ;; ;; ;; ;; ;; ;;
j, t, p=0,[?;],";;ev;;al$s=%qx ;; ;; ;; ;; ;; ;;
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)#;;xx;;xx ;; ;;
xxxxxxxx xx xxxxxxxxxx;;xx;;xx ;; ;;
xxxxxxxx xxxxxxxx;;eval$s =%q[i=1#
xxxxxxxx xxxxxxxxeval(%q[ xxxxxxxx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xx xxxx xx xx xxxxxxxxxxxx xx xx xxxx xx
xxxxxxxx x].gsub\xxxxxxxx xxxxxxxx
/x.*|\s/ ,"")#];;xxxxxxxx xxxxxxxx
;; ;; xx;;xx;;xxxxxxxxxx xx xxxxxxxx
;; ;; j,;;t,;;p=0,[?;]," ev al$s=%qx
[#$s]".split*"";i,j,t=i-j,i+j,(x
[b=?\s]*j.abs+t).map{|s|r=t.shix
ft ||b;r.gsub!(?;){p.slice!0}if $x
f| |=p>p=p.center(i*i+j*j,?;);r ,x
s=[s,r]if(i*j<0);(b*i.abs+s).ljx
ust(r.size).gsub(b){r[$`.size]|x
|b}}unti l$ f;puts(t)# xx xx
xxxxxxxx xx xxxxxxxxxx xx xx
xxxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xx xxxx xx xx xxxx xx
xx xxxx xx xx xxxx xx
xxxxxxxx x].gsub\
/x.*|\s/ ,"")#];;
;; ;; ;; ;;
;; ;; ;; ;;
際限なく倍々に増えていきます。
というわけで、コンセプトは自分自身を複製しながらAAで巨大なフラクタルを形成するというプログラムでした。
フラクタルの名前はTwin dragonといい、(もう少し)有名なDragon curveというフラクタルを二つ重ねたものになっています。
1ピクセルの点から初めてどんどん倍々にコピペしていけばMS Paintでも簡単に手作業で描くことができるかわいいやつです。
## ## ##
## ## ## ##
# -> ## -> ## -> ## -> #### -> ######## -> ...
## ## #### ########
## ## ## ##
## ## ##
プログラムの説明文にはこんなことを書きました。
A simple quine which prints itself twice on a slightly complex base.
quine というのは自分自身を書き出すプログラムの総称です。
「simpleなquine」に対比させて「slightly complex base(少しばかり複雑な基礎)」 と書きましたが、
これはダブルミーニングで、complex base(複素数の底)とかけています。
おなじみの10進法や2進法ではなく複素数z=1+iを記数法の底として使い、z進法でn桁以下の数を集めて図示するとTwin dragonの形になる、ということにちなんでいます。
ついでにプリニウスの博物誌から以下を引用して添えておきました。
ありとあらゆる動植物を非実在のものも含めて大量に陳列している書物の中で、いろいろな蛇を紹介している章からです。
geminum caput amphisbaenae, hoc est et a cauda, tamquam parum esset uno ore fundi venenum. aliis squamas esse, aliis picturas, omnibus exitiale virus.
— GAIUS PLINIUS SECUNDUS, Naturalis Historia 8.85.1
(……またアンフィスバエナという種は二つの頭、つまり尻尾にもう一つの頭を持ち、まるで一つの口では毒液を出し足りぬとでも言いたげである。これらの蛇のいくらかは鱗を、また別のものはまだらの皮膚を持つが、いずれも致死毒を所有する。)
「双子の龍」を「双頭の蛇」に見立てていますが、着目したのはvirus、ラテン語で毒液という単語。
これはもちろん日常語になった「ウイルス」の語源です。
自分自身を複製し際限なく増殖していくプログラムにはまことふさわしい語ではないでしょうか。
というのは建前で、賞の名前を審査員に代わって考えてあげておくと受賞しやすいという都市伝説の遂行が目的でした。
Most complexとか、Most viralとかそういうの。
伝わったかどうかはまったく定かではありませんが……
実装方法はもう完全に伝説的名著あなたの知らない超絶技巧プログラミングの世界からテクニックをパクりまくっています、ので先にこれを購入して完読しておけば以下の説明は不要でしょう。
プログラムはだいたい三層からなっています。外側から解説します。
まずは一番外のガワ。
eval $s = %q[ ... #
...
... # ]
中のプログラム文字列を変数に代入しつつeval
するおなじみのテクニックです、が一つ注意することがあり、
自分自身を複製していく中で4段階目に達するとこの部分が以下のように横に重なってしまいます。
eval $s = %q[ ... # eval $s = %q[ ... #
...
... # ] ... # ]
Rubyの謎仕様、入れ子にできる文字列リテラル%q[...]
を駆使したおかげでうまいこと構文エラーにならずに回避できています。
コメントアウトしない場合でも連続でeval
できて楽しかったのですが、せっかく保存した$s
がどんどん短くなっていくのでやめました。
二層目ではまた、中の文字列を各行x
という文字以降と空白を削って一行のプログラムに連結してからeval
しています。
eval(%q[
...
].gsub(/x.*|\s/,""))
これもプログラムが成長して横に並んだ時に余計な文字が入らないようにするためです。
中のプログラムの最後は#
で終えてあるので、以降の文字もすべてコメントアウトされて無視されます。
というわけで、三層目、ここが本当に自分自身を二回出力している部分です(i=1
はスペースの都合上先に実行しておきましたが)。
p = "eval$s=%q[#$s]".split*""
i,j,t = 1,0,[";"]
i,j,t = i-j,i+j,([""]*j.abs+t).map {|s|
r = t.shift || ""
r.gsub!(";") { p.slice!(0) } if $f || = p > p = p.center(i*i+j*j, ";")
r,s = s,r if i*j < 0
(" "*i.abs+s).ljust(r.size).gsub(" ") { r[$`.size] || " " }
} until $f
puts t
まず最初に保存しておいた$s
からもともとのプログラムを(前後の;
を除き)p
として復元しておきます。
i,j
にコピーするときに移動させる距離、t
に出力すべきAAを入れておいて、これらをループで成長させ、
最後の一歩手前でAAにp
を流し込んで目的の出力を得ています。
ここの文は二つの文字列を空白を埋めて合成しています。
s.ljust(r.size).gsub(" ") { r[$`.size] || " " }
# r = " AA AA"
# s = "B B B BB"
# => "B AAB B AABB"
gsub
で空白にマッチさせるとグローバル変数$`
にはその直前までの文字列が入るので、size
を見れば空白の位置がわかるわけです。
また審査員の方にもお気に入りにしていただいたこの部分
$f || = p > p = p.center(i*i+j*j, ";")
もう少しわかりやすく書くとこうなって
unless $f
length = i*i+j*j #空白を除いたAAの文字列の長さ(複素数の絶対値!)
p2 = p.center(length, ";")
if p != p2
p = p2
$f = true
end
end
ループの終了条件$f
の判定と;
を前後に補ってプログラムの復元を完成させる作業を同時にこなしているわけです。
これを思いついてから基盤部分をほぼ完成させるまでにだいたい3~4日でしたが、
プログラムのサイズを半分(256文字)に減らそうとして努力を無駄にし続ける作業にその後二週間くらいかかりました。
おかげでAAとしてきれいにまとまる短さにはなりましたが、ゴルフ力が足りなかった。
ぜひとも、この跡を引き継いでくれる方を募集しています。
とりあえずこれは
t=[?;]
こう書くと一文字減らせることを発見しましたが
t=*?;
まったく意味が分からない。なんだこれは。