書いた人: 遠藤侑介 (@mametter)
この記事では、Ruby を用いたプログラミングコンテスト TRICK 2013 の概要と入賞作品を、主催の 1 人であるわたくし遠藤からご紹介します。 RubyKaigi 2013 での発表とほぼ同一の内容です (リンク先に当日の様子を撮影した動画あり)。
まずはコンテストの趣旨を説明し、それから入賞作品を紹介します。大体わかってる人や、ごたくはいいから作品みせろという人は、後半から読んでください。
TRICK は一言でいえば、Ruby で「変態度」を競い合うプログラミングの祭典です。いろんな意味で「変態だー 1」と言いたくなるようなプログラムほど評価されます。
TRICK というのは Transcendental Ruby Imbroglio Contest for rubyKaigi の略です2。Transendental というのは本来「霊界に関する」という意味の単語ですが、なぜか日本語では「超絶技巧」のような訳が当てられる3ので、このコンテストでもこちらの語感として使っています。Imbroglio は「極めて混乱した、複雑で、厄介な事態」というイタリア語由来の単語です。ということで、意訳すると「超絶技巧 Ruby 意味不明コンテスト for RubyKaigi」という感じです。
TRICK は 4 つの目的を掲げています。4
TRICK は International Obfuscated C Code Contest (IOCCC) に非常に影響を受けています。 IOCCC は、読みにくい C コードで競い合うコンテストです。この分野では最も有名で、最も歴史があります (1984 年から)。 初期は「C ってこんな書き方できたのか!」という気の利いた作品が比較的多かったですが、最近は「こんなものすごいコードが動くのか!」という大作が採択されやすい傾向になってます。
同様のコンテストは Perl でもあったようです (Obfuscated Perl Contest 、現在は開催されていない模様)。
Ruby でも International Obfuscated Ruby Code Contest (IORCC) というコンテストが 2 度ほど企画されたようですが (2005 年 、2012 年)、継続せずサイトが落ちていたり、企画倒れになったりしたようです。
TRICK も元々は IOCCC にならって IORCC という名前にしようと思いましたが、あまり縁起がよくなさそうだったので別の名前にしました。
ルールは以下の 5 点のみです。
作品の芽をなるべく潰さないよう、ルール自体は必要最小限にしました。審査員がどういう作品を期待してるかを伝えるため、ガイドラインも用意してます 5 。詳しくは TRICK 2013 のサイトを見てください。
今回は 7 名が審査員をしました。
これ書いてて気が付きましたが、7 人中 5 人が Rubyist Hotlinks に出てました。リンク貼っておいたので興味あればご参照ください。
なお、筆者 (遠藤) が告知や取りまとめや RubyKaigi での発表など首謀者みたいな顔をしてましたが、実際にはコンテストの発案と豪華審査員の勧誘は shinh さんの仕業だったりします。また、投稿の受理や採点の集計などの雑務は全部 @hirekoke さんにやってもらいました。
各審査員が投稿された各作品を 10 点満点で評価します。その平均点が作品のスコアとなります。 このコンテストは審査員自身が (他の審査員には内緒で) 作品を投稿することを認めています。自分が投稿した作品は当然採点の対象外となります。 また、先入観なく審査するため、匿名審査を行いました (審査の際、審査員は、作者の情報を見ず、作品のみを見て評価する)。
今回は 10 作品を入賞とさせて頂きました。また、上位 4 件には「選外佳作」「銅賞」「銀賞」「金賞」を差し上げました。 入賞作品は github で公開しています。以下は各作品のネタバレを含みますので、自分で考えたい人は github の方を参照してください。
mame | leonid | yhara | eban | shinh | eto | matz | total |
4 | 6 | 6 | 5 | 6 | 5 | 6 | 5.43 |
不思議の国のアリスをそのままコード化したものです。ヒアドキュメントとかではなく、直接コードになってるあたりにご注目。 実行するとその文章が出力されたあと、キャラクターの行動履歴が抜粋されて出てきます。
主なトリックは method_missing です。動作を表す単語にはアンダースコアが前置されてます。ここにもうちょっと工夫があれば、さらに高得点が得られていたと思います。
baban さんの入賞の喜びの声にリンクはっておきます。
mame | leonid | yhara | eban | shinh | eto | matz | total |
7 | 8 | 7 | 1 | 9 | 7 | 1 | 5.71 |
Ruby では Unicode の文字をそのままプログラムに書いて識別子として使えるのですが、Unicode にはたちの悪い文字が多数存在します。これを悪用 (Unicode phishing) したのが本作品のトリックです。 例えば、’NO-BREAK SPACE’ (U+00A0) は空白文字ですが、ASCII のスペースとは異なる文字なので識別子とみなされます。def の後にメソッド名がないように見えるのは、普通のテキストエディタではこの文字が目に見える形で表示されないためです。また、ハイフンにしか見えない ‘MINUS SIGN’ (U+2212) や、アラビア語などで右から左に文字を書くための制御文字 ‘RIGHT-TO-LEFT EMBEDDING’ (U+202B) なども効果的に使われています。
remarks にはかかれていませんが、この作品は Unicode 以外のトリックも冴えています。例えば %%%%%%% はコメントのように見えますが、# ではなく % です。これは % 記法と String#% を巧妙に用いていて、4 で割った時の余りが 3 になる長さの時だけ書けます。また、一見普通のプログラムに見える部分が、実はヒアドキュメントとして無効化されていたり、defined? の中で変数宣言してみたり、さまざまな技巧が駆使されて難読化されています。
さらに、remarks の日本語も難読化されていて、審査員間で話題になりました。
惜しむらくは、プログラム自体が「Unicode の結合文字を大量に出力する」という攻撃的な挙動なことです。ちょっと意図を掴み兼ねたので、審査員間の評価は大きく分かれるものになりました。もうちょっと面白い出力にするか、さもなくばこういう挙動にした意図を remarks で説明するなどの工夫があるとさらによかったと思います。
mame | leonid | yhara | eban | shinh | eto | matz | total |
6 | 7 | N/A | 5 | 8 | 7 | 5 | 5.83 |
審査員による入賞作品 1 つめです。面白さはコードを見れば一目瞭然だと思います。 トリックは、alias です。必要な組み込みメソッドを片っ端から ruby に別名付けしています。 実行すると 主題に関連する URL が出てきます。0 分 50 秒付近から見るといいです 6 。 詳しいことは remarks を読むといいと思います。
mame | leonid | yhara | eban | shinh | eto | matz | total |
7 | 7 | 8 | 3 | 2 | 8 | 9 | 6.29 |
GC で有名な Ruby コミッタの authorNari さんの作品です。brainfuck のインタプリタです。挙動も見た目も普通なんですが、実装方法がクレイジーでした。
トリックは ObjectSpace#each_object の悪用です。このメソッドはオブジェクト空間にある全オブジェクトを、メモリアドレスの低い順にイテレートしていくものですが、そのブロックの中でメモリアドレスの高い位置に新しいオブジェクトを作る 7 ことで、このイテレートが止まらなくなります。この無限ループを利用して brainfuck の評価器を構成しています。
mame | leonid | yhara | eban | shinh | eto | matz | total |
6 | 6 | 7 | 5 | 10 | 9 | 2 | 6.43 |
Ruby コミッタで mswin64 メンテナのなかむら (う) さんの作品です。たった一行のコードなので全部引用します。
このコードの主旨・意義は remarks で雄弁に語られているのでそちらをご覧ください。 真の命題をそのまま書いて true を返すのがすばらしい、ということでこの賞名になっています。
mame | leonid | yhara | eban | shinh | eto | matz | total |
N/A | 7 | 8 | 6 | 4 | 7 | 8 | 6.67 |
また審査員による入賞作品です。battle quine system です。 チェスや将棋で、プレイヤーの強さを数値化した「レーティング」を聞いたことがある人は多いと思うのですが (参考例)、ああいうのはイロレーティングという計算方法に従っていることが多いです。 この作品は作品自身がプレイヤーになっていて、実行するとレーティングが更新されます。プログラムの形状が自分のレートを表しています。初期状態では 1500 。 このプログラムを alice.rb や bob.rb などと複製して、ruby alice.rb bob.rb というように実行すると、alice.rb が勝利、bob.rb が敗北したものとみなして、新しいレートの形状になったプログラムが 2 つ続けて出力されます。 引数に与えられたファイルの数値を読み解く必要があるので、ある意味 OCR みたいなことをしていますが、まあフォントデータを使って照合しているだけなので大したことはないです。 さらに小ネタとして、あまり知られていなさそうな -i という ruby のオプション 8 を使うと、引数の方のファイル (上の例でいうと bob.rb) を破壊的に書き換えてくれるので、コピペの手間が減ります。
mame | leonid | yhara | eban | shinh | eto | matz | total |
6 | 9 | N/A | 8 | 8 | 7 | 7 | 7.50 |
ここから上位 4 件になりますが、これまた審査員の入賞です。実行すると “JUST ANOTHER RUBY HACKER” と出ます。 プログラムは例外の見本市みたいになってます。一行ずつ例外を起こし、その例外クラスの名前から必要な文字を拾ってくる、という感じです 9 。 その遠回しな実装方法や、なんとなくセンスを感じさせるレイアウトなど、全審査員に好評でこの順位となりました。
mame | leonid | yhara | eban | shinh | eto | matz | total |
N/A | 8 | 10 | 5 | 7 | 10 | 7 | 7.83 |
ピアノロールの DSL がくっついた、オルゴールのプログラムです。 実行には /dev/dsp が必要です。cygwin だと簡単。Linux だと padsp などを使えばよい。os x では sox を使うためにちょっとコードを書き換える必要があるので、remarks を見てください。面倒なら youtube の動画を見てください。
DSL は # が音符で、| はスラーになってます。一番左端の列に # を各と低いラの音が出ます。そこから一つ右にいくごとに半音ずつ上がっていきます。 ものすごくさりげないこだわりとして、Ruby コードを簡単に AA 化するための “eval-split-join” は使ってません。つまり、キーワードや変数の途中でスペースや改行が来ないようになってます。 また、sin 波やのこぎり波だとつまらないので、周波数変調 (いわゆる FM 音源) で多少オルゴールっぽい音を合成してます。コマンドライン引数でパラメータを変えると、若干音が変わります。 審査員からは「Quine はもう飽きた」という不評の声も多かったですが、一部の審査員には評価され、このスコアとなりました。実は 2 位と同点なんですが、万人に好評なものの方がよかろうということで、こちらが銅賞。
mame | leonid | yhara | eban | shinh | eto | matz | total |
9 | 8 | 9 | 9 | N/A | 7 | 5 | 7.83 |
まずはコードのハイライトがない状態で、プログラム全文をご覧ください。
ポエムです。実行すると、何も出力せず正常終了します。つまり、syntax error とか出ません。なんでかわかりますか? 以下、本当にネタバレになってしまうので見る前にぜひ一度考えて見てください。
正解は、プログラム全体が
という構造になっていることです。この you != program が false に評価されるので、全体として何も評価されません。 で、この you と program ってなんだ、ということですが、これは begin の中に秘密があります。
という部分です。Ruby では普通 for 文って使わないんで知られていませんが、for 文のパラメータとして使った変数は for 文の後でも使えます。つまり、この for 文は変数宣言のような役割を果たしています。これによって you と program が定義され、でも初期化はされないので両方 nil になったまま if 文の条件式が評価され、you != program は nil != nil なので false となる。 という、すばらしい仕組みになっています。
この作品は審査員全員から高評価を得ました。唯一惜しいのは、単に正常終了するだけであることです。これで何か出力するなどすれば、金賞だったのではないかと思います。
mame | leonid | yhara | eban | shinh | eto | matz | total |
8 | 9 | 10 | 10 | 10 | 7 | 8 | 8.86 |
このプログラムは、以下の 2 文で完全に説明されます。
ということで、アルゴリズムとしては 32.upto(126) { | x | putc x } 相当ですが、いくつか困難があったとのことです。 |
この作品はほぼ満場一致で金賞でした。金賞が審査員じゃなくて本当にほっとしました。
なお、作者の kinaba さんによると「自分としては、このコード、まだまだ不満です」とのことなので、改良に挑戦してみるのもいいのではないでしょうか。
作品は以上です。景品は、
ということになりました。
以上、TRICK 2013 の趣旨と入賞作品紹介でした。
「審査員ばっかじゃん」とか思われるかもですが、はっきり言ってしまえば総投稿数がそんなには多くなかった 11 ためだと思います。来年もできれば開催したいと思ってますので、その際はぜひぜひご参加ください。
投稿数は少ないものの、平均レベルが非常に高く、10 件を選び出すのはなかなか大変でした。残念ながら落選した作品 (リポジトリには含まれていません) の中にも個人的には結構好きなものがあり、ぜひ改良して来年のコンテストに投稿してほしいです。12
また、今回は残念ながら日本人以外の投稿者がいませんでした。海外からのご応募もぜひぜひ。(日本語で書いてもしょうがないですが)
最後に、この文章は筆者 (遠藤) の独断と偏見で書かれているので、他の審査員の記録にリンクをはっときます。
遠藤侑介。 CRuby コミッタ。Ruby 2.0.0 のリリースマネージャだった。 本内容に関わる業績としては、超絶技巧プログラミング提唱者、IOCCC 2012 入賞。
Twitter: @mametter
褒め言葉として。特に性的な意味合いはない。 ↩
審査員の一人 leonid がつけてくれました。 ↩
Transcendental Etudes というピアノ曲の定訳が「超絶技巧練習曲」だったり、transcendental number が「超越数」だったり。 ↩
知ってる人ならわかると思いますが、IOCCC の目的のパクリです。 ↩
このやり方も IOCCC にならってます。 ↩
審査員の遠藤は動画のタイトルとイントロだけ見て止めてしまったので、RubyKaigi の発表当日まで面白さに気づいてませんでした。IOCCC のガイドラインにも書かれていることですが、remarks ははっきりちゃんと書いた方がいいと思います。審査員が面白ポイントを見逃して低評価を付けることがあるので :-) ↩
オブジェクトのメモリアドレスは Object#object_id で取得できますが、アドレス位置を指定してオブジェクトを作る方法は当然ないので、所望の位置にオブジェクトが出来るまでオブジェクトを生成し続けるという大変アホなコードになっています。 ↩
引数に与えられたファイルを in place に編集するモード。理解せずに使うとファイルを消してしまうので注意。 ↩
例外を起こさず、値を返してそのクラス名から文字を拾ってくる行もあります。 ↩
まあ全作品を載せてもいいんですが、”Most characteristic” のように端末をぶっ壊すとか、”Worst house of garbage” のように動作保障しがたすぎるのとかは面白いけどさすがにまずいかなあ、などという配慮の結果です。 ↩
なんとなく IOCCC の伝統に従って件数は公表しませんが。審査員に直接聞けば教えてくれることもあります。 ↩
落選作品には「面白さがわからない」「コンセプトはいいけど実装が平凡」という傾向があったと思うので、「remarks をちゃんと書く」「アイデアだけでなく実装方法ももう少しだけひねってみる」というのをお勧めします。 ↩