こんにちは。昨日と同じような今日を過ごしているどこにでもいるしょうもない高校生です。自粛期間に暇を持て余してプログラミングを始め、ついでにAIについて少し勉強したのでヌメロンというゲームのAIを作ってみました。もし指摘があればコメントお願いします。
#ヌメロンについて
まずヌメロンというゲームについてですが、正式名称を「Numer0n」といい、ルールはWikipediaには以下のように記述があります。
####基本ルール
- それぞれのプレイヤーが、0-9までの数字が書かれた10枚のカードのうち3枚を使って、3桁の番号を作成する。カードに重複は無いので「550」「377」といった同じ数字を2つ以上使用した番号は作れない。
- 先攻のプレイヤーは相手の番号を推理してコールする。相手はコールされた番号と自分の番号を見比べ、コールされた番号がどの程度合っているかを発表する。数字と桁が合っていた場合は「EAT」(イート)、数字は合っているが桁は合っていない場合は「BITE」(バイト)となる。
- 例として相手の番号が「765」・コールされた番号が「746」であった場合は、3桁のうち「7」は桁の位置が合致しているためEAT、「6」は数字自体は合っているが桁の位置が違うためBITE。EATが1つ・BITEが1つなので、「1EAT-1BITE」となる。
- これを先攻・後攻が繰り返して行い、先に相手の番号を完全に当てきった(3桁なら3EATを相手に発表させた)プレイヤーの勝利となる。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
特に言及することもないので次に進みます。
#AIの作り方
###方針
まずヌメロンでプレイヤーがコールし得る数字の組み合わせは720個あります。つまり、AIは毎ターンこの720通りの選択肢の中から1つの数字の組み合わせを選ぶことになります。
そこで、ここではその720個に対して、それぞれの数字の組み合わせをコールすることでどれだけ相手の数字の候補が減るかを計算し、コールした結果候補数が最も低くなる数字の組み合わせをコールすることとします。
###実装
#####下準備
ヌメロンでコール可能な数字の組み合わせ720個を相手の数字の候補としてリストに保存します。
#####1ターン目
当たり前ですが、1ターン目は全ての数字の組み合わせが同じ期待値を示すのでランダムな数字をコールします。
その結果(EAT,BITEの組み合わせ)から相手の数字の候補を更新します。
#####2ターン目以降
720個の数字の組み合わせに対して、その数字をコールしたときに結果として残る相手の数字の候補数の期待値をみます。
例として、相手の候補数が400のときにある数字の組み合わせAをコールすることを考えます。
(EAT,BITE) | (0,0) | (0,1) | (0,2) | ・・・ | (3,0) | 合計 |
---|---|---|---|---|---|---|
候補数 | 120 | 200 | 300 | 1 | 720 | |
重複する候補数X | 20 | 40 | 80 | 1 | 400 | |
確率p | 20/400 | 40/400 | 80/400 | 1/400 | 1 | |
X×p | 1 | 4 | 16 | 1/400 | 100 |
このとき、(EAT,BITE)の組み合わせ9通りの結果としてそれぞれ返ってき得る候補数を計算します。これは全ての数字の組み合わせに対して決まるので合計720となります。
次に現在の相手の数字の候補と重複する候補数を計算します。
これがコールした場合の結果として残る相手の数字の候補数ですが、Aについての期待値を求めたいので、Aをコールしたときに9通りの(EAT,BITE)がそれぞれ返ってくる確率を求めます。
これは単純にそれぞれの重複する候補数を400で割るだけです。
求めたいのは重複する候補数なので、それらを確率pとかけて、全て加算します。
この場合、100がAをコールしたときに結果として残る相手の数字の候補数の期待値となります。
これを720通り検証し、期待値が最小値をとる数字の組み合わせをコールします。そのとき、最小値をとる数字の組み合わせが複数あり、その中に相手の数字の候補があった場合はそちらを優先してコールします。
その結果(EAT,BITEの組み合わせ)から相手の数字の候補を更新します。
#####nターン目
リストの要素数が1になったときその数字をコールします。
リストの要素数が2になったときランダムに片方をコールします。
また、相手が先行で、AIの数字を当てたときリストの中からランダムにコールします。
#成績
大体4~6ターンで当てることができました。