東京ドームから脱出してきた

 久しぶり……でもない(?)、リアル脱出の話。

 今度は東京ドームから脱出してきた。もともとは4月に開催される公演に参加する予定だったが、震災の影響で4月分は全部中止となり、そのためにGWに振替公演が開催されたので、そちらに参加。共に参加してくれた仲間たちは朝早い集合にもOKしてくれて感謝。

東京ドームからの脱出とは

 リアル脱出ゲーム OFFICIAL WEB SITE を参照願いたい。

最初が一番難しかった

 最初ってなんのことかといいますと、「現地への到着」。乗る予定だった電車が本数縮小のために悉くなかったため、遅れに遅れて、開始5分前に到着しました。ギリギリすぎる。しかも何人か分のチケットを持っているので皆を待たせることに。いつもより余裕のないスタートとなってしまったが、実のところ、脱出自体はこれに比べれば難しくなかったと思う。

メンバー構成

 最近の規模の大きいリアル脱出は、チームわけなどが存在しないため、一緒に参加した仲間たちと協力することが一般的になりつつある。遊園地からの脱出では14人で挑んだ我々は、今回も大人数で挑んだ。といっても、人数にして7人。特に謎解きスキルの高いメンバーが何人か別チームでの参戦となったため、戦力的には遊園地の時と比べると若干厳しいのではないかと推測していた。初参加の人もいたし。

最初の一手が肝要

 今回も手元に地図や謎の回答を書くためと思われる答案用紙のようなレイアウトのページで構成された紙を渡された。開始宣言まで数分。紙に書かれている情報を全て頭にインプットする。開始までの時間はリアル脱出シリーズではお決まりのBGMが流れている。が、今回はドーム側の都合か、定期的に森永のCMが前面のスクリーンに表示された。雰囲気を作るBGMがその度に途切れ、明るくバカっぽい(失礼)ノリのCMが流れる。はっきり言ってシュールだった。が、これも謎を解く鍵かもしれないのでCMの内容を頭に入れておく。

 時間になるとグラウンドに一人の男が現れる。注意事項、説明、その他諸々話した後、開始を宣言する。制限時間は60分。

 僕は開始前に地図を見ながら、数字が振られたいくつかの場所に謎があるのではないかと推測し、開始を宣言される前に誰がどの場所を見に行くかの分担をある程度決めておいた。開始直後に全員に指示し、自らも自分の担当する場所を見に行くことに。

 道中、いくつかの謎をゲットする。数字しかかかれていないもの、平方の升目が半分だけひらがなで埋まっているもの。そして、観客席から見えるスクリーンに表示されたヒント。また、観客席の奥の壁上部に6つのユニフォームが置かれているのも目にした。僕は野球に詳しくないが、最初に聞いた説明によると読売ジャイアンツ永久欠番のユニフォームらしい。どう考えてもこれは謎を解く鍵だろうと思いつつ、とりあえず使いどころがないので保留。

 結局、地図上のポイントには何もなかった。一通り担当箇所を見て、最初の位置に戻る。他のポイントを見に行った仲間も、それぞれ謎を持ち寄りつつも、目的地では何も得られなかった様子。場所によっては、そこにいくための階段が封鎖されており、近づくこともできなかったとのこと。

 数字が振られた場所に謎があるという推測は外れていたが、それを探す仮定で一通り必要と思われる情報は得た。まずはこれらの謎を解く。謎ははっきりいって簡単だった。まあ最初から全力全開で謎を用意されたら9割以上の人が何もできないという酷いゲームになるだろうことは想像に難くないので、ちょうど良い難易度といえる。

×をもらいに

 もらった紙の上部に問題番号と連動すると思われる枠があった。何かに使うことは間違いないが、最初の時点では放置していた枠だが、使用できない階段にいったメンバーから、「×が4つ以上あれば階段を使えるそうだ」と聞いたことから、解答所に答えをもっていけば×がもらえるのではないか、と推測。この時点で解ける謎を全て解いて、皆で解答所に赴く。そこはまだがらがらだった。僕らはそこで解けた謎の分、――4つの×をゲットした。でも、なんで○じゃなくて×なんだろう。

階段をのぼって4階へ

 封鎖されていた階段からは3階と4階へいけるようになっていたが、実際には3階の扉は閉じられており、そのまま4階にのぼることに。4階には1つの謎を解くための11のヒントがあることがわかっていた。まずはそのヒントを回収。4階全部を回るのは効率が良くないので、2チームにわけて、両端からヒントを回収し真ん中で合流することに。得られたヒントは、「ちょこもなかのつづきは」。そんなの決まっている。答えは「ジャンボ」だ。チョコモナカジャンボ――開始前に散々スクリーンに流れていたCM、やはりヒントだった。

そしてグラウンドへ

 5つ目の×をゲットした僕らは、×が5つあれば入れるグラウンドへと踏み込んだ。スクリーンに提示されたヒントに従い、3つの鍵を手に入れる。「右下から時計回りに読め」「穴を読め」そして、謎の図。僕らはここで行き詰った。

 正直なところ、ここでゲームオーバーか、と思った。おそらく僕らだけでなく、ほとんどの参加者にとって、ここが脱出できるかどうかの分水嶺だろう。リアル脱出では最後、あるいは最後の手前あたりにこの手の最も難しい問題を配置しているようだ。遊園地からの脱出では、偶然にも僕の頭の中に答えがあったが、今回は自力で解ける気配もない。

 が、やはり謎解きが得意な人間というのはいるもので、仲間の一人がドームの天井と謎の図の関連性に気づいた。そして、手元にあった同じ形の図と照らし合わせて、「右下から時計回りに」「天井の穴の開いた場所」に該当する文字を読む。「すたーはゆうしゃだ」。

最後の謎

 スター――ここで言うスターとは、もちろん永久欠番の6人に違いない。そして、僕らの手元の紙には5つの×が書かれていた。それぞれの×の両隣には意味ありげな★マーク(形としては、★×★×★×★×★×★となっていた)。

 使いどころのなかった永久欠番の数字を★にあてて、なぜか○じゃなくて×がつけられた意味を考えると――、答えはひとつ。6つの永久欠番で乗算する。

 一人がスマートフォンで計算を行う。僕はその場で筆算する。計算結果が出たのは同時。どちらも同じ数字だった。

 さて、この数字をどう使うか。これは簡単だった。遊園地からの脱出でも使われた手法。最初に何も得られなかった地図上の数字が振られた場所。先の結果の数字の順に、地図上に線をひくと――、地図上に矢印が現れる。矢印が示すのは、閉じられていた階段の3階。

脱出後の楽しみ

 僕らは3階の閉じられた扉の前のスタッフに手元の紙を見せた。地図上に矢印が描かれている。スタッフは矢印を見て、扉を開いた。今回も、無事脱出できたようだ。

 僕らは脱出者が待つ場所へと案内された。そこには見知った顔もあった。そこでゲームの終了を待ち、別の場所へと案内された。

 僕らが案内された場所、それはVIPルーム。僕らはここから、下の観客席にいる脱出できなかった人たちを見下ろす。彼らに気づかれないように。彼らの前では開始を宣言した男によって、謎の解説が行われていた。今までと違い、スクリーンを使ったグラフィカルな解説。解説が進化していたとは。

 謎の解説はこのイベントでは恒例のことである。基本的に、参加者には回答が提示される。わからないまま帰されることはない。そして、全ての解説を終え、いつもの台詞。「今回脱出したのはこの方々です!」

 観客席の皆が振り向く。その先にいるのはVIP席に座る脱出成功者たちだった。僕らが参加した回の公演は、脱出率2.9%程度だったらしい。僕らは今回も無事に、最後に紹介される側になれた。そして少しの間、優越感に浸った。

関数型言語でプログラミングできない人が読む「プログラミング Coq」

 なにやら日本語で書かれた定理証明支援系言語 Coq の良質なチュートリアルが公開されたとのことで、とりあえず読んでみました。

http://www.iij-ii.co.jp/lab/techdoc/coqt/

 定理証明支援系とは何か、 Coq で何ができるのか、という話題についてはここでは触れませんので、興味のある方は検索してみるなり、 Twitterハッシュタグ #coqt をつけて誰かに聞いてみるのが良いと思います。

 さて、公開された最初の記事「http://www.iij-ii.co.jp/lab/techdoc/coqt/coqt1.html」では最初に読者に前提として求める知識についての記述があります。

読者の前提知識としては OCamlHaskell などの関数型言語でプログラミングできることを想定します。

http://www.iij-ii.co.jp/lab/techdoc/coqt/coqt1.html

 ここではこの前提条件を満たさない、つまり関数型言語でのプログラミングができない人がこの記事をどう読んだか、を記そうと思います。なんでそんなことをするかというと、「関数型言語でのプログラミング経験がないけど、この記事に興味を持った」人は少なからずいるのではないかと自分が考えているからです。そういう人の読解の一助になればいいな、というのが動機です。

 間違いやおかしいところなどがあれば、指摘いただければありがたいです(って書いとくと id:pi8027 君あたりが大量に指摘くれるはず)。

環境の準備(Windows

 ちょうどこの記事を読んだとき、 Windows を使用していたので、 CoqIDE の Windows 版をインストールしました。このあたりは、元記事を読んでいただければ概ね問題ないと思いますが、 Windows 版の場合、インストールした場所によっては、以下のエラーがでます。

Could not load preferences (Sys_error("C:\\Documents and Settings\\********/.coqiderc: No such file or directory")).

 このエラーが出た場合は、インストールしたディレクトリにある .coqiderc を "C:\\Documents and Settings\\********" にコピーしてから再度起動すれば上手くいくはずです。

関数の定義

 チュートリアルの最初に例示されている関数は以下の形です。

Definition plus (n : nat)(m : nat) : nat := n + m.

 これを手続き型っぽく書くと以下のようになると思います( C 言語っぽく記述してみました)。

int plus(int n, int m)
{
    return n + m;
}

 上記の2つの plus 関数は、概念はともかく処理の内容は結果的には同じです(ただし、 int が整数型であるのにたいして nat は自然数型なので負の整数を扱えるかどうかの違いや扱える数の大きさの違いがある)。それぞれ、以下のように対応します。

Coq C 説明
(n : nat) int n 第1引数
(m : nat) int m 第2引数
plus ... : nat int plus(...) 戻り値が nat / int の関数 plus
:= n + m return n + m; 第1引数と第2引数の和を返す

無名関数

 チュートリアルでは、無名関数としての実装例も記されています。

Definition plus' : nat -> nat -> nat := fun n m => n + m.

 まず、関数名の直後に nat が3つ並んでいますが、1つ目から順に、「第1引数の型」「第2引数の型」「戻り値の型」を指しています。

 次に、 fun の後の n m ですが、これはそれぞれ第1引数、第2引数を指します。最後の n + m は関数の行う処理ですね。

 後半で提示されるサンプルコードを見ると無名関数での実装例が多いので、この形を理解できるかどうかが、このチュートリアルを理解できるかどうかの重要な分かれ目だと思います。

forall

 次のサンプルコードの無名関数版では、全量化子 forall なるものがでてきます。これは、数学記号の∀(全ての)のことだと思います。例では引数 A にたいして「全ての」とつけていますが、 forall の他にどんなものがここに入るのかは知らないので、予約されていない型を用いる場合には基本的につけるものなのかな、という認識でいます(予約語を見る限り、 forall の他には exists とかが入りそうですね)。

アリストテレスの三段論法

 1つ目の証明を飛ばして、いきなり2つ目の証明にうつります。ここで例示されているコードの解釈で詰まった人がいるのではないかと思うので、自分なりの解釈を書きます。

Definition prop1 : forall (A B C : Prop), (B -> C) -> (A -> B) -> (A -> C) := fun A B C f g x => f (g x).

 "forall (A B C : Prop), (B -> C) -> (A -> B) -> (A -> C)" までは問題ないかと思います。『全ての「命題を表す型」A、B、C にたいして、「BならばC」ならば「AならばB」ならば「AならばC」』という型の関数を書きますよ、ということですね。ここまではチュートリアルの説明どおりの認識です。

 問題は、その関数の実装である "fun A B C f g x => f (g x)" の部分です。A、B、Cはともかく、f、g、xとはなんなのか。それがわからないと "f (g x)" の意味がわからないと思います。

 ここで、かなり前に説明した plus の無名関数を思い出してみましょう。

Definition plus' : nat -> nat -> nat := fun n m => n + m.

 この plus' 関数と prop1 関数をパーツごとに比較してみます。

plus' prop1
fun n m fun A B C f g x
nat -> nat -> nat (B -> C) -> (A -> B) -> (A -> C)

 plus' 関数では引数 n、m の型はどちらも nat でした。では、 f、g、x の型は何になるでしょうか。最後の (A -> C) が戻り値の型だとすると、引数3つに対して型が (B -> C) と (A -> B) の2つになってしまいますね。引数の型をわかりやすく理解するために、該当箇所の表記を変えてみます。

plus' prop1
fun n m fun A B C f g x
nat -> nat -> nat (B -> C) -> (A -> B) -> A -> C

 この形だと、3つの引数の型がわかりやすいと思います。つまり、 f の型は (B -> C) 、 g の型は (A -> B) 、 x の型は A です。そして、この3つの引数で表現される式 f (g x) の型は戻り値の型 C になることがわかります。

 手続き型っぽく書くと、

C f(B){}; // 型 B の引数をとって型 C の値を返す関数 f
B g(A){}; // 型 A の引数をとって型 B の値を返す関数 g
C prop1(A x)
{
    B temp1 = g(x);
    C temp2 = f(temp1);
    return temp2;
}

 という感じでしょうか。

終わりに

 さて、ここまでわかれば練習問題も解けるのではないかと思います。自分も一応 Define に成功するものを実装しました。ただ、自分の実装は模範解答ではないと思いますし、きっと模範解答はもっと詳しい方によって、あるいはチュートリアルの次回更新時に公開されるのではないかと思っているので、ここには記さず留めようと思います。

 関数型言語でプログラミングできない自分にも楽しめるチュートリアルでした。次回の更新も楽しみですね。

Tokyo Demo Fest に行ってきた

 Tokyo Demo Fest というのは、 DemoScene のイベントです。DemoScene とはなんぞや、という方は、 デモシーン - Wikipedia などを読んでみてください。日本では結構マイナー(だと個人的には思っている)なジャンルです。

 Tokyo Demo Fest のサイトは以下。

http://tokyo-demo-fest.jpn.org/

 僕自身は、 Demo を作ったことのないただの一観客なのですが、まあ縁(というか枠)があったので参加しました。

 当日は昼からセミナー、夜は鑑賞会( Demo Party )というスケジュールで行われました。

セミナー

 セミナーでは、 Demo の作り方や Demo 制作に役立つ知識の話など様々な内容のトークが行われました。内容はどれも実用的で素晴らしいものでしたが、参加された方の中には前提となる知識が不足していたりして、ついていけなかった方もいるのでは……という心配は少しあります。また、セミナーとして見るとタイムスケジューリングやトーク順など荒削りな部分もあり、試行錯誤の跡が見てとれました。今後回を重ねることで洗練されたセミナーになっていくことに期待しています。

 個人的には hole さん( @h013 )の 256byte イントロのトークが一番楽しめました。あと、 kioku ( @kioku_systemk )の how to make demo も裏側をちょっと知ってる身としては面白かったのですが、時間調整の都合かやたら長かった印象が強い……。

Demo Party

 懐かしい Demo から新作の Demo まで色んな作品が大きなスクリーンで見れたというだけでも十分素晴らしいことなのですが、 Amiga 実機上で動く Demo を見れたのが最も貴重な経験だったのかも。

 日本でこの手の Demo Party が行われた前例は知らないのですが、こういうイベントはとても楽しいので今後もまたやってもらえたらなあ、と思う次第です。そしてこういうイベントにいくと、自分も Demo を作ってみたくなるんですよね。まあ、自分は Demo を作れるほどの腕はないので、大人しく鑑賞する側でいますが。

 Demo Party の方にはフランスのケーブルテレビの方が取材にこられていたようで、日本と海外での Demo イベントに対する関心の差を痛感しました。日本ではマイナーなジャンルという認識ですが、これから少しずつでも国内で需要が増えていくといいなあと思ったり。

まずは demoscene.jp へ

 Demo に興味がある、という方はセミナーでも紹介されていた demoscene.jp を一度のぞいてみると良いと思います。色んな Demo がアップされていたり、 Demo について語れるフォーラムがある「日本語の」貴重なサイトですので、まずはここから、このジャンルに踏み出してみてはいかがでしょうか。

Notwife がすごく良い!

 Notwife というのは、 Twitter で mention とか follow とか favorite とかされた時に通知してくれるサービスです。ちなみに嫁ではありません。そもそも僕には嫁なんていません。

http://notwife.heroku.com/



何が良いのか

 Notwife が通知してくれるものの一覧は以下の図を見ていただければ一目瞭然です。

 mention は、最近ではプッシュ通知してくれる iPhone アプリなども増えてきているようですが、それ以外の情報が通知されるというところがキモです。

 Notwife はほぼリアルタイムにそれらの情報を通知してくれます。したがって、

  • 誰かに follow された
  • 誰かに fav された
  • 誰かの list に追加された

という情報が、その行為が行われると同時に通知されます。これでもう、 favstar やふぁぼったーを監視しなくても favorited 状況が把握できますね(僕の TL を見る限り、その手の監視をしている人が何人かいるようです)。

Notwife の面白いところ

 Notwife は上の図にあるように followed や favorited を通知してくれますが、通知してくれるのはそれだけではありません。なんと、以下の情報もほぼリアルタイムに通知してくれます。

  • 誰かに unfav された
  • 誰かの list から削除された

 ポジティブな情報だけでなくネガティブな情報も通知してくれるサービスってなかなかありませんよね。個人的にはここも素晴らしい点だと思ってます。

 ちなみに、これを活かして(?)、 fav/unfav を繰り返したりすることで通知しまくるというイタズラができますが、全然面白くないのでやっちゃダメですよ。

Notwife は携帯でも使える

 Notwife は Notifo という通知サービスを経由して通知してくれるので、 Notifo が対応しているプラットフォームで利用可能です。もちろん、 iPhoneAndroid でも利用可能なわけです。ちなみに僕は iPhoneMac ( Growl ) で利用しています。

Notwife で通知する内容を選べる

 でも最近の iPhone/Android アプリって mentioned とか通知してくれますよね。 faved とか followed とかを通知してくれるのは嬉しいけど、 mentioned とかは別にいらないよね、という方もいると思います。

 Notwife はこれらを個別のイベントとして通知してくれるようになっているので、個別に「これは通知してほしい」「これは通知いらない」というのを設定できます。これで unfavorited とか欲しくない人も安心ですね。



興味を持たれた方は是非一度使ってみましょう

 一番上にもリンク貼りましたが、以下から登録できます。

http://notwife.heroku.com/

 設定方法も詳しく書いてあるので、容易に設定できるかと思います。

Ruby で処理した結果を DB に格納するまでの顛末

そもそもやりたかったこと

 今開発しているツールは、複数のファイルから取得した情報を整理して用意されたテーブルに格納する、というもの。既存のプログラムから一部機能を流用できたため、開発言語は Ruby 、 DB は Oracle Database 。

 主に自分自身の備忘を目的として、どう考えて結局どうしたのかを記しておく。

RubyOracle にアクセスする方法

 まずは軽く調べてみた。 Oracle のインタフェースは ruby-oci8 がある。しかし ruby-oci8 は Oracle クライアントが必要、という制約があった。

 ツールのユーザは限定されているが数は少なくない想定で、彼らの環境には RubyOracle クライアントもない。 Ruby については他のツールの都合で遅かれ早かれインストールされるとしても、今回作成するツールのために Oracle クライアントをインストールする、というのは抵抗を覚える人も少なくないだろう。なぜなら、彼らは Java で開発しており、 Oracle への接続は Thin Driver を用いているからだ。 Ruby にも Thin Driver があれば良かったのだが、僕には見つけられなかった。

Thin Driver を使うために JRuby を採用する

 Ruby で使える Thin Driver は見つけられなかったが、 Java では普段から Thin Driver にお世話になっている。 Java で使えるということは、 JRuby でも使える、ということだ。

 ということで、 Ruby で書いていたコードに、

require 'java'

を追加。

 また、 rubygems で取得したライブラリをそのまま使えるようにするために、 JRuby の gem install が必要であることに注意。普通に gem を実行すると Ruby の gem が実行されてしまう( PATH の設定の問題)ので、 JRuby のインストール先の bin ディレクトリに格納されている gem コマンドでインストールする必要がある。

コマンドプロンプトで標準出力が文字化け

 今回のツールは Windows で作成していたが、標準出力(正確には標準エラー)が文字化けして、デバッグに苦労した。出力されたもじれつが UTF-8 であるのに対し、コマンドプロンプトは Shift-JIS であるため、文字化けが発生するのは当然なのだが、解消までに時間がかかってしまった。

 Ruby からの出力自体は Kconv文字コードを指定すれば良いのだが、 JRuby から呼んでいる Oracle から返ってくるエラーの文字コードを変更する方法がわからなかった。 エラーの内容がわからないとデバッグの難易度が一気にあがるので、なんとかしてエラーの内容を理解できるようにしたかった。

コマンドプロンプト文字コードを変更する

 ツールが出力する文字列の文字コードを変える方法がわからなかったので、思いきって、コマンドプロンプト文字コードを変更した。

chcp 65001

 これでコマンドプロンプト文字コードUTF-8 になり、ツールからの出力結果が読めるようになった。

 ちなみに、文字コードを Shift-JIS に戻すには、

chcp 932

とすればよい。

ojdbc14 の jar を JRuby で使う

 JDBC の jar もなしに Oracle にアクセスすることはできないので、 jar を認識してもらう必要がある。普通にファイルパスを require してみたが、これはダメ。どうやら対象の jar は JRuby の lib ディレクトリに格納されている必要があるらしい。ファイルをコピーしたところ、正常に動作した。

以上

 ここまできて、やっとテーブルへの insert が正しく動作することを確認できた。大したことはやってませんが、同じところで二度と嵌らぬようにここに残しておきます。

Ruby で処理した結果を DB に格納するまでの顛末 の続き

jar の配置が気にくわない

どうやら対象の jar は JRuby の lib ディレクトリに格納されている必要があるらしい。

Ruby で処理した結果を DB に格納するまでの顛末 - risou style

 って書いたけど、このツールは他のいくつかのツールと合わせて一つのツール群として提供する前提のもので、 JRuby の他に Jython も使っている都合上、ライブラリの jar はまとめておきたかった(同じ jar が2つもあるなんておかしいよね)。

 ということで、ちょっと調べてみたところ、「 jruby/lib に格納するか、 CLASSPATH に jar を追加すれば使える」とのことだったので、 CLASSPATH に jar を追加してみた。

 ところが、この方法だと例外がスローされているようでエラーが返ってくる。実際どう書いていたかと言うと、以下のように記述していた。

      java.lang.Class.forName('oracle.jdbc.driver.OracleDriver').newInstance
      con = java.sql.DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)
      stmt = con.createStatement

 例外が発生するのは1行目。 Class#forName が怪しい。あるいは newInstance 。が、 JRuby の仕組みに詳しいわけでもないし、まずは正しく動くようにしたかったので原因を追求するのではなく、記述方法を変更。

include_class 'oracle.jdbc.driver.OracleDriver'

  def db_connect
    begin
      con = java.sql.DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)
      stmt = con.createStatement
      ...
    end
  end

 include_class を用いることで newInstance する必要がなくなる。このやり方だと CLASSPATH に jar を追加しておけば正しく DB に接続してくれる。

CLASSPATH に jar を追加せずに同じことをやりたい

 環境ごとに CLASSPATH 設定してください、というのもユーザにとっては億劫だろうということで、 CLASSPATH に jar を追加せずに JRuby のプログラムで最初に処理する方式に変更。

require 'find'
cdir = Dir.pwd
Dir.chdir("#{libdir}")
Find.find(Dir.pwd) {|jar|
  if File.basename(jar) =~ /(.*\.jar)$/
    require jar
  end
}
Dir.chdir(cdir)

include_class 'oracle.jdbc.driver.OracleDriver'

 jar を格納しているディレクトリに移動し、 jar ファイルを走査して片っ端から require していく。それが終わったら、 include_class でクラスを include してあげれば、(正しく require できていれば) JRuby 上でクラスを使用することができる。