CodigGameのゲームAI対戦環境をローカルで動かす 〜Kotlin編〜

パラメータ調整用にCodingameのローカルテスト環境を作った際のメモです。
主な対象はKotlinですが、Javaでもほぼ同じ方法で動かすことができます。

## CodinGameとは？
ゲームを通してプログラミングを学んで遊べるWebベースのプラットフォームです。
自作のゲームAIを戦わせるオリジナルゲームが非常に良く練られていてまたサイトの完成度も高く、数あるプログラミングサイトの中でもトップクラスの面白さだと思います。

より詳しくは以下が参考になるかと思います。
- https://qiita.com/javacommons/items/b178c924199d1a6d524d 

## なぜローカルの実行環境を作るのか
CodinGameではエディタでコードを書いてビルドして対戦させるまでがサイト上で完結するようになっており、ブラウザ1つあれば遊べるようになっています。
ですが新しい戦略を試したりパラメータ調整を行う際にはブラウザ上でポチポチ操作するより手元で自動実行したほうが楽で効率も良いため、ローカルの実行環境を作っておくことがオススメです。

## ローカル実行環境の構築
### 実行コードの取得・ビルドの準備
ゲームの実行環境はGitHubで公開されており、それをCloneすればコードの取得は完了です。
ゲームのルールのページ等にリンクがあります。（今回のSpringChallenge2021は https://github.com/CodinGame/SpringChallenge2021 にありました。）

実行環境のビルドシステムにはMavenが使われており、環境を用意してCLIからビルドしても良いですが今回はIntellij IDEAを使いました。
"Project from existing source..." からCloneしてきたソースのルートを指定し、 "Import Project" でMavenを選択すれば基本はIDEがよしなにやってくれます。
Kotlinがうまく入らなかった場合は.ktファイルを開いた際に "Kotlin not configured" のようなメッセージが出るので、"Configure" からMavenへのKotlin追加を選択すると pom.xml にKotlinのDependencyを入れてくれます。
"File" -> "Project Structure" から "SDK" も確認し、Kotlin以外になっていたら ”Kotlin SDK" を選択します。
あとは右上のMavenのメニューからProjectをリロードすれば読み込まれます。

### ゲームの実行
いよいよ手元でゲームを動かします。
ゲーム実行環境は先程のソースの src/test/java/Spring2021.java にあります。（他のゲームでもゲーム名かMain.javaのような名前になっているようです。）

ゲーム実行の本体は MultiplayerGameRunner クラスでこれにリーグレベル（レベルによってルールが変わります）、乱数のシード、そして自分で作成したゲームAIクラスを ”Agent” として登録します。
今回はKotlinで "Agent1.kt" と "Agent2.kt" に作成したものを登録します。
Spring2021.javaを参考にKotlin化したものが以下になります。

``` Spring2021.kt
fun main(){
    val gameRunner = MultiplayerGameRunner()
//    gameRunner.setSeed(-8289918975308209200l);
    
    gameRunner.addAgent(
        Agent1::class.java,
        "Agent1"
    )
    gameRunner.addAgent(
        Agent2::class.java,
        "Agent2"
    )
    gameRunner.setLeagueLevel(3)

    gameRunner.start()
}
```

Agent1.kt の中身は基本的にはCodinGameのWeb上で動かすものと同じです。
ただし Agent1.kt と Agent2.kt に同名のルートレベル関数などがあるとまずいので、それぞれのクラスなりに入れておく必要があります。
クラスをゲームシステムに渡すため、少し変わった書き方が必要なようです。
まとめると以下のようになります。

```Agent1.kt
object Agent1 {
    @JvmStatic
    fun main(args: Array<String>?){
        Agent1.solve()
    }

    fun solve() {
        val input = Scanner(System.`in`)
        // Game AI
    }
}
```

提出の際はObjectのくくりと "@JvmStatic" を削除します。

これにて準備は完了です。実行（IntelliJならSpring2021.ktのmainの横の実行ボタンを押すだけです）すると対戦が行われ、結果のアニメーションをローカルホスト（http\://localhost:8888/test.html）から見ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212995/74d82139-5b96-cb3b-6236-1a67c17de72f.png)

### アニメーションの省略と結果の取得
まとめてローカルで対戦を実行する場合、アニメーションは不要な場合が多いかと思います。
その場合 MultiplayerGameRunner.start() に変わって MultiplayerGameRunner.simulate() を用いることでアニメーション生成を省略して実行することができ、対戦結果データを GameResult クラスで取得することができます。
例えば両者のスコアを取得したい場合は以下のようになります。

``` Spring2021.kt
fun main(){
    // （略）

    val result = gameRunner.simulate()
    println("Agent1: " + result.scores[0] + ", Agent2: " + result.scores[1])
    // Agent1: 70, Agent2: 69
}
```

## トラブルシューティング・補足

- Intellij IDEAがエラーを吐いていたり、何か動きがおかしい際は "File" -> "Invalidate Caches.." をやると復旧してくれることが多いです
- コンテストごとに設定が必要だったり問題があったりすることもあるので、何かおかしければGithubのIssueも見ておくと良いです。SpringChallenge2022ではJavaScript側の設定も必要でした
    - https://github.com/CodinGame/SpringChallenge2022/issues/2

## 参考
- https://tech.io/playgrounds/25775/codingame-sdk-documentation/game-runner