T-Rex Player (Google Chromeの隠しゲームを自動でやるやつ) を作った話

この記事は、TSG Advent Calendar 2015の19日目です。16日目は lip_of_cygnus さんの 任意の数字を出現させる話でした。

今回は、先月の駒場祭のTSGの展示で僕が展示した 「T-Rex Player」について書こうかと思います。

紹介

まずはこちらをご覧ください。



これです。
で、ソースコードは ここ です。

概要

Google Chromeには、隠し機能(イースターエッグ?)として、「インターネットにつながりません」の画面において、恐竜をジャンプさせるゲームが遊べる、というのがあります。*1これを自動でプレイさせる、というのを文化祭でやってみました。

目的とか

駒場祭に展示するため。実用性は(ほぼ)ない。もともと、あるブロック崩しを自動でクリアするために書こうとして尻切れトンボになっていたコードがあったので、それを流用して作った。*2

技術

画面の情報を得たり、キーボード入力を自動でやったりするのには、たいていWindowsAPIを使っています。いろいろなライブラリが出回っている昨今、直接APIを叩く機会はあまりないですが、このレベルの機能を使うことによって、今回のようなこと以外にも、APIフックとかDLLインジェクションとかができたりします。*3

WINAPIのとこのちょっとした解説

ウィンドウズのハンドルやデバイスコンテキストハンドルを適宜取得することによってやってます。
画面のHDCを得るのには、rensya_window.cpp の CopyWindow 関数を使ってます。これは、関数が呼ばれた瞬間のウィンドウを、いったん作ったビットマップに転写して、そのビットマップのHDCを返す関数です。これを使うとたとえば、一気に大量のウィンドウキャプチャを取っておいて、それを時間をかけてゆっくり画像にしていく、みたいなことができます。*4 *5
得たHDCをAIのjudjerに投げたあと、judjerで適当なところの色をGetPixelで得、その結果に従ってキーボード入力を自動でやっています。また、デバッグとかをしやすいように*6、その上から図形を上書きしたりしてます。
キーボード入力は、SendMessageで対象ウィンドウにWM_KEYDOWNとかWM_KEYUPとかを送ることによってやっています。(send_key.cpp内) *7

judjerについての蛇足

AIと銘打ってはいますが、中では画像認識などの大層なことはしていません。いわゆるルールベースというやつです。 *8
得た画像の中ほどの高さのところの色を横一列に得ると、例えば
....#.#.....#.###.#.....##.#.....
とかいう感じで、障害物のあるなしが得られるので、これに従って、
....###.....#######.....####.....
というように障害物の位置を推測し、直前のものと比較して現在の画面の移動速度を得、また、障害物が近ければ飛ぶようにしています。

その他

展示中はわりかし順調に(2~30分ほど)跳び続けていたので、「このくらい人間にも簡単では?」と思われてしまい、来た人がなかなか驚いてくれませんでした。もうちょっと展示法を工夫するべきだったかなと思っています。*9

今後の展望

駒祭中に、「Googleの画像検索でブロック崩しが遊べる」を自動でプレイするのを書いたりしてましたが、わりと簡単に(おおざっぱに)書いてある程度動いたので、単純なゲームを自動プレイさせるのには向いてるんじゃないでしょうか。


この記事は、TSG Advent Calendar 2015の19日目でした。
23日目は、今のところ levelfour_ さんの「MLっぽい自作言語を実装する(願望)」ですが、他にも部員がいろいろ居るはずなので、その方々の記事が間に入ってくるはず(期待)です。

*1:Wifiを切るなり、機内モードにするなり、LANケーブルを引っこ抜くなりした状態で適当なウェブページに繋ごうとすると、DNS_PROBE_FINISHED_NO_INTERNETが出るので、その画面でスペースか上十字キーを押すとゲームが始まります。オンラインでも、こことかこことかでできるみたいです。

*2:何度やってもクリアできなかったので、プログラムにやらせようと思ってコードを書いていたのですが、実験としてやってるうちにクリアできてしまったので、やる気がなくなってしまってお蔵入りしてたのです

*3:展示二日目あたりに手を加えたところ、オブジェクトの破棄し忘れをやってしまい、頻繁にプログラムが落ちちゃってました。この現代においてメモリリークでバグらせるのはなかなかたいぎなので、特殊な用途でない限りはラッパを使った方がいいかと思います

*4:というか、ファイル名からして、その用途で使ってたものでした

*5:HDCから実際にピクセルの色を得るには、GetPixel関数が使えますが、これはあんまり速くなく、640×480の画像全体の画素を得る、とかすると到底リアルタイムでは処理できないのです(もっと速い方法を知ってる方がいれば教えてください)

*6:あと、展示としての見栄えがよくなるよう、展示中にいじって「それらしく」したりしてました

*7:これももっといい方法があったら教えてください

*8:当初は、適切なパラメタを機械学習させてやろうと思っていたのですが(ちょうど強化学習分科会とかやってましたし)、適当に手でいじっていると、安定して跳べるようになってしまったため

*9:あと、パックマンとかブロック崩しとかと比べて見てて単調なので、あんまり見る面白みがなかった気がする