2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Codingame Summer Challenge 2025 で6位になり2度目のContests部門Top10入りしました

Last updated at Posted at 2025-08-04

はじめに

  • 以下、 Codingame Summer Challenge 2025 ないし Soak Overflow - Summer Challenge 2025 の画像を利用しています。
  • Codingame Summer Challenge 2025 とは
    • 2025/7/17~2025/7/28 の間行われたBot Programmingという参加者の提出するAIコード同士を戦わせる競技です。
    • 開催回毎に異なるゲームに対する AI Bot を作ります。提出すると、他の参加者の作ったAI Botと自動で対戦して順位がつきます。
    • 今回は2次元グリッド上で行われる、最大5人対5人の同時手番2人対戦ゲームで、プレイヤーは最大5体のユニットを同時に行動させます。
      • about.png
    • 実力に応じてリーグ分けされており、今回は計8リーグに分かれています。
      • league_levels.png
  • CodinGame コンテストページ
    • Contests 期間限定で終了済み。
    • Bot Programming 常設版。終了後の現時点で遊びたい方はこちらから。
  • 結果
    • 6位でContests部門の過去最高順位を更新しました!
    • my_progression.png

記事独自の略語

  • md
    • |dy| + |dx| (マンハッタン距離。移動や射程・エリア支配計算で使う)
  • chd
    • max(|dy|, |dx|) (チェビシェフ距離。正方形状。主にボム爆風範囲で使う)
  • HP
    • 100 - wetness = 100 - 累積ダメージ (残り体力)

基本ルール

  • 行動
    • 各ユニット、移動行動(停止or縦横1マス移動)と攻撃行動(射撃、ボム投擲、防御姿勢)をそれぞれ0~1個指定でき、全員同時に移動~防御~攻撃という順番で実行されます。
  • 勝利条件
    • 敵を全滅させたらその時点で勝ちます。
    • エリア支配(後述)によるスコアが敵より600点多くなったらコールド勝ちします。
    • 100ターン経った時点あるいは両者同時に全滅した時点で、エリア支配によるスコアの多い方が勝ちます。

移動

  • md1以内の移動です。つまり、停止か上下左右移動が可能です。
  • 2人のユニットが交差することはできず、両方停止扱いになります。
  • マップ上には遮蔽物が固定でおいてあり、そこへは移動できません。画面外へも移動できません。移動できない方向を指定したら、停止扱いになります。
  • 2人以上のユニットが同じ座標へ移動しようとした場合は、関連する全員が停止扱いになります。
    • 停止が別ユニットの停止を連鎖で誘発することがあります。
  • 倒されたユニットは消えるので次ターン以降移動可能です。

射撃・防御

  • 射撃ダメージ=攻撃側ダメージx(1-防御率)
  • ユニット(GUNNER,SNIPER,BOMBER,ASSAULT,BERSERKER)ごとに定められたベースダメージ(16,24,8,16,32), 射程(4,6,2,4,2), クールダウン(1, 5, 2, 2, 5) があります。
  • ただし、射程は実質上記の2倍です。
    • 射程内では攻撃側ダメージ=ベースダメージ
    • 射程超え~射程の2倍の範囲においては攻撃側ダメージ=ベースダメージ/2
  • 防御
    • ボム投擲や射撃をしていない時はいつでもコストなしに防御姿勢をとることができて、防御率+0.25を得られます。なので、暇ならとりあえず防御します。

    • 遮蔽物に隣接していると高さに応じて防御率+0.5または+0.75。

      • 遮蔽発動条件は以下をすべて満たしているときです。
        • 防御側がmd1で隣接している。
        • 攻撃側がchd1で隣接していない (接近戦は壁越しでも遮蔽効果なし)。
        • x座標だけ見たとき、またはy座標だけ見たときに、攻<遮蔽物<防 あるいは 防<遮蔽物<攻 という位置関係になっている。
          • 攻と遮蔽物の間に空白があってよいが等号はNGです。
      • 複数遮蔽効果あったら一番良いものだけ採用されます。
    • 防御姿勢と遮蔽物は加算なので、高い遮蔽物に隠れて防御姿勢をとっていると0ダメージになります。

  • クールダウン
    • ターン開始時にクールダウン0の時だけ攻撃できます。
    • 射撃したらユニット毎に定められたクールダウン値+1がセットされます。(+1の分は1行下ですぐキャンセルされます。)
    • 毎ターン、1以上なら1減少します。
    • 結果的に、攻撃できる間隔は、(2,6,3,3,6)ターンに1回、至近距離敵防御なしのターンあたりダメージ(DPT)は(8,4,2.667,5.333,5.333)と計算できます。

ボム

  • ユニット(GUNNER,SNIPER,BOMBER,ASSAULT,BERSERKER)ごとに定められた個数(1,0,3,2,1)個初期状態で持っていて、在庫があれば1ターンに1個投げることができます。

  • 0~1マス移動後、md4マス以内に投げて、ただちに着弾し着弾点を中心とするchd1以内の9マスが爆風範囲になります。

  • ボムのダメージは爆風ならどの位置でも30で、敵味方関係なく当たり、防御姿勢も遮蔽物も一切関係ないです。

  • ボムにはクールダウンはありません。在庫があれば連続で投げることができます。

  • 確定投げ

    • 敵の座標に投げれば、絶対に回避できないです。
      • なので、ボム持ちのmd5以下に接近するには、敵が持っているボムを全部ぶつけられる覚悟が必要です。
      • bomb_10.png
      • bomb_11.png
    • 外周や遮蔽により逃げ場が減っていると、縦横斜め1マス隣に着弾しても回避できない位置があります。
      • これにより、自分だけ逃げ場がないと、md6,7マスから一方的に被弾確定されることがあります。以下の図は、一番上のラインは画面上端なので、さらに上に逃げられません。
        • bomb_00.png
        • bomb_01.png
        • bomb_02.png
        • bomb_03.png
      • ユニット同士がmd2以内にある場合、衝突により、逃げ場を減らすことができます。
        • bomb_20.png
        • bomb_21.png
  • 遮蔽による確定回避

    • 敵が遮蔽に隣接していて遮蔽を挟んだ反対側に自分がいる場合は、md5に接近しても次敵は確定できないです。
      • bomb_30.png
  • 上位互換座標へのアップグレード

    • 以下条件をすべて満たすと、上位互換とみなし、行動選択の際上位互換候補のどれかに矯正します。
      • ある投下地点のchd1のどこかである。
      • 元の投下地点の爆風範囲のうち敵が存在しうる地点すべてをカバーしたうえで、追加の敵が存在しうる地点が爆風範囲になっている。
      • 投擲者の移動後の位置からmd4以下である。移動候補に衝突が絡む場合は、移動前後のマス両方から投げられないと上位互換とみなさない。
      • 爆風内の味方の移動しうる範囲は同じか純減している。

エリア支配

  • 各セルについて、遮蔽を無視したmdで近い方が支配しているとみなします。同点ならどちらも支配していない扱い。
  • HP半分以下ならmdが倍扱いとなり、支配しづらくなります。
  • 毎ターン|支配数の差|がスコアとして、多く支配している陣営に加算されます。
  • スコア差が600点以上になったらコールド勝ちで終了するので、ボムを回避するために引き撃ちし続けているとエリアで負けます。

ユニット特徴まとめ

ユニット名 GUNNER SNIPER BOMBER ASSAULT BERSERKER
HP 100 100 100 100 100
射撃ベースダメージ 16 24 8 16 32
射撃射程 4(8) 6(12) 2(4) 4(8) 2(4)
射撃クールダウン 1 5 2 2 5
射撃DPT 8.000 4.000 2.333 5.333 5.333
ボム個数 1 0 3 2 1
ボムダメージ 30 - 30 30 30
ボム投擲射程 4 - 4 4 4

アルゴリズム

  • 1手読みです。
  • 自ユニット全員の行動セットをMプール, 敵ユニット全員の行動セットをNプールずつ固定で確保し、全停止+防御で初期化しておきます。
  • 完全に同一にならないように注意しながら、各プールそれぞれ山登りで改善します。
    • 各ループ、乱数で1プレイヤー1プール1キャラ選びます。
    • 山登りの近傍
      • 移動は移動可能な方向から一様に選びます。
      • 攻撃対象は射撃クールダウンが0でお互いに一番接近したときに攻撃可能になるなら選ばれる可能性があります。一様ではないです。
      • ボム座標は敵に当たりうる場所に限定したうえで、確定投げでないものを除去したり、前述の上位互換候補があるならアップグレードを行います。敵は楽観的に、味方は保守的にしたいので、選び方は敵味方非対称です。一様ではないです。
      • ボムを投げた結果味方に当たるなら、確率的にその味方の逃走を連鎖させます。
      • 移動した結果味方とバッティングするなら、確率的にその味方の移動を連鎖させます。
  • 評価値計算では、改善対象は乱数で選んだ1プールだけ見ますが、その対戦相手はNプールを評価して重み付き和(p×最良値+q×平均値+r×最悪値, p+q+r=1, 敵味方非対称)し、対象プールの元のセットと比較してよい方を残します。
    • 例えば、以下のような図になっていた場合、最良重視や平均重視なら前のものをキープ、最悪値重視なら更新されるでしょう。
      • pool1.png
      • pool2.png
  • 最後に残ったMプールの中から一番良いものを選びます。

評価方針

  • HP
    • 被弾側の現在HPごとに1ダメージ与えることの価値を変えます。
      • 特に、倒しきるときと50以下になるときは重要です。これをやらないと、倒しきるときは大抵の場合フルにダメージを与えられないので価値が低いとなってしまい、瀕死の敵が大量発生してしまいます。
    • 被弾側ユニット特性ごとに1ダメージあたりの評価値を変えます。大体DPTと相関します。
    • 攻撃は役職によって差異がありますがHP量は全員一律なので、相対的に攻撃が弱いSNIPER, BOMBER, BERSERKERはタンク職として前に出てボムを体で受ける役割を担います。
      • SNIPERが前に出るのは直感的でないですが、そうすればGUNNERの生存期間が延びるのでやりましょう。
      • SNIPERはある程度ダメージを受けたら引いて遮蔽に隠れつつSNIPERの名前通りの仕事をします。
  • ボム
    • 2者の座標を与えるとここは双方確定、片方だけ確定などの優劣関係が前計算できます。
      • 単なる座標を2つ入れた表引きです。
      • 敵だけ片方確定になりやすい場所は弱いです。
        • 前線と反対方向に遮蔽があるマスは弱いです。
        • 画面上下端もボムに関しては一方的に確定されるリスクが高まるので弱いです。
          • ただし、射撃フォーカスの合う敵が減るというメリットはあるので、何もない中央を進めというわけでもないです。あと、マップ生成アルゴリズム上外周とx座標中央は必ず遮蔽が生成されないので、上下端が通り道になるので仕方なしに通るパターンはかなり多いです。
    • ボムは無制限でないので、投げる行為自体にボムを1個失うというコストがあります。1体当てれば収支+になるようにしておきます。2, 3個目のボムは、投げないと抱え落ちのリスクが高まるので、安いコストにしておきます。
    • root局面~1手後
      • 実際の結果はダメージで見ます。
      • 実際の結果とは別に、非確定敵ユニットの移動可能範囲内に爆風を増やしたかも評価対象です。
    • 1手後~の状態評価
      • 1ターン目のダメージやりとりとは別に2者の座標から次の優劣予測として評価関数に反映できます。
      • 特に、移動後集中砲火ポジションにいるか、はちゃんと見ておき評価関数に入れておく価値が高いです。
    • 抱え落ちは避けたいです。
      • 上位になると、不用意にBOMBERが進んだだけで集中砲火を浴びて抱え落ちすることがよくあります。
    • 逃げ可能投げ
      • 原則は投げないです。
      • 敵が一方的に確定投げ可能な不利な状況で、自分へ当ててくることを読んで投げるぐらいが妥当です。特にトドメをさされそうなときは投げなくても抱え落ちするので投げてよいです。
      • 敵の行動を決めるときは、候補に入れます。
      • 当たらなかったとしても、逃げ道を限定することによってその先の逃げ場が無いから2体巻き込みが成立するとか、次ターンそこに攻撃をフォーカスするように集結しておける、といった長手数要素は探索するなら見つけていきたいところ (深さ方向に探索していないので未実装)
    • 射撃クールダウン0で射程範囲内のときに先にボムを投げるのは勿体無いのですが、ダメージが高い分ピュアな評価関数だと先にボムを消費しがちなので、状況に応じて補正しておきます。
    • 防御側
      • タンク職(SNIPER, BOMBER, BERSERKER)はボムを受けるのは仕事なので、複数巻き込みやボム抱え落ちでなければ無理に遮蔽から逃げなくていいです。遮蔽から逃げて別の射撃に当たる方が問題です。
  • 射撃
    • 高遮蔽に逃げ込まれてダメージが0になるかもしれない状況は、結果ダメージ当てられたとしても評価値マイナスしておきます。
      • ただし、防御側2人以上が1箇所の遮蔽を取り合う状況だと誰を狙うかのじゃんけんになり、一切打たないのは損なので調整が必要です。
  • エリア
    • エリア負けていたら接近、勝っていたら遮蔽重視が基本とします。
      • 単純にエリアスコア最大化しようとすると、敵だけHP50超のときに引いてなんとかエリア拮抗させよう(離れた方が確保できる領域数が多いため)、複数ユニット分散してエリア確保しよう(被らない方が領域数多くなるため)という動きになってどのみちエリア負けしつつ各個撃破されてよくないです。
      • スコア減少局面で有利ポジションにいる場合にそれを捨てて前進するモチベーションを付与するとよいです。
    • 最終盤一騎打ち接近戦では遮蔽要素が皆無なのでエリアが最重要です。精いっぱい広くとるような動きをしましょう。

リーグ別やったこと

  • リーグとは
    • league_levels.png
    • Legend以外のそれぞれにはボスがいて、規定回数プレイ後にボスのスコアを上回る瞬間が発生すると次のリーグへ進めます。
  • Wood(7/17)
    • Woodリーグはチュートリアルなので、ゲーム勝敗ではなく、問題文に書いてあることをやる必要があります。
    • 提出すると5回ある程度ランダム性を持った状況が生成されて、過半数成功すればクリアなので、ある程度実装できたら細部を詰めずに運頼みで提出しても突破できることがあります。
    • Wood4
      • 移動チュートリアル。
      • 2ユニットをそれぞれ問題文指定の位置に移動するだけです。
    • Wood3
      • 射撃チュートリアル。
      • パッと見は詰んでいる状況ですが、ルールを読むと、一番ダメージを受けている敵を集中攻撃しろとあり、それをこなすと敵が攻撃してこなくて勝ち扱いになります。
    • Wood2
      • 遮蔽チュートリアル。
      • 移動と攻撃を一度に両方実行します。移動は高い遮蔽に隠れるように行い、射撃は近くの敵2体のうち遮蔽が低い方を打てばクリアです。
    • Wood1
      • ボムチュートリアル。このリーグでは射撃が禁止されています。
      • 4隅に敵が大量に3x3領域に囲われて配置されており、1箇所だけ味方も一緒にいるので、味方がいない3箇所の3x3の中央にボムを1回ずつ投げ込みます。
      • 攻略に複数ターンかかる前提になるので、射程に入るまでボムを無駄打ちしないように。投下地点候補4択それぞれに対応する投擲地点候補は決め打ちで大丈夫なので、あとは味方との距離チェックして3択に絞り、そこに対応する地点まで順番に移動して投げ込めばOKです。
    • 1時間ちょっとでWoodリーグ終了。ブロンズRTA8位。
      • bronze_rta.png
  • Bronze(7/17)
    • ここから本来のルールで固定されます。
    • これ以降の対ボス攻略は出現時点で実力差離れすぎていたので正確な情報ではないです。
    • Wood1の特殊な実装をやめて、敵陣へ向かって移動しつつ攻撃していればボム投げを実装していなくてもおそらく十分です。
    • 射撃クールダウン1以上の時は防御しましょう。
    • Javaコードを読んでシミュレータを実装。
  • Silver(7/21)
    • 複数巻き込みを受けないように味方同士の距離をある程度空けました。
    • スコア計算を実装し、最終版一騎打ちのエリア確保に向けた動きがまともになりました。
    • プール各々を山登りしてminimaxという基本方針実装。
    • ボムを確定投げに限定。
    • SNIPERをタンクとして運用し、GUNNERを下がらせる方針に変更。
    • このあたりで順位表1位にちょくちょくなれるようになりました。
      • silver_ranking.png
    • シルバーボスは平均的なコンテストのシルバーボスよりはちょっと強い印象があるので、これさえやっておけばOKという単純な領域ではなさそうです。
  • Gold(7/23)
    • 遮蔽されない攻撃位置としての最短経路などを実装。
      • shortest_path_to_camper.png
    • GUNNERや、大ダメージ受けているユニットの遮蔽隠れを重視。
    • ボムの上位互換変換、衝突込みの確定投げを実装。
    • ゴールドボスはLegendリーグ解放直後の1時間ほどは全一のコピーでそのあと上位10%程度の人の提出にチェンジされたんですが、その時点では全一の方とほぼ互角の実力だったのでボスがチェンジされる前に突破できました。ボスになった人自身を除くと唯一突破できた人だったのでコンテスト期間中一番の見せ場でした。
    • 変更後のゴールドボスはLegend到達したい人にとってはかなり狙い目だったみたいです。実際過去最高レベルの多さの160人Legendリーグ進出者が出たみたいです。
  • Legend(7/25)
    • キャラ同士の距離感を調整して孤立突出しづらくなるように。評価関数でボムの危険度をより正確に判断できるようになっていた分、そういう脅威がないときの距離感はSilverリーグ時の調整よりもだいぶ狭くしました。
    • 遠距離ポーク合戦が増えてきたのでSNIPERを以前より引き気味に、短射程は敵陣の高遮蔽に入ったらその地点で引きこもり気味にしました。

おわりに

  • ゲームバランスの感想
    • エリア支配によるルールが待ち戦術を取らせづらくしており、前進するモチベーションを発生させています。HP半分以下になるとmd計算で倍扱いになるというルールのおかげで、普通のゲームでよくある「人数有利=優勢」という原則が当てはまらず、終盤2人vs1人なのに2人側がエリアでコールド負けするということもよくあって、誰を狙うか?のセオリーが確立しないのがよかったです。エリア支配ルールはよく考えられているなという印象でした。
    • Woodリーグはいつもチュートリアルですが、今回は特にチュートリアル成分が高めでゲーム本編と別の勝利条件を求められていることで逆に直感的でない部分があり、離脱率が高いみたいです。
      • 1キャラボムなし遮蔽なし→1キャラボムあり遮蔽なし→1キャラボムあり遮蔽あり ぐらいでよかったんじゃないかと思います。
    • 射撃戦は密集していた方が人数有利作りやすく有利、でもボムの脅威があるのでばらけないといけないというのは良い調整だと思いました。密集すると、キャラ同士交差できないというルールが結構難しいです。私のbotは最終提出でもガンガンぶつかります。
    • 遮蔽ルールは直感的でないですが、やってみると、なるほどこれのあるなしで待ちの敵に対するアプローチの難易度が全然違うので考えられているなと思いました。
    • ユニットの役職のランダム性はもっとあっても良かったかもしれません。
  • 方針について
    • 各所で敵味方非対称にする試みはいい感じに機能していたように見えます。
    • 1状態を見る際に複数プールの評価値を参照するのは労力がプール数倍になってデバッグは大変でした。
  • 最終結果について
    • 過去最高順位更新できてうれしいです!次回もがんばります。

備考

  • この記事は株式会社バンダイナムコ研究所のエンジニア独自の研究成果で、バンダイナムコグループの製品及びサービスやその他開発業務とは関係がございません。そのため、質問やお問い合わせにつきましてもお答えできないことがございますので、予めご了承くださいますようお願い申し上げます。
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?