Quitada ブログ HAX

Hatena Blog でも Quitada ブログ

「じゃんけんに必ず勝つロボット」をソフトウェア的に GemFire で実装

ちょっと前に、日本の最高学府である東京大学で「じゃんけんに必ず勝つロボット」が開発されました。記事は以下を参照願います。

WIRED - 「必ず勝つじゃんけんロボット」を東大が開発

仕組みは、カメラで人間がじゃんけんの手を作ろうとする中間動作から判断してそれに対して勝つ手を出す、いわば「後出しじゃんけん」である。それを、超高速に行うので、人間の見た目には後出しじゃんけんに見えないので「必ず勝つ」と言っている。

で、これにインスパイアされて、VMware 社のデータグリッドミドルである vFabric GemFire のリアルタイムなメッセージング機能を使えば、ソフトウェア的に似たようなことができるのでは?と思い立って、ま、リアルタイムメッセージング&イベントハンドリングの簡単なデモとして GUI アプリを作ってみたのでさらしてみたく思います。

アーキテクチャは以下のような感じです。

  • GemFire のキャッシュサーバー機能を持つ GUI アプリケーションで、ジャンケンの手を入力→リージョンエントリの更新でイベント発生
  • キャッシュサーバーとして動作する GUI アプリに、必ずジャンケンに勝つアプリケーションがクライアントとして接続し、サブスクライブ→イベント発生によるメッセージ受信で、GUI アプリケーションの手に対して必ず勝つ手をリアルタイムに表示
    • 拡張機能として、「必ず負ける」、「必ず引き分け」機能も無駄に実装


#画像をクリックして、オリジナルサイズを表示をクリックすると原寸大のサイズで表示します。

ということで、いきなりソースコードやらクラスファイル、cache.xml ファイルやらプロパティーファイル一式をまとめたものを添付いたします。
GemJanken.zip 直

Eclipse のプロジェクトにこの階層でインポートして、GemFire の lib 配下にある gemfire.jar と antlr.jar を外部 jar として設定すると動くはず…。

さて、ソースコードを軽く説明します。

上述のキャッシュサーバー(実際は、だす手を決める GUI アプリケーション)は、MyJankenGUI.java です。Swing とか AWT を使った GUI アプリケーションです。GemFire と関連する実装としては、以下の actionPeformed メソッドです。

public void actionPerformed(ActionEvent e){
    String cmd = e.getActionCommand();
    if (cmd.equals("g")){
        //label.setText("グーを出しました");
	jankenRegion.put("janken","g");
	label.setIcon(gicon);

GemFire 的にやっていることは、上述の場合だと「グー」ボタンがおされたら、JankenRegion リージョンに対して、キーが janken、バリューが g とエントリーを追加(あるいは更新)しているだけです。

次にクライアント(実際は、だされた手に応じて必ず勝つ・引き分け・負けるといった手を出すアプリ)ですが、こちらは JankenClient.java です。こちらはちょっとトリッキーで、GUI アプリケーション兼、GemFire クライアント兼、GemFire キャッシュリスナーになっています。理由は、各パート間でいくつかのオブジェクトを共有したかったからでして…。GemFire と関連する実装としては、まずは JankenRegion を取得して、キーが janken というエントリにサブスクライブをしている以下の部分です。

jankenRegion = cache.getRegion("JankenRegion");

jankenRegion.registerInterest("janken");

これだけで、キーが Janken というエントリーに対して新規作成やら更新があった場合にリアルタイムにイベントを拾います。後は、拾ったイベントに対するイベントハンドラーの部分です。ここは、キャッシュリスナーの実装となります。

public void afterCreate(EntryEvent e) {
    //System.out.println("    Received afterCreate event for entry: "
    //+ e.getKey() + ", " + e.getNewValue());
    changeImage( (String) e.getNewValue());
}

public void afterUpdate(EntryEvent e) {
    //System.out.println("    Received afterUpdate event for entry: "
    //+e.getKey() + ", " + e.getNewValue());
    changeImage( (String) e.getNewValue());
}

afterCreate がサブスクライブしたエントリーが新規作成された場合にコールされるメソッドで、afterUpdate は更新された場合にコールされます。このアプリケーションでは、どちらの場合は changeImage という private なメソッドをコールしており、そこで、キャッシュサーバーで実装された GUI アプリのジャンケンに対して必ず勝つ(あるいは、引き分け、負ける)手の画像に切り替えます。

なお、デフォルトでは「必ず勝つ」ですが、クライアントアプリ起動時の引数に l(アルファベット小文字のエル)を渡すと「必ず負ける」、d(アルファベット小文字 d)を渡すと「必ず引き分け」となります。

ちなみに、ジャンケンの手の画像は漫画カメラにて撮影した当方の手です。漫画カメラにて撮影した画像の、当ブログエントリへの掲載について問題がある場合は本ブログエントリにコメントをいただければ対処いたします>漫画カメラ作者様

さてこのアプリケーション、くだらないですねー。ともあれ、アピールポイントとしては、ライセンスの範囲内でクライアントアプリケーションをどんどん追加起動できるところです(評価ライセンスだと 3 つまでですが…)。それなりの数が一斉に反応するのはなかなか壮観です。

まとめますと、このアプリケーションの中核となる機能の実現のため、実際やっていることはリージョンへの put と、イベントハンドリングコーディングだけです。これだけで、プロセス間通信とイベントドリブン的な処理が実現できるのは、すごくないですか?>もちろん、GemFire が。

ということで、今後も GemFire を弄り倒していきたいと思います。