ICFP Programming Contest

http://icfpcontest.org/

実に楽しかったです。とりあえず結果は 2761 てん。スコアボードと比べると結構増えてるんですが、それまでカラだった BLNCE ができるようになったからだと思います。点数的には今回はまぁグループ有利なんでしょうね。まぁどうせ上の方行けないので別にいいですが。

感想としては、まず第一にこのシステムすげーーーということ。まず UM の仕様とその UM 上で動くプログラムが公開されていて、それを実行するとまたプログラムが出力され、それを実行すると UMIX なるシステムが UM 上で動き、その上で色々なパズルが楽しめる、と。この中に言語処理系が 4つも (私が発見できなかっただけで5つ目があるとのこと)入っててまぁそれはそれはすごいなぁと。

そんでパズルは発見したものは自由に選べるので、ある程度自分の好みの問題を選んだりと、レベルに応じて楽しめる感じで、毎回まんなかあたりでぼんやりしてる私にはとても嬉しい仕様でした。

さて思い出。

まず全然 UM が動きませんでした。割とすぐに UMIX のプログラム自体は出てきたんですが、 Allocation の仕様の英語が読めてなくてえらい時間がかかりました。普通に実装したら良かったんだなぁと…この間はなんか楽しげな問題がありそうなふうの ICFP からのメールとかが来てるなか、何があるのかすらわかってなくてつらかったです。

INTRO
UM が動いたらログインしたりメールを見たりすると得点がもらえました。こうやって得点ゲットしていくんだなぁとわかります。メールによると、パスワードを置いてある謎 BASICコンパイルしてクラックせよとのこと。そのへんでファイルの転送の仕方が学べました。まぁこの BASIC に感動したのですが、行番号を含めた数字が全て アラビア数字 (それはローマ数字だろ!とのこと。ご指摘感謝)なのでした。遊び心満載というか。最初に置いてある BASIC コードは foobar のような文字列を試すだけなのですが、 foobar42 のような定型のパスワードに2ケタの数字つけたものをクラックすると良いよーとコメントが書いてあるのでそれは自分で実装することになります。 words(i)+j とかいうふうにして、あれークラックできないなーとずっと思ってたんですが、これを PRINT してみると foobarXII とかになってて、うあーすげーと思いました。正解は words(i)+CHR(j+XLVII)+CHR(k+XLVII) みたいなかんじ。
ADVTR
それでまずアドベンチャーゲームが置いてあるアカウントをクラックできたと思います。このアドベンチャーは実にひどいもので、 A の上に B が置いてあって B の上に C その上に D …というぐあいになってるのですが、このゲームでは捨てるコマンドが使えないらしく、不要なアイテムは焼き捨てることになります。アイテムは壊れてるものが多くて、壊れたアイテムは組み合わせることによって復活するのですが、まぁこの組み合わせがイヤがらせのようになっていて、 antenna のついてない radio でないとダメ、とか、組み合わせの順序が決まってたりとか、まぁ大変でした。私は最後の方になってやっと最初の部分だけやりました。その後はなんかもっと悲惨な世界で、たぶんプログラム書いて自動的に解かせろとかいう意図だったんじゃないかなと思います。まぁ最初の部分が終わるとアカウントが拾えたのですぐやめました。
BLACK
次におもちゃを作るパズルをやったと思います。簡単なルールに従って音が鳴るもので、仕様にあったおもちゃを作れという問題。普通に探索したらすぐ指数爆発して、ちょっと枝刈りしたらほんの少しは解けたのですが、難しいおもちゃは全然作れませんでした。 GA とかでいけるかなーと思って保留して後で再チャレンジしたのですが、全然ダメでした。みんな割と 1000 てん取ってるし私も後で考えたらできるだろーと思ってたのにできなかったのは悲しかったです。まぁこの手の問題は一人でやってる身としては先行きが不安すぎてやる気が起きにくいです。
CIRCS
次に 2D プログラミング言語で仕様にあったプログラムを書くというお題でした。具体的にはこんなの。
,...................|.....................,
:mult               v                     :
:       *=============*  *===============*:
:       !case N of S,E!->!send[(Inr(),E)]!-
:       *=============*  *===============*:
: *=================*|                    :
->!send[(W,E),(W,S)]!#----------+         :
: *=================*v          v         :
:         |        *========*  *=====*    :
:         +------->!use mult!->!use p!-----
:                  *========*  *=====*    :
,.........................................,

こんなもののパーサとか書くのは普通でも大変だろうに、このシステム作った人はホントすばらしいなぁと思いました。必死こいてえらい長い時間かけてやったら一応問題は解けました。これは小さなコードを書いた方がいいというお題で、なかなか PKU チックで楽しかったです。 "a professional programmer knows that shorter code is better code" という文は色々素晴らしいなぁと思いました。

ANTWO
次はアリのシミュレータみたいなのに従って、アリがエサにたどりつける戦略を考えてあげるというもの。これも探索系のプログラム書くのかなぁと思ったのですが、これも先行きが不明ですし、ちょっとやってすぐやめました。他の人見る限りあまり得点取れてないようでしたし。
ADVIS
今度はマクロ言語。変換ルールを記述しておいて題意にあう結果を出すコードを書きます。 CIRCS もそうですが、すごく実用性のない言語なので、なかなかにマゾプログラミングが楽しめて良かったです。しかしできるものは足し算とかかけ算とかなので浮かばれない。苦労しましたけど解けたようです。これをやってる最中に UMIX が更新されて、最新の UM は微妙に最適化したせいで動かなくなってるとかで困りました。さらにその時に変なメモリ確保しまくりバグを入れてしまったのでその後はとても苦労しました。
BLNCE
最後の方にやっと発見できた問題で、かつなんか極めつけという感じの変態的なもので、 VM の上にまた実装されてる VM でした。これもご多分に漏れず実用性皆無の VM で、命令が 4 つしかありません。「レジスタの値をメモリに代入せよ」とかが問題になるくらい使えない物体でした。これはかけ算とメモリを埋めるもの以外はできました。かけ算は 1 が無いのにどうやって実装するのかなぁ…と思いつつ終わった感じでした。

個人的には今年はとても楽しかったです。妙にイヤげなトラブルだのヘンな勘違いだのが多かったのはしんどかったですが。今年は特に、チームじゃなくても、他の人が近くにいると他の雰囲気とかつかめてラクなんだろうなぁと思いました。1人で暗中模索というのもなかなか雰囲気にもあって、良いものでしたけど。あとまぁ、関数型言語の素養とかがあると有利なんですかね。今回は Haskell 風味な問題が多かったような気がするのですが、関数型よーわからんのでわかりません。

どうでもいいけど終わってみると得点はほとんどマゾプログラミング系だけでした…

主催者に感謝。たぶんスーパーハカーな人が膨大な労力を払ってたんだろうなぁと思います…他の参加者のかたはおつかれさまでした。

ぐへー… UM で動く UM …自分自身を永久に呼び出し続けて下さい…

http://icfpcontest.org/task.shtml#materials

追記。

http://www.kmonos.net/wlog/63.html#_0214060725

あー kuma- とかいるけど情報理工のおかしい連中(言うまでもなく誉め言葉)じゃねーとか思ってたらやはりというか。少なくとも単身じゃないのは安心というか。トップ3つくらいはチームじゃなきゃビビるというか。 BLACK とか ANTWO とかどう解くもんなんだろう、とか、 BLNCE のかけ算どうするねんとか気になりますが、そんなことより第5の言語処理系が楽しいというお話はもったいないことをしたなと。無駄な時間はたくさんあったので ADVTR やっておけば良かったです…とりあえず解説は読まずに、暇な時にやってみようと思います。

UM で動く UM とか見てると UMIX のソース配って欲しいなぁとか思いますね。もちろん UM 上で動くコンパイラコンパイルできるソースを。あと JS で UM 実装しておくと 3年もすればブラウザ上でできるかもね、とか。夢ひろがるなぁ。こういう、クロスプラットホーム/マルチ言語を実現するためにまず VM を定義しちゃうっていう思考は面白いよなぁとか。今流行ってる種類の VM は言語をだいたい特定しちゃってるのはアレなのかもとか。 MS Excel とかそんな感じで動いてるとかでしたっけ。最近はそういうの流行らないのかな。私のコードはバイナリ配布ですが、 NES バイナリなのでクロスプラットホームです! iPod でも動くぜ hehehe... とかどうだろう。

一応、上のは個人的な感想なので、せっかくだから説明足りてない部分について、すばらしい問題群の紹介を簡単にしておきます。興味を持ったかたは来年の参加を検討されると良いです。

  • ADVTR

emacs で遊べるアレみたいなものです。テキストベースでメッセージが出てて、取れるコマンドを入力する、と。

There is a bolt here.
Underneath the bolt, there is a spring.
Underneath the spring, there is a button.
Underneath the button, there is a (broken) processor.
Underneath the processor, there is a red pill.
Underneath the pill, there is a (broken) radio.
Underneath the radio, there is a cache.
Underneath the cache, there is a blue transistor.
Underneath the transistor, there is an antenna.
Underneath the antenna, there is a screw.
Underneath the screw, there is a (broken) motherboard.
Underneath the motherboard, there is a (broken) A-1920-IXB.
Underneath the A-1920-IXB, there is a red transistor.
Underneath the transistor, there is a (broken) keypad.
Underneath the keypad, there is some trash.

などと積み上がってるアイテムは、6個までしか手元に持てないため、必要でないものは焼き捨て、必要なものはうまく合成して数を減らしつつ keypad の修復に必要なものをそろえます。「もっと悲惨な世界」ではさらにたくさんの、無機質な名前のアイテムがたくさんあって辟易してやめちゃったのでした。

  • BLACK

音の鳴るおもちゃを組む問題です。パイプが何本かある、そのてっぺんからボールを入れると、アミダ状にボールが動いて出てくる、その時に右に動く時は音が鳴る。各パイプにボールを入れた時に出てくる場所と、音の回数が与えられているので、それを満たすおもちゃを考えろ、とのこと。

0 -> (3,4)
1 -> (2,3)
2 -> (0,1)
3 -> (1,1)

みたいな仕様に対して、

 ><||
 |><|
 ><||
 |><|
 ||><
 |><|
 ||><
 ><||
 ><||

みたなものを作ってやれば OK 。 >< がアミダの交叉する部分です。このサイズは手でも作れますが、大きくなると大変です、というか解けませんでした。

  • CIRCS

上の図がだいたい全てをあらわしています。この言語、アルゴリズム考えてから実際に書くまでがとても大変で、専用のエディタとか作った方が良かったなぁと思いました。トップの方の人はきっとサイズ最適化機能つきコンバータとか書いてるんじゃないかなぁと思ってたのですが、真相は不明。他もそうですけど、基本的にプログラムは再帰ばっかりって感じで、関数型関数型してる感じかもです。

  • ANTWO
Puzzle 1: Simple
NNNNNNN
NNNWENN
8 8
 - - - - - - - -
 -0>0< - - - - -
 - - - - - # - -
 - - - - - - - -
 - - - - - - $ -
 - - - -0v - - -
 -1^ - -0^ - - -
 - - - - - - - -

図をはってみたものの、全然やってないし思い入れないので説明むずかしい…特定のルールで動くアリに対して、行動の方針と初期配置を決められるので、うまくエサを取らせてあげましょう…と。行動のルールはだいたい決まってるのですが、アリとアリが衝突した場合の方針は指示できます。それが NNNNENN とか書いてあるところです。

  • ADVIS
Add Z y => y;
Add (S x) y => S (Add x y);

ルールベースの置換を連続させて計算をさせる、マクロ言語?みたいなもの。上記のコードはサンプルにあった足し算です。 Z が 0 、 (S Z) が 1 、 (S S Z) が 2 ... というルールで、足し算を実装しています。

Add (S S Z) (S S Z) -> S (Add (S Z) (S S Z)) -> S (S (Add Z (S S Z))) -> S S S S Z で 2+2=4 が計算されることがわかるかと思います。たぶんこの言語の肝は、ルールの適用は、先頭しか見てくれないことだと思います。上記定義ではたぶん Add (Add x y) z みたいなのは動かないと思います。

そこを根性でなんとかするという話なのですが、私は中から計算するために、外にある関数はとりあえずマークをつけて後ろにやっておき、中を計算してから戻してくる、っていう方針でやりました。マークをつけるとかはモナドとかと関係があったりするかもしれませんがよくわかりません。

  • BLNCE

パズルとしか言いようがない言語というか VM でした。私的にはこれが一番楽しかったような気がします。命令が 4 つしか無いというのがなんとも素晴らしいです。そしてこれらの命令は妙な挙動をします。たとえば MATH という命令は、

M[ dR[D+1] ] <- M[ sR[S1+1] ]  -  M[ sR[S2+1] ]
M[ dR[D]   ] <- M[ sR[S1]   ]  +  M[ sR[S2]   ]

と記述されている通り、足し算と引き算を同時に行う命令です。あとは AND と XOR を同時に行う LOGIC 、特定条件でプログラムカウンタの動作速度を変更する SCIENCE (これが if とか goto とかを兼任しています、動作速度を変更するだけなので、色々妙なコード短縮ができそうで面白そうでした)、レジスタに即値を加算しつつレジスタを即値の値に応じたルールでローテーションさせる PHYSICS 、とどうしようもなくクセだらけの 4命令です。こんなわけのわからない命令セットでプログラムが書けるというのはなかなか驚きでした。というか私は LOGIC 使わないまま問題解いてたので、実質 3 命令です…

たとえばメモリの内容をレジスタにコピーしろ、という問題があったのです。まず mov みたいなのは無いですし、そもそもメモリ→レジスタの方向で干渉できる命令が存在しません。しょうがないのでループ回してメモリの内容をデクリメントしつつ、レジスタを 1 ずつ増やしていくことになるのですが、レジスタは加算するとすぐに場所が変わってしまいますし、メモリ内の引き算をする副作用の足し算が起きるので、それが大事なメモリを破壊しないようにしつつ…などとパズルなことを考えなければなりません。

メモリ内にある a*b を計算しろという問題があったのですが、どこにも 1 が無いので、えーとどうやってかけ算実装するのかなぁ、と考えてる間に終わりました。真相は闇気味です。 LOGIC うまく使うのかしら…

  • と、

まぁこんな感じでした。よくわからんばかり言ってますが。書いたコード群の記念撮影。名前が不適切なのが多くてひどいですけど。

i@u wrk/icfpc/2006> wc *.d *.cc *.rb *.sh *.ant *.adv *.bal *.2d~README.2d Makefile
    64    209   1877 asm.d
    21     56    450 disasm.d
   338   1048   9627 ga.d
    17     59    451 mkultra.d
   184    519   4319 raytest.d
   167    471   4048 raytest2.d
     5     16    106 test.d
   315    936   6750 toy.d
   493   1324  11144 um.d
   477   1284  10598 um_stable.d
    78    217   1377 umutil.d
   226    654   4569 toy.cc
    15     29    214 asm.rb
     9     16    146 convgoal.rb
     7     12    132 cutgif.rb
    14     35    226 dumpimg.rb
     7     20    204 rayparse.rb
    32     72    667 umixsh.rb
     4      5     39 brute.sh
     2     11     65 toyparse.sh
    12     66    173 pz1.ant
    20    504   1086 pz2.ant
    15     98    251 pz5.ant
    34    216    790 mult.adv
    27    213    647 xml.adv
    37    339   1048 xml2.adv
    35    333   1098 xml3.adv
    27    253    766 xml4.adv
    32    277    855 xml5.adv
    47    450   1433 xml_.adv
     1      1      9 addmem.bal
     1      1     17 addmem2.bal
     1      1     19 clearreg.bal
     1      1     36 copymem.bal
     1      1     37 copymem_.bal
     1      1     37 copyreg.bal
     1      1     37 copyreg_.bal
     0      1      2 dummy.bal
     1      1      5 stop.bal
     1      1      5 stop1.bal
     1      1      5 stop127.bal
     1      1      5 stop128.bal
     1      1     13 swapmem.bal
     1      1      7 swapreg.bal
     0      1     60 swapreg2.bal
    25     83   1061 mul.2d
    88    328   5701 mult.2d
    29    105   1848 plus.2d
    29    106   2004 plus2.2d
   149    777  12451 ray.2d
   393   2369  36426 ray_.2d
   150    777  12450 ray_last2.2d
    26     77   1466 raytest.2d
    42    100   1583 rayu.2d
    24     80    930 rev.2d
    17     48    287 Makefile
  3746  14607 141657 合計
なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h