「CodinGame : Gamese for Programmers」はゲームをしながらプログラミングスキルを高めることができる夢のようなサイトです。
最近この手のサイトを結構見かけるようになりましたが、CodinGameはエディタがVim/Emacsバインドに対応していたり、多数のプログラミング言語が使えたり(Ruby/JavaScrip/Java/C++など多彩)プログラマー心をくすぐる作りになっているのが特徴です。
出題されるゲームもパズル的でやりごたえがあり、グラフィックスも本格的です。ただちょっと解き方に癖があるので慣れないと最初いきなり詰まってしまうかもしれません。
始めのほうのゲームを少しやってみたので紹介してみたいと思います。
Onboardingを解く!
最初のゲーム「Onboarding」の目的は自分のキャノンが敵に体当たりされないように防衛することです。ターンベースで敵がせまってくるので、うまく撃ち落とせるようにコードを改良していきます。
ゲーム画面
ゲーム画面は4つのパートに分かれていて、左側が実行中のゲーム画面とコンソール出力、右側がエディタとテストケース(問題)となっています。テストケースはOnboardingの場合、01〜04まであり数字が増えるほど難しくなっています。
習うより慣れよということで、初期状態のソースコードのままテストケース「Five enemies」実行してみると…最後の敵にぶつかって失敗してしまいました。やはりソースコードを改良するしかないようです。
ゲームのルール
コーディングを始める前にルールの把握を。ソースコードの上の方に英語でゲームのルールが書かれていたので、訳してみました。
- 1ターンごとに標準入力から情報が入手できる: トータルの敵の数、敵の名前と自分からの距離を得ることができる
- 標準出力に敵の名前が書かれるまでシステムはウエイトする。
- 指示を出すと、キャノンの発射、敵の移動が実施され、最初に戻る
なんとなくわかったような、わからないような感じですが、要するに「標準入力から敵の情報を読み取って、標準出力に敵の名前を出力すれば撃ち落とすことができる」というルールのようです。
ソースコードの改良
これを踏まえた上で、ソースコードを見てみます。初期状態のソースコードを整理してみました。
STDOUT.sync = true # 削除しないように
# ゲームループ
loop do
$count = gets.to_i # 敵の船の数を取得
$count.times do
# enemy: 敵の船の名前
# dist: キャノンと船の間の距離
$enemy, $dist = gets.split(" ")
$dist = $dist.to_i
end
puts "HotDroid" # 敵
end
ゲームループの中で、全ての敵の情報をループで持ってきて、最後に「puts "HotDroid"」によりHotDroidという名前の敵を撃ち落としていることがわかります。これだと、敵の名前が固定されているので、他の名前の敵が攻めてきたときに対応できなさそうですね。
実は、テストケース「Five enemies」では、攻めてくる最初の4体の敵の名前はHotDroidになっていてそのままのソースコードで撃ち落とせるのですが、最後の敵だけ名前が異なっていて撃墜に失敗していたのです。
そこで「puts "HotDroid"」の行を次のように変更すると、変数$enemyに格納された敵の名前を狙ってキャノンを発射できるようになり、いろいろな名前の敵に対応することができるようになります。
puts $enemy # 敵
もう一度「Five enemies」を実行してみます。
「You Win!」見事成功です!!
まだまだ続く
次のテストケース「Imminent danger」は改良版ソースコードでも失敗します。理由は高速で移動する敵が含まれていて、やみくもに見つかった敵を選んで撃墜しているだけでは攻撃が間に合わないからです。
これを避けるためには、敵との距離$distをうまく使って、一番接近した敵から撃ち落せばよさそうですが、ソースコードにどう反映すればよいのか…。ということでここからの改良はおまかせしたいと思います。
まとめ
標準入力と標準出力を使ってゲームを解くという概念がおもしろいです。Onboardingでは敵の名前と距離を標準入力から読み取り、標準出力に名前を書き出すシステムでしたが、
次のゲームSkynetでは、道路の長さを標準入力から読み取り、バイクの操作を標準出力に書き出すというシステムになっています。シングルプレイゲームだけではなく、マルチプレイゲームや、コンテストもあり、かなり奥が深そうです。
プログラミングに興味が有る方はチャレンジしてみてください。