simanのブログ

ゆるふわプログラマー。競技プログラミングやってます。Ruby好き

Marathon Match 134 ShearTheSheep 参加記

問題

www.topcoder.com

戦略

  • どの農家がどの羊を追いかけるかの割当を最小費用流問題として解く
  • 対象への最良経路を探索
  • 羊毛のバケツリレーを行う

農家と羊の割当

どの農家がどの羊を追いかけるのかを最小費用流問題として解きます。辺の張り方としては「農家」→「羊」となるようにし、フローの量は「農家の数」とします。

f:id:simanman:20220324211559p:plain:w480

辺のコスト

S →「農家」は 0、「羊」→ T は 0、「農家」→「羊」についてはそれぞれコストを計算するための関数を使って定義を行います。また、各辺のキャパシティは 1 で定義します。

コスト計算では以下の要素を考慮します。

  • 対象との距離
  • 対象の羊毛の量
  • 周辺の羊毛の量
  • 納屋までの距離

対象との距離

対象までの距離をダイクストラ法で求めます。

距離が 4以下の「羊」については「壁」「木」と同様に通れないオブジェクトとして扱い、それ以上の距離にいる「羊」については距離に応じたペナルティを与えます。(ペナルティの係数は optuna で調整)、「農家」ついては交差したとしても羊毛を受け渡しが可能なので特にペナルティ無しで通過出来るようにしています。

羊毛の量

羊毛の量が MAX の時に成長のタイミングが来ると狩れるはずだったの毛の成長が行われないので実質スコアの減少となります。そのため、羊毛の量が MAX の状態の羊を優先的に狩るようにしました。

周辺の羊毛の量

保持できる羊毛の量 C が多い場合は連続で毛を狩るようにしておくと得点効率が良いので、対象の羊毛の量だけではなく周辺の羊毛の量も調べてそれも評価します。

納屋までの距離

ある程度羊毛を集めると納屋に戻って収める必要があるので、納屋までの距離も評価します。(この評価値はある程度羊毛を集めた農家だけに適用)

「農家」→「納屋」の辺を追加

ある一定量の羊毛を集めた農家に対しては「納屋」への辺の追加を行います。「納屋」→ T のコストは 0、 キャパシティは無限(農家の数より大きな値であれば何でも良いです)で定義し、「農家」→「納屋」のコストについてはダイクストラで計算した値に設定します。

f:id:simanman:20220324215816p:plain:w480

対象への最良経路を探索

目的への経路をダイクストラ法で探索します。「羊」との距離を詰める場合は、羊側の行動によって距離が短くなる候補が多くなるように行動します。(基本的にはY軸とX軸の長い方を短くするような動きになります)

羊毛のバケツリレー

納屋の数が少ないケース等では羊毛を収めに来た農家が詰まってしまうので、詰まった状態でも羊毛を納屋に収めれるようにします。

f:id:simanman:20220324235644p:plain:w320

f:id:simanman:20220324235723p:plain:w320

f:id:simanman:20220324235835p:plain:w320

f:id:simanman:20220324235902p:plain:w320

農家の行動順を入れ替えて納屋までの羊毛の受け渡しを 1ターンで終了させるようにすることで詰まること無く羊毛を収めることが出来るようになります。意図的にリレーを行うというよりかは詰まった時にこの機能が無いと不便だよねという感じです。

感想

2次元グリッドベース + マルチエージェント系の問題。スコア計算が線形的なとの「これをやれば爆発的にスコアが伸びる」みたいなのも特に無かったので、基本的な実装は前半で終わってしまって、後半は地味なチューニング作業が多くなってしまった。焼鈍系の問題が減った代わりにオンライン系の問題が増えてきた印象がある。いい感じの評価値もといコスト計算を行う方法が全然わからないので、機械学習でその辺りを改善できるなら実装していきたい気持ちがある。

optuna 今までは Best parameters しか見てなかったけど、今回は Visualization 機能でパラメータとスコアの関係を俯瞰的に見れて便利だったので今後も機会があれば使っていきたい。(本音はチューニングじゃなくて方針で差がつく問題をやりたい)

Seed  1: 251
Seed  2: 6048
Seed  3: 4250
Seed  4: 403
Seed  5: 1179
Seed  6: 187
Seed  7: 1997
Seed  8: 639
Seed  9: 1406
Seed 10: 382

第8回 Asprova プログラミングコンテスト 参加記

問題

atcoder.jp

方針

  • 製造する製品の(形状、色、生産個数)をまとめて タスク という形にまとめる。
  • タスク を行う順番を焼きなましで更新していく
  • 順番が確定した後 生産個数 の部分を別の焼きなましで改善していく

タスクの作成

製品を 1個ずつ扱うと計算量が大きくなってしまうのと、基本的に切り替えのコストが大きいのである程度製品をまとめて生産するほうが良くなります。

f:id:simanman:20220313221455p:plain:w480

今回は初期の生産個数を N[s][c] / K[s] と定義してタスクの生成を行いました。

焼きなましによる生産順番の改善

焼きなましによって生産の順番を改善していきます。遷移としては以下の 3つになります。

  • 任意の 2つのタスクの順番の入れ替え (swap)
  • 2-opt
  • 任意の 1つのタスクの位置の入れ替え (insert)

スコア計算ですが1回のループで生産できる個数に制限が存在しているのと、前に設置したハンガーの再利用を考慮すると差分計算が出来ないため、基本的には再計算を行っています。 また、スコアを厳密に計算してしまうと必要な試行回数を稼げないためある程度簡易化したものを利用しました。(簡易版で試行回数 2M 程度)

簡易版では「再利用できるかどうか」の判定を簡略化しており、大まかに「前に設置した形状 s とどれだけ間隔が離れているか」を評価の基準としています。(間隔が H に近ければ近いほど評価しています。)

f:id:simanman:20220313223507p:plain:w480

また、個数制限についても「直前に設置したタスクの生産個数との和が制限を超えているかどうか」だけで判定しており、この辺りの精度も落としています。

ただ、ベストスコアに関しては完全版のスコア関数を利用して比較を行います。

生産個数の焼きなまし

順番を確定させた後に、生産個数について別で焼きなましを行いました。一緒にまとめてやってしまうと非常に複雑化してしまって全然安定しなかったので「順番」→「個数」と別々で行っています。遷移としては「任意の製品を選び生産個数を変動させる」というものになります。

f:id:simanman:20220314041146p:plain:w480

ここでのスコア関数は完全版を使用しています(制限時間は 0.2 sec 程度で試行回数が 20K 程度)。

感想

焼きなましの試行回数が全然稼げず終わってしまいました。簡易版を使用して誤魔化したのですがそれでもまだ足りなかったです。 「切り替えコストを少なくして、なるべくハンガーを再利用出来るようにする」と頭の中では理解していたのですが、結局それをどう評価していけばよいのかがわからず終わってしまいました。良い評価関数を作るにあたってデータ集めと解析を怠ってしまったのは個人的には反省です。

ラソンマッチにおいては「改善できなくなったら負け」なので、継続的にスコアを改善できるような仕組みを考えていきたいですね。

AHC008 Territory 参加記

AHC008 に参加していました。

atcoder.jp

方針

大きく分けて「柵を配置する」「動物を捕まえる」の 2つのフェーズでゲームを進めていきます。

  • 複数人で 1つのチームを作ってチーム単位で行動を行う
  • 部屋の中に予め設定しておいた柵を配置する
  • チーム単位で動物の捕獲を行う

f:id:simanman:20220228054037g:plain:w480

チームの作成

1人でペットを追いかけても捕獲するのが非常に困難なので、複数人で対象の動物を捕獲します。人間の数にもよりますが基本的には 2チームに分かれて捕獲を行います。

f:id:simanman:20220227234400p:plain:w480

f:id:simanman:20220227234638p:plain:w480

人間の数によってチームのメンバー数と編成数は変化します。

  • 5人 (3, 2)
  • 6人 (3, 3)
  • 7人 (4, 3)
  • 8人 (4, 4)
  • 9人 (4, 3, 2)
  • 10人 (4, 3, 3)

柵の配置

今回はグリッドサイズが 30 * 30 で固定だったので、前もって柵を配置する場所を決めておいてそれに元に柵の配置を行います。 今回自分は全部で 11個の配置パターンを用意しました。

基本的には図のような配置になります

f:id:simanman:20220227204949p:plain:w480

ポイントとしては主に以下の 2点になります

  • 人が横移動するだけで捕獲が可能
  • 犬、猫を捕獲するための通路の作成

その他の配置パターン M = 5

f:id:simanman:20220228010722p:plain:w480

M = 6

f:id:simanman:20220228010900p:plain:w480

M = 7

f:id:simanman:20220228011335p:plain:w480

Mが小さいケースでは 4ラインだと人が足りなくなってしまうので 3ラインに減らしています。 また、M が 7以下の場合は 1回の横断で柵を設置しきれないため、柵の数を少なくしてなるべく早く設置出来るようにしています。

M >= 8 かつ動物の数 <= 12

f:id:simanman:20220228054436p:plain:w480

人間の数が多くて動物の数が少ない場合は柵を多少減らしても達成率に影響が無いので、なるべく少ない柵の設置で目標が達成出来るようにパターンを修正しています。

横移動のみでの捕獲が可能

柵の設置が終わると水色のラインに人を割り当て、後は対象のペットに対して横軸を合わせるだけで捕獲が可能になります。

f:id:simanman:20220227225856p:plain:w480

チームは横軸を合わせて行動することでペットが捕獲範囲に入ってきた際にすぐに捕獲出来るようになります。

f:id:simanman:20220227235928p:plain:w480

その他の配置箇所

M = 5 (3人, 2人)

f:id:simanman:20220228012542p:plain:w480

M = 6 (3人, 3人)

f:id:simanman:20220228012633p:plain:w480

M = 7 (4人, 3人)

f:id:simanman:20220228012822p:plain:w480

M = 8 (4人, 4人)

f:id:simanman:20220228014643p:plain:w480

M = 9 (4人, 3人, 2人)

f:id:simanman:20220228014812p:plain:w480

M = 10 (4人, 3人, 3人)

f:id:simanman:20220228014905p:plain:w480

犬、猫の捕獲

今回の問題では完全にランダムではない行動を行う動物として「犬」と「猫」の 2つが用意されていました。 これらの動物は最初の行動が「目的地に近い方向に移動」というものなので、通路を用意してあげればそこに勝手に入って来てくれて捕獲することが出来ます。

f:id:simanman:20220228020956p:plain:w480

乱数次第ではずーーーっと横移動だけを行い通路を使ってくれないパターンもあるとは思いますが、基本的にはそのようなことは発生しないので特に問題はありませんでした。

通路の長さは 4以上を確保する

「犬」「猫」「豚」に関しては移動距離が 2であり、 移動前と移動後の (x + y) の偶奇が変化しないので最小効率の 3の通路で捕獲しようとすると捕まらない可能性があるのでそれを避けるために 4以上の長さを確保する必要があります。

f:id:simanman:20220228022115p:plain:w120

この場合、緑マスに止まる「犬」「猫」「豚」は絶対に捕獲することが出来ません。

f:id:simanman:20220228022323p:plain:w120

長さを 4以上にすることでどちらの場合でも捕獲可能なマスが存在するようになります。

通路から出ようとしている「犬」「猫」は積極的に捕まえに行かない

例えば図のような状況下で最初の行動が下方向だった場合、絶対に通路内に入ってくることは無いので捕獲対象としての優先度を下げるようにします。

f:id:simanman:20220228024814p:plain:w120

その場待機 or 通路からの脱出のいずれかの行動になるので追ってもあまり意味がないです。

f:id:simanman:20220228025008p:plain:w120

通路に入ろうとする「犬」「猫」は捕まえに行く

図のように通路の中に入っていく動きをしている場合は高確率で通路内に侵入してくる可能性があるので捕まえに行きます。

f:id:simanman:20220228025745p:plain:w120

最後の 1匹を無理やり捕まえに行く

今回のスコア計算ではペットが 1匹残っているだけでスコアが半分になってしまうので、最後の 1匹に関しては無理やり捕まえに行きます。

その時に使用する捕獲のパターンです。(画像は M >= 8 の時に使用するもの)

f:id:simanman:20220228030957p:plain:w480

赤いマスの部分は最後に部屋を閉める場所になっています。

最後の捕獲に関してはゴリゴリに if 文を書いてるのでかなり無理矢理な実装になってます。

f:id:simanman:20220228031600g:plain:w480

牛が全然捕まってくれず最後に捕まえにいっている様子がわかると思います。

「最後の 1匹ではなく 2匹だともっとスコアの取りこぼしが少ないのでは?」と思ったのですが、この方針だと捕獲までにかかる柵の数と閉じ込めた際の空間の消費量が「待ち」の戦略に比べて増えてしまうので、全体的なスコアとしては下がってしまったので採用しませんでした。

感想

今回は比較的得意な分野(二次元グリッド + マルチエージェント)だったので、特に大きく方針を外すことなく実装出来たかなとは思います(1位のスコアの 9割が毎回の目標値 - 今回だと20位辺り)。柵の配置を固定ではなくて不定形にすることで 7G あたりのスコアが出ることは確認していたので (seed 0 で確認 77111111)、もう少し期間があればそっちの方針も試していたのかなとは思います。(不定形だと犬 & 猫の捕獲は難しいので「犬」「猫」「その他」で戦略を切り替える。このあたりを実装してきて 7G ぐらいを出す人が出てもおかしくないなとは思ってました)

f:id:simanman:20220228045142g:plain:w480

もし他のヒューリスティックコンテストに興味があるという方は 3/17 2:00 から Topcoder Marathon Match 134 が始まるので是非参加してみてください。(ただし、こちらに関してはコンテスト期間中の解法や考察に関する言及は禁止となっているので注意が必要です)

使用素材

サラリーマン(事務職) | ドット絵ダウンロードサイト DOTOWN

茶色のネコ | ドット絵ダウンロードサイト DOTOWN

Hitachi Hokudai Lab. & Hokkaido University Contest 2021 の感想

atcoder.jp

に参加してました。と言っても最低限のコードを書いて終わったのでガッツリ参加したというわけではありませんが、 全体を通しての感想を残しておきます。(どちらかというと要望)

f:id:simanman:20220205014242p:plain:w800

運営も自虐するぐらいにはグダっていたと思います。

共通

アナウンス無しの延期は止めてほしい

  • OO 日に更新予定です。 -> OO 日に特に何も無し
  • XX 日に公開予定です。 -> XX 日に特に何も無し

このパターンが多かったです。当日でも良いので延期のアナウンスがあったほうが嬉しいです。(アナウンス無しは「もしかして放置されている?」と不安になります)

ジャッジプログラムや問題文の更新が多い

正直多すぎました。特にコンテストの序盤とかはちょっとジャッジプログラムを読み進めるだけでもすぐ問題文との矛盾点が見つかるぐらいには多かったです。4000行近くのジャッジプログラムだったので不具合が多いのは仕方ないと思いつつ、コンテスト終了直前まで定期的に修正が入るのはさすがにきつかったです。

ジャッジプログラムが読みにくい

不要な ;if (false) のようなコードが多く、ジャッジプログラムを読むときには毎回消しながら読んでいました。

エラーメッセージが不親切

コンテストタント側のプログラムが何かしらの制約にひっかかって落ちた時にエラーメッセージ無しに exit(1); だけで終わらせていたので原因を突き止めるのに時間がかかりました。 (自分でエラーメッセージを追加してました)

ジャッジプログラムの計算資源を別で管理して欲しい

今回制限として「実行時間制限: 300 sec」「メモリ制限: 3072 MB」とあったのですが、正直何の指標にもなりませんでした。 というのも今回、コンテスタント側はジャッジ側にクエリを投げることで仮スコアを得ることが出来たのですが、 クエリの処理にかかる「時間」と「メモリ使用量」がこの制限に含まれていました。そのため、コンテスタント側で制限を守っていてもジャッジ側の処理でそれを超えてしまう可能性がありました。

様々なケースが考えられる中でテストケースのジェネレータも無いままジャッジ側のメモリの最大使用量を見積もるのは面倒でした。

「時間」に関しては A 問題だと、グリッド配置を決めたら後はジャッジプログラムが処理するだけなのですが、例えば 280sec 使って配置を決めても、ジャッジ側の処理で 21 sec 以上使ってしまうとアウトなので、コンテスタント側では「この配置だと XX sec 使用する」みたいな予測を行って、余裕をもたせてプログラムを終了させる必要がありました。

コンテスト序盤とかはほぼ何もしてないサンプルコードでも 90 sec 近くかかっていました。(改良版が出て 20 sec ぐらいには下がりました)

テストケースジェネレータの提供もしくは生成方法の擬似コード表記

今回 pretest が 3件だけだったので、システムテストが重要になってくるのですが、システムテストでどのようなパラメータになるのかがちょっとわからなかったです。 例として

(例: 系統から電力を購入することによるペナルティ(alpha_buy_ele やalpha_buy_env)が大きいケース、w_acc, w_trans, w_ele, w_envのいずれか1つが小さくなるケース)

とあり「alpha_buy_elealpha_buy_env は制約内でランダムに決められる値ではないのか」「いずれか1つが小さくなるケース ということは他のパラメータは同一の値なのか」とか色々質問したい事項がありました。

将来的な技術動向や政策動向を考慮し、特定のアセットや条件などが有利になるケースを含むように選択

と書かれてしまうと正直もう個人的にはお手上げでした。

相対スコアの導入

今回大まかに「輸送スコア」「電力スコア」「災害対応スコア」とカテゴリー別のスコアがあったのですが「輸送スコア」で得られるスコアが他のスコアに比べて大きかったので「輸送スコアを得意とする人」と「電力スコアを得意とする人」を比較した時に「輸送スコアを得意とする人」が有利となってしまうので、各スコアを平等に評価するのであれば相対スコアで比較したほうが良いと思いました。

A 問題

サンプルコードの充実化

A問題のサンプルコードが簡素なシェルスクリプトだったのは不親切かなと思いました。 問題自体が多くの入力を受け取る必要があったのでサンプルコードを書くコストもそれなりに高かったと想像できるのですが

なお、初心者向けのサンプルコードを用意しているので、必要に応じて参照して欲しい。

この文言からの

#!/bin/sh

echo end
echo 2
echo 1000 10000
echo 1 3
echo 1 
echo 1 3
echo 1
echo 1561 20000
echo 1 2
echo 1 
echo 1 3
echo 1 
echo 2
echo 1000 100000 1
echo 1561 100000 1
echo submit 9999 1

このコードは少し不親切かなと思いました。最低限入力を受け取るあたりのコードは欲しかった気がします。

車両アルゴリズムの説明

A 問題はグリッドと EV の配置を決めたら後はジャッジ側で事前に用意されたアルゴリズムによって処理が行われていくのですが、この車両アルゴリズムについての説明が一切無いのでジャッジプログラムを読んで理解する必要がありました。

A 問題の目的として「安定した電力供給に加えて円滑に輸送サービスを提供するために適切なナノグリッドの配置を考えて欲しい」とあるので、車両のアルゴリズムについてはある程度出題者側から説明が欲しかったです。

B 問題

ジャッジプログラムに計算資源を取られること以外は特に不満はありませんでした。

Marathon Match 132 BouncingBalls

問題

www.topcoder.com

方針

  • 銃の位置を焼きなましで決める
  • 銃の位置決定後はビームサーチで良い盤面を探す
  • ボールが真ん中に集まるように盤面を作成する

銃の位置を焼きなましで決める (START ~ 1 sec)

まず最初に銃の位置を固定するために焼きなましを行って良さそうな銃の配置を決めます。

遷移としては以下の 1つだけです。

  • ランダムに銃の位置を変化させる

銃の位置が決まったあとは、シミュレーションを行い貪欲にパネルを配置していってスコアを求めます。

貪欲に配置

パネル配置については以下のルールのもと行われます。

新しい空白マスに到達した際に

  • ボールが上方向に進んでいる場合
    • ボールが盤面の左側に存在する場合は / のパネルを設置
    • ボールが盤面の右側に存在する場合は \ のパネルを設置
    • それ以外はランダムにパネルを配置

f:id:simanman:20220120061550p:plain:w240

f:id:simanman:20220120061621p:plain:w240

  • ボールが右方向に進んでいる場合
    • ボールが盤面の上側に存在する場合は \ のパネルを配置
    • ボールが盤面の下側に存在する場合は / のパネルを配置
    • それ以外はランダムにパネルを配置

f:id:simanman:20220120062111p:plain:w240

f:id:simanman:20220120062328p:plain:w240

下方向と左方向については上方向と右方向を逆にしたものになります。

貪欲にパネルを置いていってスコアが高くなる配置を採用します。

ビームサーチで良い盤面を探す (1 sec ~ TIME_LIMIT)

銃の位置を固定したらビームサーチで盤面を探索していきます。

進め方としては以下の方法で行っていきます

  • 盤面のシミュレーションを行う
  • いずれかのボールがまだパネルを配置していないマスに止まるとシミュレーションを止める
  • パネルを配置
  • シミュレーションを再開

パネルの配置

パネルの候補として /, \, . のいずれか 1つを選択します。 配置候補が複数ある場合はすべての組み合わせを試しますが、空白パネルを 2つ以上を含む候補は除きます。

例) 配置候補が 2箇所ある場合のパネル候補一覧

  • /, /
  • /, \
  • /, .
  • \, /
  • \, \
  • \, .
  • ., /
  • ., \

盤面の評価

1つでもボールが盤面から出てしまうとゲームが終了するので、盤面から出ないように中央にボールが集まっている状態を良しとします。 また、各パネルの配置についてボーナス点を与えていきます。いくつか例をあげます

  • 盤面中央に向きを変える配置

f:id:simanman:20220120064446p:plain:w240

  • 盤面中央へと進む配置

f:id:simanman:20220120064807p:plain:w240

また特定の配置にはペナルティを与えています

  • 盤面外に向きを変える配置

f:id:simanman:20220120065117p:plain:w240

  • 盤面外へと進む配置

f:id:simanman:20220120065225p:plain:w240

他にもボールの位置についてもペナルティやボーナスを与えています

f:id:simanman:20220120065532p:plain:w240

赤い部分はペナルティ、緑の部分はボーナスになっています。

他にも色々ありますが、基本的には「盤面中央に向かう変更ならボーナス、盤面外へと進む変更ならペナルティ」といった感じです。

感想

評価関数をうまく作れた人が勝てそうだと思ったので、ビジュアライザをずっと眺めてましたが最後までこれとこれといったアイデアは出てきませんでした。事前に計算した盤面を埋め込む方針も考えましたが、固定パネルとボーナスパネルがかなりノイズになっててそれが出来ない仕組みになっていた気がします。

Seed 1: 198
Seed 2: 42985
Seed 3: 28195
Seed 4: 2748
Seed 5: 285
Seed 6: 1866
Seed 7: 4445
Seed 8: 28274
Seed 9: 46363
Seed 10: 35872

MM131 StopTheElves 感想

問題

www.topcoder.com

最近オンライン問題多くなってきた。

方針

  • プレゼントを囲むように箱を設置する
  • 箱はすぐ設置するのではなくエルフが近づいたときに設置を行う
  • 事前にシミュレーションを行って防衛成功率の低いプレゼントは守らない

プレゼントを囲むように箱を設置

プレゼントの周辺マスをチェックして、このマスに対して凸包を求めます。この操作は毎ターン行います。

f:id:simanman:20211223235843p:plain:w240

f:id:simanman:20211224000523p:plain:w240

f:id:simanman:20211224000842p:plain:w240

求めたマスに対して、それぞれ最短路を求めてマス同士を接続します。

f:id:simanman:20211224001108p:plain:w240

箱はすぐ設置するのではなくエルフが近づいたときに設置を行う

箱を早く設置しようがしまいが結局エルフは箱 or プレゼントを持ち帰るまでフィールドに居続けるので、なるべく右往左往してもらうために箱の設置はギリギリまで待ちます。あと、お金は基本的に保持していたほうがあらゆる事象に対処出来るのでなるべくセーブします。

f:id:simanman:20211224002300p:plain:w240

f:id:simanman:20211224002316p:plain:w240

事前にシミュレーションを行って防衛成功率の低いプレゼントは守らない

実際にクエリを送る前にシミュレーションをある程度回してみて、防衛に成功する確率の低いプレゼントについては守る対象から外します。

感想

エルフ行動の優先順位をもっとうまく利用した守り方もありそうだったけど、そこまで考察 & 実装が出来なかった。ただプレゼントを守るだけではダメなのはスコア差から読み取れたけど、そこから何も進めれなかったので残念。凸包以上に効率的な守り方を見つけられなかったのもダメだった。(木が多いときはもっと効率的な守り方がありそう)

TCO21Final 参加記

TCO21Final に参加してきました。

問題

下の画像のようなトーラス状の N * N フィールドが存在しており、コインを 1つ取ると 100 point、棘を踏むとスコアが半分になります。コインは取得すると消えますが、棘は踏んでも消えません。

f:id:simanman:20211119225829p:plain:w400

プレイヤーは自由に移動できるというわけではなく移動用のサイコロを複数保持しています。(2 ~ 6 個) サイコロの種類は全部で 12種類存在しています。

  • 0 が出るサイコロ (STAY)
  • 1 が出るサイコロ (ONE)
  • 2 が出るサイコロ (TWO)
  • 3 が出るサイコロ (THREE)
  • 4 が出るサイコロ (FOUR)
  • 5 が出るサイコロ (FIVE)
  • 6 が出るサイコロ (SIX)
  • 1 or 3 or 5 が出るサイコロ (ODD)
  • 2 or 4 or 6 が出るサイコロ (EVEN)
  • 1 or 2 or 3 が出るサイコロ (LOW)
  • 4 or 5 or 6 が出るサイコロ (HIGH)
  • 1 or 2 or 3 or 4 or 5 or 6 が出るサイコロ (RANDOM)

1 度サイコロを使用すると、そのサイコロは消費され新たなサイコロを補充します。このとき補充されるサイコロの確率は各サイコロの出目の種類数に比例します。(なので、例えば RANDOM は ONE のサイコロよりも 6倍出やすく、ODD よりも 2倍出やすいといった感じです)

N * N ターンの間でより多くのスコアを取得するように頑張りましょうという問題です。(N * N ターンの経過を待たずとも途中で辞めることも可能です)

方針

最初は効率的にコインを集める方法で最初考えていたのですが、棘が密集している画像を見て「いかに棘を踏まないようにサイコロを振るか」に方針を変えました。

f:id:simanman:20211119232321p:plain:w400

基本方針は貪欲で現時点で保持しているサイコロを振ってみて止まる可能性のあるマスをすべて試して、その後の盤面の評価値の合計が高いものを採用していきます。

評価値としては主に以下のような内容になってます(一部)

  • コインに止まる [+100]
  • 棘に止まる [-score / 2]
  • RANDOM サイコロを消費する [+1000]
  • RANDOM サイコロを安全に消費できるマスに移動する [+100]
  • 棘以外のマスで STAY サイコロを振る [+1000000]

ある程度ゲームを回してみたところ、手持ちが RANDOM サイコロだけになり棘を踏むリスクが高まってゲームを中止するといった流れが多く見られたので、とにかく RANDOM を捨てることに比重を置きました。STAY サイコロは実質手札交換みたいなものなので振れるタイミングでは必ず振るようにしています。

あとは、実際にジャッジ側に投げる前にどの程度のスコアが取れるのかを事前にシミュレーションをしておき、そのシミュレーションの平均スコアを基準にゲームをやめるかどうかの判断を行いました。

やめる基準としては 事前計算した平均スコア * 0.8 のスコアを保持している状態で棘を踏む可能性が発生した際に、ローカルでシミューレーションを複数回してそのスコアの平均値が今よりも大きいのであればゲームを続行、そうでないなら終了という判断をしています。

感想

実装した内容は 2手読みな感じになったが、個人的にはモンテカルロ木探索とか刺さりそうな感じはした。(ただ眠くて頭が回らず実装できなかった) 3手読みは時間制限的に無理で、いい感じに DP で書けるかな?と思ったけどあまりいい感じにスコアは伸びてくれなかった。

関係ない話

ノート PC 1台で参加したけど、画面共有とカメラ ON でかなりのパワーを取られて少しモッサリした環境で参加する必要があったのが辛み。

事前に AWS マシンの提供があったけど結局使ってない、割とハイスペックだったからテスト回すときとか使っとけばよかった感はある。

途中眠すぎて寝てた。

トイレ行きたいときに毎回チャットで連絡して返信を待つのが面倒だった。

途中 AHC006 が開催れていることに気づいて順位表だけ眺めてた。

たまに英語でなにか言われたけど、何を言ってるかわかってなくてとりあえず「OK!!」とか言ってた。間違った返事をしている可能性がある。(大体はテキストでやり取りしてたので多分大丈夫)

色々新鮮で楽しかった。