2
0

Pythonで〇×ゲームのAIを一から作成する その60 弱解決の定義と性質とその判定方法

Last updated at Posted at 2024-03-08

目次と前回の記事

これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

これまでに作成した AI

これまでに作成した AI の アルゴリズム は以下の通りです。

ルール アルゴリズム
ルール1 左上から順空いているマス を探し、最初に見つかったマス着手 する
ルール2 ランダム なマスに 着手 する
ルール3 真ん中 のマスに 優先的着手 する
既に 埋まっていた場合ランダム なマスに 着手 する
ルール4 真ん中 のマスの 優先的着手 する
既に 埋まっていた場合ランダム なマスに 着手 する
ルール5 勝てる場合勝つ
そうでない場合は ランダム なマスに 着手 する
ルール6 勝てる場合勝つ
そうでない場合は 相手の勝利阻止 する
そうでない場合は ランダム なマスに 着手 する
ルール6改 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は ランダム なマスに 着手 する
ルール7 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手の勝利阻止 する
そうでない場合は ランダム なマスに 着手 する
ルール7改 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は ランダム なマスに 着手 する
ルール8 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は、自分の手番勝利できる ように、「自 2 敵 0 空 1」が 1 つ以上 存在する 局面になる着手を行う
そうでない場合は ランダム なマスに 着手 する
ルール9 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は、自分の手番必ず勝利できる ように、「自 2 敵 0 空 1」が 2 つ以上存在する 局面になる着手を行う
そうでない場合は、自分の手番勝利できる ように、「自 2 敵 0 空 1」が 1 つ存在する 局面になる着手を行う
そうでない場合は ランダム なマスに 着手 する
ルール10 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は、自分の手番必ず勝利できる ように、「自 2 敵 0 空 1」が 2 つ以上存在する 局面になる着手を行う
そうでない場合は、以下 の 2 つを 総合的に判断 して着手を行う
  • 自分の手番勝利できる ように、「自 2 敵 0 空 1」が 1 つ存在する 局面になる着手を行う
  • 自分有利になる ように、「自 1 敵 0 空 2」が 最も多い 着手を行う
そうでない場合は ランダム なマスに 着手 する
ルール11 真ん中 のマスに 優先的着手 する
そうでない場合は 勝てる場合勝つ
そうでない場合は 相手勝利できる 着手を 行わない
そうでない場合は、自分の手番必ず勝利できる ように、「自 2 敵 0 空 1」が 2 つ以上存在する 局面になる着手を行う
そうでない場合は、以下 の 3 つを 総合的に判断 して着手を行う
  • 自分の手番勝利できる ように、「自 2 敵 0 空 1」が 1 つ存在する 局面になる着手を行う
  • 自分有利になる ように、「自 1 敵 0 空 2」が 最も多い 着手を行う
  • 相手不利になる ように、「自 0 敵 1 空 2」が 最も少ない 着手を行う
そうでない場合は ランダム なマスに 着手 する

基準となる ai2 との 対戦結果(単位は %)は以下の通りです。太字ai2 VS ai2 よりも 成績が良い 数値を表します。欠陥 の列は、アルゴリズム欠陥 があるため、ai2 との 対戦成績良くても強い とは 限らない ことを表します。欠陥の詳細については、関数名のリンク先の説明を見て下さい。

関数名 o 勝 o 負 o 分 x 勝 x 負 x 分 欠陥
ai1
ai1s
78.1 17.5 4.4 44.7 51.6 3.8 61.4 34.5 4.1 あり
ai2
ai2s
58.7 28.8 12.6 29.1 58.6 12.3 43.9 43.7 12.5
ai3
ai3s
69.3 19.2 11.5 38.9 47.6 13.5 54.1 33.4 12.5
ai4
ai4s
83.0 9.5 7.4 57.2 33.0 9.7 70.1 21.3 8.6 あり
ai5
ai5s
81.2 12.3 6.5 51.8 39.8 8.4 66.5 26.0 7.4
ai6 88.9 2.2 8.9 70.3 6.2 23.5 79.6 4.2 16.2
ai6s 88.6 1.9 9.5 69.4 9.1 21.5 79.0 5.5 15.5
ai7
ai7s
95.8 0.2 4.0 82.3 2.4 15.3 89.0 1.3 9.7
ai8s 98.2 0.1 1.6 89.4 2.5 8.1 93.8 1.3 4.9
ai9s 98.7 0.1 1.2 89.6 2.4 8.0 94.1 1.3 4.6
ai10s 97.4 0.0 2.6 85.6 2.6 11.7 91.5 1.3 7.2
ai11s 98.1 0.0 1.9 82.5 1.9 15.6 90.3 1.0 8.7 あり

最強の AI であるかどうかの判定

前回の記事で説明した、演繹法 を使って、最強の AI定義完全に満たす ような ルール条件AI を 作成 した場合は、間違いなく 最強の AI です。一方、少しでも ヒューリスティック手法 が用いられた ルール条件作られた AI が、本当に 最強の AI であるか を知るためには、検証 を行う 必要あります

作成した AI最強の AI であるか どうかを 検証 するためには、下記の 最強の AI定義通りすべての局面最善手選択するか どうかを 調べる必要あります

最強の AI とは、すべての局面 で、最善手選択する AI のことである。

しかし、下記 のような 理由 で、そのようなことを行うのは 容易ではありません

  • オセロ、勝利、囲碁1などのように、局面の数多すぎる 場合は、現実的 に、すべての局面最善手を選択するか どうかを 調べる ことが 不可能 である
  • AI が、すべての局面最善手を選択する ことを 判定するため には、すべての局面最善手何であるか分かっている必要 がある。それは、最強の AI であるか どうかを 判定するため に、最強の AI必要になる ということであり、矛盾 する

従って、演繹法最強の AI作れない場合 に、ヒューリスティック手法作成された AI最強の AI であるか どうかを 判定する ことは、多くのゲーム では ほぼ不可能 です。ただし、最強の AI であることは わからなくても他の AI比較 して、相対的に強いか どうかであれば、他の AI との 総当たり戦 や、レーティング などの 方法判定 することは 可能 なので、一般的 には それらの方法 で、特定の AI の中 での、最も強い AI判定 します。また、実際 に、それらの方法さまざまな競技用いられています

弱解決 の AI であるかの判定方法

これまで の記事では、〇×ゲーム新しい AI作成 した際に、その 強さを検証 するために、ランダムな AI との 対戦 と、それまで作成 した 最も強い AI との 対戦 を行ってきました。それまで最も強い AI対戦する ことは、新しい AI強さを検証 する上で、直観的理にかなっている思う人が多い と思います。しかし、実際 には、それまで最も強い AI対戦するだけ では、新しく作成 した AI強くなったか どうかを 判定できる とは 限りません

その 理由の一つ は、対戦成績良かった としても、作成した AI偶然 それまでの中で 最も強いの AI弱点つくことができる相性の良い AI である 可能性がある からです。これは 対戦成績悪かった場合 でも同様です。

他の理由 としては、前回の記事検証 を行った、修正後ai11 VS ai10s で行われる 着手の図 を見ればわかるように、強い AI は、ルール条件厳しくなる ために、特定着手 のみを 選択する傾向 があります。分かりづらいと思った方は、修正前ai11s VS ai10s で行われる 着手の図見比べて下さい

そのため、強い AI どうし対戦 を行っても、〇× ゲーム特定の局面 に対する 検証しか行えません。従って、強い AI どうし対戦した際 に、出現しなかった局面 でも、新しい AI最善手選択するか どうかの 検証 を行うことは できません

一方、AI の強さ検証 する際に、明らか弱い AI である、ランダムな AI対戦 することの 必要性疑問を感じた人 はいないでしょうか?ランダムな AI は、平均的 にみると 弱い AI ですが、ありとあらゆる着手行う可能性がある ので、繰り返して 何度も戦う と、その中の いくつかの対戦常に最善手を選択 する 可能性が生じます実際 に、〇×ゲーム のような、局面の種類 があまり 多くないゲーム では、数万回対戦 を行うことで、その中の いくつかの試合 では ランダムな AI最善手のみ選択する ことが 期待できます

〇×ゲーム は、ゲーム開始時局面の状況引き分け の局面であるので、最強の AI であれば、どのような AI対戦 しても、負ける ことは 決してありません。従って、ランダムな AI数多く対戦 した 結果一度負けることがなければ最強の AI である 可能性が高い判断する ことが できます。これが、これまで の記事で、新しい AI作成するたび に、ランダムな AI対戦した これまでに説明していなかった 理由 です。

ランダムな AI数多く対戦する ことで、最強の AI であるか どうかを 判定する という 方法 は、〇×ゲームだけではなく、理論的 には すべて二人零和有限確定完全情報ゲーム適用できます。ただし、厳密 には、判定できる のは、最強の AI であるか どうか ではなく、この後で説明する 弱解決の AI であるか どうかの 判定 です。また、現実的判定できる のは、〇×ゲームのような、局面の数少ないゲーム限られます

今回の記事では、そのようなことができる 具体的な理由方法 について 説明 します。

ゲームの解決とその種類

ゲームある局面 から、お互い最善手のみ選択 した場合の 結果判明している ことを、ゲーム解決された(solved)と呼び、以下の 3 種類分類 されています2

  • 超弱解決(ultra weakly solved)
    ゲームの 開始時局面の状態判明 しているが、その 局面の状況以上結果 になる 合法手選択する方法判明していないゲーム のこと3本記事 では 取り扱いません

  • 弱解決(weakly solved)
    ゲームの 開始時局面の状態判明 しており、どのような相手と対戦 しても、その 局面の状況以上結果 になる 合法手選択する方法判明しているゲーム のこと

  • 強解決(strongly solved)
    すべての局面 で、局面の状況 と、最善手判明しているゲーム のこと

強解決意味簡単 で、最強の AI定義 から、強解決 された ゲームである ということと、最強の AI作れる ということは、同じ意味 を持ちます。

一方、弱解決意味わかりづらい と思いますので、詳しく説明 します。

最強の AI どうし が、実際対戦を行う場合 のことを考えると、すべての局面最善手みつけることができる必要ありません

例えば、ai11s は、ルール 11 の「真ん中 のマスに 優先的着手 する」という 条件 から、〇 を担当 した際に、1 手目 では 下図(1, 1)合法手のみ選択 します。

このことから、ai11sゲームの開始時 から 対戦を行う際 には、1 手目 で、上図真ん中 のような、マス への 着手 が行われる 局面決して生じない ことが わかります。従って、ai11s は、それらの局面最善手選択できる必要ありません

強解決弱解決違い は以下のようになります。

  • 強解決 されたゲームは、すべての局面最善手判明 している
  • 弱解決 されたゲームは、必要な局面のみ で、目的を達成 できる 合法手判明 している

本記事では、弱解決定義を元 に、弱解決の AI以下 のように 定義 します。

弱解決の AI とは、ゲーム開始時局面 から、どのような AI対戦 しても、ゲーム開始時局面の状況以上結果 になる 合法手を選択 する AI のことである。

弱解決の AI という呼び方は、最強の AI区別するため筆者が作った 本記事 独自呼び方 です。おそらく、他では使われていない と思います。

なお、これまで最強の AI のことを、強解決の AI と呼ぶこともできますが、本記事では 弱解決の AI強く区別したい場合除いて、これまで通り 最強の AI表記する ことにします。

弱解決 の AI は、最善手選択できる必要 がある 局面の数 が、最強の AI比べて大幅に少ない ので、最強の AI より簡単に作る ことが できます

実際 に、オセロ は、2024 年 3 月の時点では、強解決されていません が、2023 年 10 月に 弱解決がされた という 論文 が発表 されました。

とはいえ、将棋囲碁 などの、オセロより かなり 複雑なゲーム では、弱解決の AI であっても 作成 することは ほぼ不可能 だと 考えられています

弱解決でよくある誤解

上記で、「弱解決 されたゲームは、必要な局面のみ で、目的を達成 できる 合法手判明 している」と説明しましたが、強解決の説明同様 に、「弱解決 されたゲームは、必要な局面のみ で、最善手判明 している」のように 説明しなかった点気になっている 人はいないでしょうか?筆者最初そのように考えていた のですが、よく考える とその説明では 間違っている のではないかと思いました。ただし、これは 筆者勘違いかもしれない ので、間違っていたらコメントで指摘していただけると嬉しいです。

実際に、「弱解決」を 検索 すると、「ゲーム開始時の局面から、お互い最善手を選択 した場合の 結果がわかっている ゲームのこと」のような 説明いくつか発見 できました。また、下記の論文96 ページ でも、弱解決 を「初期局面の勝敗が解明されていて、それを証明するのに必要な 各局面最善手分かっている」のように説明しています。

しかし、下記の論文の 8 ページ にある 本来弱解決の定義述べられている必要な局面ゲームの開始時局面の状況以上 になるような 合法手選択できる ことと、最善手わかっている ことは 同じではありません

そのことを理解するためには、弱解決の AI性質理解する必要 があるので、その 説明を行いながら弱解決の誤解の意味 を説明します。

弱解決 の AI の性質

二人零和有限確定完全情報ゲーム には、先手後手 があります。そのため、先手を担当 した場合は 弱解決 の AI最強の AI であるが、後手を担当 した場合は そうではない という場合が あります。これは、先手と後手入れ替えた場合同様 です。

一般的 には 弱解決の AI最強の AI は、先手と後手両方その性質を持つ AI のことを 指す ので、本記事 でも、そのような意味 でそれらの 表記を行います

また、片方手番だけ性質を満たす 場合は、先手の弱解決の AI のように、手番つけて表記 することで、区別する ことにします。

次に、弱解決の AI性質 を、ゲームが 先手の必勝引き分け後手の必勝3 つの場合 と、先手後手 を担当する 場合分けて説明 します。

先手の必勝のゲームで、先手を担当する場合

弱解決の AI先手を担当 する場合は、ゲーム開始時局面必勝の局面 です。勝利より良い状況存在しない ので、勝利以上結果勝利だけ です。従って、弱解決の AI の定義 から 弱解決の AI は、この場合は 必ず勝利 する 必要 があります。

以前の記事で説明したように、最善手を選択するか どうかで、局面の状況下記 の表のようになります。この表 から、局面の状況悪化した場合 は、相手最善手選択し続ける と、元の状況戻ることはない ことが わかります。従って、どのような AI に対しても 勝利する ためには、弱解決の AI常に最善手選択 する 必要がある 事が わかります

局面の状況
最善手を選択する 変化しない
最善手を選択しない 悪化する

上記まとめる と以下のようになります。

先手の必勝のゲーム で、ゲームの開始時局面 から 弱解決の AI先手を担当 する場合は、以下 のような 性質 がある

  • 出現 する すべての局面最善手を選択 する
  • 相手どのような AI であっても 100 % 勝利 する
  • 最強の AI同じ強さ を持つ

従って、この場合 は、弱解決の AI は、最強の AI同じ強さ を持ち、「弱解決 されたゲームは、必要な局面のみ で、最善手判明 している」という説明は 正しい説明 です。

先手の必勝のゲームで、後手を担当する場合

ゲームの 開始時の局面先手の必勝の局面 の場合で、後手を担当 する場合は、後手の立場 から見ると、後手の必敗の局面 です。敗北以上結果 は、勝利引き分け敗北 という、すべての結果含みます。従って、弱解決の AI の定義 から 弱解決の AI は、出現 する すべての局面 で、どのような着手選択 しても 構わない ことになります。つまり、この場合 は、すべての AI弱解決の AI である言える ということです。

別の言葉説明 すると、この場合 は、弱解決の AI は、出現 するどの 局面 でも 最善手知る必要がない ということです。従って、この場合は、「弱解決 されたゲームは、必要な局面のみ で、最善手判明 している」という 説明間違っています

一方、最強の AI は、すべての局面最善手を選択 するので、この場合 は、最強の AI弱解決の AI同じ強さ では ありません

具体例としては、先手の必勝の局面 で、先手最善手選択しなかった場合 は、引き分けの局面 または 先手の必敗の局面 になります。後手最強の AI であれば、そのような場合でも 常に最善手を選択 するので、決して 敗北する ことは ありません。一方、弱解決の AIそのような局面最善手を選択 するとは 限らない ので、敗北 してしまう 可能性あります。これが、最強の AI弱解決の AI大きな違い です。

上記まとめる と、以下のようになります。

先手の必勝のゲーム後手を担当 する場合は、以下 のような 性質 がある。

  • 勝利、引き分け、敗北の すべての結果 になる 可能性がある
  • すべての AI後手の場合弱解決の AI である
  • 最善手選択 するとは 限らない点 が、最強の AI とは 異なる

後手の必勝のゲームの場合

後手の必勝のゲーム先手を担当 する場合は、先手の立場 から見ると、先手の必敗の局面 です。後手の必勝のゲーム後手を担当 する場合は、必勝の局面 です。

従って、先手の必勝のゲーム の場合と 同様の考え方それぞれの性質知る ことができ、下記 のように、一つにまとめる ことができます。

なお、『「先手の必勝のゲーム で、先手を担当 する」または「後手の必勝のゲーム で、後手を担当 する」』のような表記は長いので、そのことを 自分の必勝のゲーム のように 表記 することにします。自分の必敗のゲーム同様の意味 です。

自分の必勝のゲーム で、ゲームの開始時局面 から 対戦 を行った場合は、弱解決の AI には 以下の性質 がある。

  • 出現 する すべての局面最善手を選択 する
  • 相手どのような AI であっても 100 % 勝利 する
  • 最強の AI同じ強さ を持つ

自分の必敗のゲーム で、ゲームの開始時 から 対戦 を行った場合は、以下 のような 性質 がある。

  • 勝利、引き分け、敗北の すべての結果 になる 可能性がある
  • すべての AI自分の手番 の場合は 弱解決の AI である
  • 最善手選択 するとは 限らない点 が、最強の AI とは 異なる

引き分けのゲームの場合

ゲーム開始時局面の状況引き分けの局面 場合は、弱解決の AI先手を担当 しても、後手を担当 しても、その 定義 から 結果引き分け以上 になります。従って、この場合は、弱解決の AIどのような AI対戦 しても 敗北する ことは ありません。そのためには、弱解決の AIどのような合法手選択 する 必要があるか について 説明 します。

相手最善手選択し続けた 場合、局面の状況良くなる ことは ない ので、弱解決の AI は、必敗の局面 につながる 合法手選択 しては いけない ことが わかります

従って、引き分けの局面 では、弱解決の AI は、常に 最善手選択する必要あります

弱解決の AI対戦する相手 が、最善手選択しない 場合は、弱解決の AI手番の局面自分の必勝の局面 になる 場合あります。この場合も、弱解決の AI最善手選択 する 必要がある思う人いるかもしれません が、そうではない 点に 注意 して下さい。

その理由は、自分の必勝の局面 から、引き分けの局面 につながるような、最善手ではない 合法手を 選択しても引き分け以上結果 になるという 条件満たされる からです。この点 が、常に最善手選択 する、最強の AI との 違い です。

上記まとめる と、以下のようになります。

引き分けのゲーム で、ゲームの開始時局面 から 対戦 を行った場合は、弱解決の AI には 先手と後手いずれを担当 した場合でも、以下 のような 性質 がある。

  • どのような AI対戦 しても 敗北 する ことはない
  • 自分の必勝の局面 では 必敗の局面 につながる 合法手以外選択 する。この点 が、最強の AI とは 異なる
  • 引き分けの局面 では、最善手を選択 する

弱解決の AI の性質のまとめ

弱解決の AI は、ゲームの開始時局面 から 対戦 を行った際に 生じる局面 に対して、局面の状況 によって、下記 のような 合法手選択 します。なお、空欄 は、そのような局面生じない ことを意味します。

必勝の局面 引き分けの局面 必敗の局面
自分の必勝のゲーム 最善手
引き分けのゲーム 必敗の局面につながる
以外の合法手
最善手
自分の必敗のゲーム 任意の合法手 任意の合法手 最善手4

表から、弱解決の AI は、ゲーム開始時局面の状況同じ状況の局面 では、常に最善手選択 する AI であることが わかります。そのため、相手常に最善手を選択 する場合は、下記の表のように、弱解決の AI最強の AI同じ結果 になります。

弱解決の AI VS 最強の AI 最強の AI VS 最強の AI
自分の必勝のゲーム 100 % 勝利 100 % 勝利
引き分けのゲーム 100 % 引き分け 100 % 引き分け
自分の必敗のゲーム 100 % 敗北 100 % 敗北

一方、試合で 相手最善手以外1 回以上選択 する場合は、以下 のようになります。このことから、弱解決の AI は、相手最善手を選択しない という、ミスつけこむ という 能力を持っていない ということが分かります。

弱解決の AI 最強の AI
自分の必勝のゲーム 100 % 勝利 100 % 勝利
引き分けのゲーム 勝利、引き分け 100 % 勝利
自分の必敗のゲーム 勝利、引き分け、敗北 勝利、引き分け

また、弱解決の AI は、ゲーム開始時局面 から 着手の選択 を行った場合に 出現 する 可能性 のある 局面以外 では、自分の必勝の局面 であっても 最善手を選択 する 能力を持ちません。そのため、例えば 人間どうし が行った 対戦途中の局面 から、弱解決 の AI引き継いで対戦 を行った場合に、その 局面の状況 よりも悪い結果になる場合があります。

最近では、将棋や囲碁の 対局の中継 で、AI による形成判断表示される場合 が良くありますが、そのような 形成判断 は、弱解決目的 として 作られた AI5 では 行えません

ランダムな AI による弱解決の AI の判定

ここまでで説明した、弱解決の AI性質 から、ランダムな AI利用する ことで、原理的 には、AI が 弱解決の AI であるか判定 することが できます 。その理由と手順について少し考えてみて下さい。

弱解決の AI であることの判定手順

具体的には、下記の手順AI弱解決の AI であるか高確率判定 することが できます。なお、条件 1具体的な意味 は、この後で説明します。

  1. 判定したい AIランダムな AI を、先後を入れ替えてすべての AI高確率1 回以上対戦 することが できる ように、十分な数だけ対戦 する
  2. 下記いずれか の場合は、判定する AI弱解決の AI であり、いずれの条件満たさない 場合は、弱解決の AI ではない
    • 先手 または、後手いずれかの場合勝率100 % になる
    • 先手後手 のいずれも 敗率0 % になる

このような、乱数(ランダムなもの)を使って 問題を解決する方法 の事を モンテカルロ法 と呼びます。モンテカルロ法 は、ヒューリスティック手法の一種 なので、100 % 正しい かどうかは 判定できません。従って、上記では 高確率 という 表現 を行いました。

モンテカルロ法 については、AI を作成する方法 の一つとして、今後の記事取り扱う予定 です。参考までに、モンテカルロ法の Wikipedia のリンクを下記に示します。

上記の方法判定できる理由 を少し考えてみて下さい。

ランダムな AI の性質

AI弱解決な AI であるか判定するため には、定義 から どのような AI対戦 しても、ゲーム開始局面の状況 よりも 悪化しない ことを 確認する必要 があります。

単純考えると、そのようなことを 判定する ことは 難しい思えるかも しれませんが、ランダムな AI利用する ことで そのような判定 を行うことが できます。その 理由ランダムな AI以下性質 にあります。

ランダムな AI は、すべての局面 で、すべての合法手選択 する 可能性がある AI です。従って、ランダムな AI は、世の中存在 する すべての AI選択する着手 と、まったく同じ着手選択する可能性 があります。そのため、ランダムな AI何度も対戦する ことで、理論上すべての AI1 回以上対戦 することが できます。また、その中 には ランダムな AIすべての着手 が、最強の AI選択 する 合法手と同じ である 試合 があります。

これは、下手な鉄砲も数撃てば当たる という、でたらめな行動 であっても、何回も行えば、偶然 うまくいく場合がある という意味の ことわざ に相当します。

ただし、これはあくまで 理論上の話 で、オセロ、将棋、囲碁といった、局面の数多いゲーム では、現実的 では ありません。その 理由 は、すべての AI1 回以上対戦 するということは、その AI対戦した際出現 する可能性がある すべての局面 が、何度も行う対戦 の中で 1 度は出現 する 必要がある ということを 意味 するからです。

例えば、オセロ は、一回の対戦 で、基本的には 60 回の着手 を行うので、約 60 種類局面 しか 出現しませんオセロ は、局面の数 が $10^{28}$ もある といわれているので、最低 でも $10^{28}$ / 60 = 約 $10^{26}$ 回以上対戦行わなければすべての局面出現しません。従って、オセロでは、ランダムな AI対戦 することで、すべての AI1 回以上対戦 するためには、現実的でないほど の、途方もない回数対戦行う必要あります

上記 を、サイコロ を使った 例え説明 します。サイコロ2 つ振った 場合の 合計 は、2 ~ 12 までの 11 種類 があります。これを ゲームの局面対応 させて考えてみて下さい。サイコロの出目ランダム なので、サイコロ2 つ2 ~ 12 までの すべての出目出現するまで振る ことは、すべての局面出現するまでランダムな AI と対戦 することに(全く同じではありませんが)似ています

プログラムの説明は省略しますが、下記2 ~ 10 個までサイコロを振った 際に、何回すべての種類出目が出現するか数えて表示 するプログラムです。

プログラム(興味がある方はクリックしてみて下さい)
from random import randint

def dice(num):
    """ num 個のサイコロの合計を返す """
    total = 0
    for _ in range(num):
        # randint は実引数で指定した範囲のランダムな整数を返す
        total += randint(1, 6)
    return total

def countall(num):
    """ num 個のサイコロを振った場合に、すべての出目が出るまでの回数を返す"""
    
    # 出現した出目を記録する set
    s = set()
    # 出目の種類を計算する。最小値が num で 最大値が num * 6
    left = num * 5 + 1
    # サイコロを振った数を数える変数を初期化する
    count = 0
    # 全ての出目が出るまで繰り返す
    while left > 0:
        # サイコロを振る
        dicenum = dice(num)
        # 振った回数を増やす
        count += 1
        # s の要素に登録されているかを判定する
        if dicenum not in s:
            # 登録されていなければ、left を 1 減らし s に出目を登録する
            left -= 1
            s.add(dicenum)
    return count

# 2 ~ 10 個のそれぞれの場合の回数を表示する
for i in range(2, 11):
    print(i, countall(i))

実行結果(実行結果はランダムなので下記とは異なる場合があります)

2 45
3 333
4 3746
5 2333
6 99256
7 833719
8 4610084
9 10782768
10 51201303

実行結果 からわかるように、サイコロの数増える と、すべての出目出現するまで回数大幅に増えていき10 個振った場合約 5000 万回 もかかることが わかります。なお、筆者のパソコン では、上記実行約 5 分 かかりました。

一方、〇×ゲーム は、局面の数5478 のように6 少ない ので、1万回程度ランダムな AI との 対戦 で、高確率 で、すべての AI1 回以上対戦 することが できます

判定手順で、弱解決の AI であることを判定できる理由

次に、先程示した、下記判定手順 で、弱解決の AI であるか判定 することができる 可能性が高い理由 について 説明 します。なお、以降の説明 では、実際に すべての AI1 回以上対戦できた ことを 前提説明 します。運悪く、その 前提満たされなかった場合 は、弱解決の AI であると 正しく判定できない 場合が あります。それが、上記で「判定 することができる 可能性が高い」と表記した 理由 です。

  1. 判定したい AIランダムな AI を、先後を入れ替えてすべての AI高確率1 回以上対戦 することが できる ように、十分な数だけ対戦 する
  2. 下記いずれか の場合は、判定する AI弱解決の AI であり、いずれの条件満たさない 場合は、弱解決の AI ではない
    • 先手 または、後手いずれかの場合勝率100 % になる
    • 先手後手 のいずれも 敗率0 % になる

下記は、弱解決の AI が、相手最善手のみを選択 する場合と、そうでない場合試合の結果 をまとめた表です。

相手が最善手のみを選択 そうでない場合
自分の必勝のゲーム 100 % 勝利 100 % 勝利
引き分けの ゲーム 100 % 引き分け 勝利、引き分け
自分の必敗のゲーム 100 % 敗北 勝利、引き分け、敗北

自分の必勝のゲーム とは、先手が必勝 のゲームで 先手を担当 する場合か、後手が必勝 のゲームで 後手を担当 する 場合 のことを表します。

自分の必敗のゲーム とは、先手が必勝 のゲームで 後手を担当 する場合か、後手が必勝 のゲームで 先手を担当 する 場合 のことを表します。

そこで、先手の必勝後手の必勝引き分けのゲーム分けて説明 することにします。

先手の必勝のゲーム

弱解決の AI先手を担当 する場合は、上記の表から 100 % 勝利 します。一方、弱解決の AI では ない AI の場合は、その 定義 から、ゲーム開始時局面の状況 よりも 悪化する場合がある ので、すべての AI対戦 を行った場合は、いずれかの対戦勝利できない 場合が あります。従って、勝率100 %なりません

下記 は、上記の考察まとめた表 です。

勝率 引き分け率 敗率
弱解決の AI 100 % 0 % 0 %
弱解決の AI ではない 100 % 未満 0 ~ 100 % 0 ~ 100 %

先手の必勝のゲーム で、後手を担当 した場合は、先程説明したように、すべての AI後手の場合弱解決の AI になります。従って、先手の必勝 のゲームでは、弱解決の AI であるか どうかは、その AI先手を担当 した 場合のみ判定すればよい ことが分かります。

後手を担当 した場合に、相手最善手を選択 した場合は、敗北する ので、敗率必ず 0 % より大きく なります。下記 は、上記の考察まとめた表 です。

勝率 引き分け率 敗率
後手を担当するすべての AI 0 ~ 100 % 未満 0 ~ 100 % 未満 0 % より大きい

下記は、先手後手勝率敗率関係 をまとめたものです。 は、どちらの可能性もある 事を意味します。

弱解決の AI 弱解決でない AI
先手 の場合に 勝率100 % になる ×
後手 の場合に 勝率100 % になる × ×
先手 の場合に 敗率0 % になる
後手 の場合に 敗率0 % になる × ×

上記の表 から、先手の必勝のゲーム の場合は、下記の 判定方法正しい ことが分かります。なお、下記の 2 つ目の条件 は、引き分けのゲーム の場合に 必要となる条件 であり、先手の必勝のゲーム の場合は 常に満たされません

下記いずれか の場合は、判定する AI弱解決の AI であり、いずれの条件満たさない 場合は、弱解決の AI ではない

  • 先手 または、後手いずれかの場合勝率100 % になる
  • 先手後手 のいずれも 敗率0 % になる

後手の必勝の場合

先手後手入れ替えて考える ことで、上記同じ考察 ができるので、後手の必勝 の場合も、上記の 判定方法正しい ことが分かります。

引き分けのゲーム場合

引き分けのゲーム の場合は、先手後手どちらを担当 しても、ゲーム開始時局面の状況引き分けの局面 です。従って、以下の説明は、先手を担当 した場合と 後手を担当 した場合の 両方当てはまります

引き分けのゲーム の場合は、弱解決の AI定義 から、どのような AI対戦 しても、ゲーム開始時局面の状態 よりも 悪化することはない ので、敗北 することは ありません。一方、弱解決の AI では ない AI の場合は、ゲーム開始時局面の状態 よりも 悪化する場合がある ので、敗率0 % より大きくなります

また、引き分けのゲーム の場合は、相手最善手のみ着手 した場合に、必ず引き分けになる ので、勝率100 % になることは ありません

下記は、先手後手両方の場合勝率敗率関係 をまとめたものです。

弱解決の AI 弱解決でない AI
勝率100 % になる × ×
敗率0 % になる ×

上記の表 から、引き分けのゲーム の場合は、下記の 判定方法正しい ことが分かります。

下記いずれか の場合は、判定する AI弱解決の AI であり、いずれの条件満たさない 場合は、弱解決の AI ではない

  • 先手 または、後手いずれかの場合勝率100 % になる
  • 先手後手 のいずれも 敗率0 % になる

なお、先手の場合敗率0 % になっても後手の場合敗率0 % にならなければ弱解決の AI では ない 点に注意して下さい。

以上で、先手の必勝後手の必勝引き分けすべての場合 で、上記の手順弱解決の AI であるか どうかを 高確率判定 することが できる ことが わかりました

繰り返しになりますが、上記判定方法 は、オセロ、将棋、囲碁のような、局面の数が多いゲーム では 利用できない 点に 注意 して下さい。その 理由 は、局面の数多い場合 は、ランダムな AI と対戦することで、すべての AI1 回以上対戦 することができる 可能性ほぼ 0 になる からです。

ランダムな AI との対戦による ai11s の検証

実際ai11sランダムな AI である ai2対戦 させることで、ai11s弱解決の AI であるか判定する ことにします。その際に、以前の記事で、ai_match を、仮引数 paramsAIパラメータ代入 するように 修正 したので、以前の記事 で説明したように、下記のプログラムのように 記述する必要 がある点に 注意 して下さい。

from ai import ai_match, ai2, ai11s

param_ai11s = {
    "score_201": 2,
    "score_102": 1,
    "score_012": -1
}
ai_match(ai=[ai11s, ai2], params=[param_ai11s, {}])

実行結果(実行結果はランダムなので下記とは異なる場合があります)

ai11s VS ai2
count     win    lose    draw
o        9879       0     121
x        8767     236     997
total   18646     236    1118

ratio     win    lose    draw
o       98.8%    0.0%    1.2%
x       87.7%    2.4%   10.0%
total   93.2%    1.2%    5.6%
関数名 o 勝 o 負 o 分 x 勝 x 負 x 分
ai9s 98.7 0.1 1.2 89.6 2.4 8.0 94.1 1.3 4.6
ai10s 97.4 0.0 2.6 85.6 2.6 11.7 91.5 1.3 7.2
修正前 の ai11s 98.1 0.0 1.9 82.5 1.9 15.6 90.3 1.0 8.7
修正後 の ai11s 98.8 0.0 1.2 87.7 2.4 10.0 93.2 1.2 5.6

上記は、ai9sai10s修正前修正後ai11s のそれぞれが ai2対戦 した場合の 対戦結果 の表です。〇×ゲーム は、ゲーム開始時局面の状態引き分けのゲーム なので、 から 以下 のようなことが わかります

  • ai9s〇 を担当 した場合も、× を担当した 場合も、弱解決の AI ではない
  • ai10s修正前修正後ai11s は、〇 を担当 した場合は、敗率が 0 % なので、〇 を担当 した場合のみ 弱解決の AI である 可能性が高い。従って、ルール 10ルール 9 に追加 した 条件 によって、〇 を担当 した場合は 弱解決の AI になった
  • いずれの AI も、× を担当 した場合に 敗率0 % ではない ので、弱解決の AI ではない

次に、ai11s〇 を担当 した場合に 弱解決の AI ではない ことの 理由検証 し、ai11sルール改善 することにします。

ai_match などの改良

ai11s〇 を担当 した場合に 弱解決の AI ではない ことの 理由検証 する前に、このままでは、ai_match使いづらい ので 改良 することにします。

ai_match の問題点

先程は、下記 のような プログラム で、ai_match呼び出し ました。ai11sパラメータ は、AIルールの修正行う際 には、頻繁に変更 しますが、最適 と思われる パラメータ決まった後 は、頻繁に変更 することは ありません。そのため、パラメータ決まった後 でその AI で 対戦を行う際 に、下記のプログラムのように、毎回パラメータ実引数に記述 するのは かなり面倒 です。

param_ai11s = {
    "score_201": 2,
    "score_102": 1,
    "score_012": -1
}
ai_match(ai=[ai11s, ai2], params=[param_ai11s, {}])

下記 のプログラムのように、ai_match と同様 に、params記述しなくても AI どうしの 対戦を行える便利 だとは思いませんか?下記のように記述できるようにするための方法を少し考えてみて下さい。

ai_match(ai=[ai11s, ai2])

ai11s の修正

ai_matchparams記述 した パラメータのデータ は、マッピング型の展開 によって、ai11s呼び出される際 に、キーワード引数 として 記述 されます。従って、ai11sパラメータ を代入する 仮引数 を、調整済みのパラメータデフォルト値 とする、デフォルト引数変更 することで、ai11s対戦する際 に、毎回 調整済のパラメータ記述 する 必要なくなります下記 は、そのように ai11s を修正 した プログラム です。

  • 4 行目評価値を計算 する際に 利用 する パラメータ代入 する 仮引数 を、調整したパラメータデフォルト値 とする、デフォルト引数修正 する
1  from marubatsu import Marubatsu, Markpat
2  from ai import ai_by_score
3
4  def ai11s(mb, score_201=2, score_102=1, score_012=-1, debug=False):
5      def eval_func(mb):  
元と同じなので省略
行番号のないプログラム
from marubatsu import Marubatsu, Markpat
from ai import ai_by_score

def ai11s(mb, score_201=2, score_102=1, score_012=-1, debug=False):
    def eval_func(mb):      
        # 真ん中のマスに着手している場合は、評価値として 300 を返す
        if mb.last_move == (1, 1):
            return 300
    
        # 自分が勝利している場合は、評価値として 200 を返す
        if mb.status == mb.last_turn:
            return 200

        markpats = mb.count_markpats()
        if debug:
            pprint(markpats)
        # 相手が勝利できる場合は評価値として -100 を返す
        if markpats[Markpat(last_turn=0, turn=2, empty=1)] > 0:
            return -100
        # 次の自分の手番で自分が必ず勝利できる場合は評価値として 100 を返す
        elif markpats[Markpat(last_turn=2, turn=0, empty=1)] >= 2:
            return 100

        # 評価値の合計を計算する変数を 0 で初期化する
        score = 0        
        # 次の自分の手番で自分が勝利できる場合は評価値に score_201 を加算する
        if markpats[Markpat(last_turn=2, turn=0, empty=1)] == 1:
            score += score_201
        # 「自 1 敵 0 空 2」1 つあたり score_102 だけ、評価値を加算する
        score += markpats[Markpat(last_turn=1, turn=0, empty=2)] * score_102
        # 「自 0 敵 1 空 2」1 つあたり score_201 だけ、評価値を減算する
        score += markpats[Markpat(last_turn=0, turn=1, empty=2)] * score_012
        
        # 計算した評価値を返す
        return score

    return ai_by_score(mb, eval_func, debug=debug)
修正箇所
from marubatsu import Marubatsu, Markpat
from ai import ai_by_score

-def ai11s(mb, score_201, score_102, score_012, debug=False):
+def ai11s(mb, score_201=2, score_102=1, score_012=-1, debug=False):
    def eval_func(mb): 
元と同じなので省略

上記の修正正しく動作するか確認 するために、下記のプログラムで、パラメータ実引数記述せず に、ai11s を呼び出し てみます。実行結果 から、パラメータ記述しなくてもai11s呼び出せる ことが 確認 できます。

mb = Marubatsu()
print(ai11s(mb))

実行結果

(1, 1)

また、上記の修正 により、ai_match対戦を行う際 には、params対応する要素 には、下記のプログラムのように、空の dict記述するだけで済む ようになります。なお、実行結果は、先程と変わらないので省略します。

ai_match(ai=[ai11s, ai2], params=[{}, {}])

ai_match の修正

デフォルト値パラメータ を使って、ai_match対戦を行う 際に、上記のプログラムのように、params要素空の dict記述 するのも 面倒 です。そこで、下記のプログラムのように、ai_match仮引数 params を、デフォルト値[{}, {}]デフォルト引数 にすることで、params に対応する 実引数省略できる ようになります。

from collections import defaultdict

def ai_match(ai, params=[{}, {}], match_num=10000):
    print(f"{ai[0].__name__} VS {ai[1].__name__}")
元と同じなので省略
プログラム全体
def ai_match(ai, params=[{}, {}], match_num=10000):
    print(f"{ai[0].__name__} VS {ai[1].__name__}")
    
    mb = Marubatsu()

    # ai[0] VS ai[1] と ai[1] VS a[0] の対戦を match_num 回行い、通算成績を数える
    count_list = [ defaultdict(int), defaultdict(int)]
    for _ in range(match_num):
        count_list[0][mb.play(ai, params, verbose=False)] += 1
        count_list[1][mb.play(ai=ai[::-1], params=params[::-1], verbose=False)] += 1

    # ai[0] から見た通算成績を計算する
    count_list_ai0 = [
        # ai[0] VS ai[1] の場合の、ai[0] から見た通算成績
        { 
            "win": count_list[0][Marubatsu.CIRCLE],
            "lose": count_list[0][Marubatsu.CROSS],
            "draw": count_list[0][Marubatsu.DRAW],
        },
        # ai[1] VS ai[0] の場合の、ai[0] から見た通算成績
        { 
            "win": count_list[1][Marubatsu.CROSS],
            "lose": count_list[1][Marubatsu.CIRCLE],
            "draw": count_list[1][Marubatsu.DRAW],
        },
    ]           

    # 両方の対戦の通算成績の合計を計算する
    count_list_ai0.append({})
    for key in count_list_ai0[0]:
        count_list_ai0[2][key] = count_list_ai0[0][key] + count_list_ai0[1][key]

    # それぞれの比率を計算し、ratio_list に代入する
    ratio_list = [ {}, {}, {} ]
    for i in range(3):
        for key in count_list_ai0[i]:
            ratio_list[i][key] = count_list_ai0[i][key] / sum(count_list_ai0[i].values())
            
    # 各行の先頭に表示する文字列のリスト
    item_text_list = [ Marubatsu.CIRCLE, Marubatsu.CROSS, "total" ]    
    
    # 通算成績の回数と比率の表示
    width = max(len(str(match_num * 2)), 7)
    diff_list = [ ("count", count_list_ai0, f"{width}d"),
                  ("ratio", ratio_list, f"{width}.1%") ]
    for title, data, format in diff_list:
        print(title, end="")
        for key in data[0]:
            print(f" {key:>{width}}", end="")
        print()
        for i in range(3):
            print(f"{item_text_list[i]:5}", end="")
            for value in data[i].values():
                print(f" {value:{format}}", end="")
            print()
        print()
修正箇所
from collections import defaultdict

-def ai_match(ai, params, match_num=10000):
+def ai_match(ai, params=[{}, {}], match_num=10000):
    print(f"{ai[0].__name__} VS {ai[1].__name__}")
元と同じなので省略

上記の修正 を行うことで、下記 のプログラムのように、ai_matchparams に対応する実引数記述 する 必要なくなります。なお、実行結果は省略します。

ai_match(ai=[ai11s, ai2])

ai2 VS ai11s の検証

以前の記事で作成した show_progress を使って、ai2 VS ai11sai11s敗北する理由検証 します。しかし、下記 のプログラムを 実行する と、実行結果のように エラーが発生 します。このようなエラーが発生する理由を少し考えてみて下さい。

from ai import show_progress

show_progress(ai=[ai2, ai11s], winner=Marubatsu.CIRCLE)

実行結果

略
--> 178     if mb.play(ai=ai, verbose=False) == winner:
    179         records = mb.records
    180         mb.restart()

TypeError: Marubatsu.play() missing 1 required positional argument: 'params'

上記のエラーメッセージは、以下のような意味を持ちます。

  • TypeError
    データ型(type)に関するエラー
  • play() missing 1 required positional argument: 'params'
    play メソッドに必要とされる(required)、params という位置引数(positional argument)が記述されていない(missing)

play メソッドの修正

このエラー は、play メソッドに 仮引数 params追加 されているのに、show_progress で、play メソッドを 呼び出す際 に、params実引数記述されていない からです。

この問題解決 する 方法 の一つは、下記 のプログラムのように、play メソッドの 仮引数 paramsデフォルト引数 にするという方法です。なお、その方法は、以前の記事で説明した 関数の互換性 を持つ方法です。

def play(self, ai, params=[{}, {}], verbose=True):
元と同じなので省略

Marubatsu.play = play
プログラム全体
def play(self, ai, params=[{}, {}], verbose=True):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲームの決着がついていない間繰り返す
    while self.status == Marubatsu.PLAYING:
        # ゲーム盤の表示
        if verbose:
            print(self)
        # 現在の手番を表す ai のインデックスを計算する
        index = 0 if self.turn == Marubatsu.CIRCLE else 1
        # ai が着手を行うかどうかを判定する
        if ai[index] is not None:
            x, y = ai[index](self, **params[index])
        else:
            # キーボードからの座標の入力
            coord = input("x,y の形式で座標を入力して下さい。exit を入力すると終了します")
            # "exit" が入力されていればメッセージを表示して関数を終了する
            if coord == "exit":
                print("ゲームを終了します")
                return       
            # x 座標と y 座標を要素として持つ list を計算する
            xylist = coord.split(",")
            # xylist の要素の数が 2 ではない場合
            if len(xylist) != 2:
                # エラーメッセージを表示する
                print("x, y の形式ではありません")
                # 残りの while 文のブロックを実行せずに、次の繰り返し処理を行う
                continue
            x, y = xylist
        # (x, y) に着手を行う
        try:
            self.move(int(x), int(y))
        except:
            print("整数の座標を入力して下さい")

    # 決着がついたので、ゲーム盤を表示する
    if verbose:
        print(self)
    return self.status

Marubatsu.play = play
修正箇所
-def play(self, ai, params, verbose=True):
+def play(self, ai, params=[{}, {}], verbose=True):
元と同じなので省略

Marubatsu.play = play

上記の修正 を行うことで、下記のプログラムを実行しても エラーが発生しなくなります

show_progress(ai=[ai2, ai11s], winner=Marubatsu.CIRCLE)

実行結果(実行結果はランダムなので下記とは異なる場合があります)

Turn x
...
...
O..

Turn o
...
.X.
o..

Turn x
...
.xO
o..

Turn o
X..
.xo
o..

Turn x
x..
.xo
o.O

Turn o
x..
Xxo
o.o

winner o
x.O
xxo
o.o

show_progress の修正

上記のように、show_progress によって、ai2 VS ai11s で、ai11s敗北 する 試合の経過表示できています が、このまま では、show_progress呼び出す際AI のパラメータ指定できない ので、show_progress にも下記のプログラムのように、デフォルト引数 params追加 し、play メソッドを 呼び出す際 に、キーワード引数 として params=params記述する ように 修正 します。

  • 1 行目[{}, {}]デフォルト値 とする デフォルト引数 params追加 する
  • 4 行目play メソッドの 呼び出しの際 に、キーワード引数 として params=params記述する
 1  def show_progress(ai, winner, params=[{}, {}]):
 2      mb = Marubatsu()
 3      while True:
 4          if mb.play(ai=ai, verbose=False, params=params) == winner:
 5              records = mb.records
 6              mb.restart()
 7              for x, y in records:
 8                  mb.move(x, y)
 9                  print(mb)
10              break
行番号のないプログラム
def show_progress(ai, winner, params=[{}, {}]):
    mb = Marubatsu()
    while True:
        if mb.play(ai=ai, verbose=False, params=params) == winner:
            records = mb.records
            mb.restart()
            for x, y in records:
                mb.move(x, y)
                print(mb)
            break
修正箇所
-def show_progress(ai, winner):
+def show_progress(ai, winner, params=[{}, {}]):
    mb = Marubatsu()
    while True:
-       if mb.play(ai=ai, verbose=False) == winner:
+       if mb.play(ai=ai, verbose=False, params=params) == winner:
            records = mb.records
            mb.restart()
            for x, y in records:
                mb.move(x, y)
                print(mb)
            break

下記 のプログラムで、上記の修正 を行っても show_progress正しく動作 することが 確認 できます。なお、実行結果は先ほどと同様なので省略します。

show_progress(ai=[ai2, ai11s], winner=Marubatsu.CIRCLE)

長くなりましたので、ai2 VS ai11s の検証は次回の記事で行うことにします。

今回の記事のまとめ

今回の記事では、弱解決弱解決の AI定義性質 を説明し、〇×ゲーム のように、局面の数少ないゲーム では、ランダムな AI十分な回数の対戦 を行うことで、AI弱解決の AI であるか どうかを 判定できる ことを 示しました

また、いくつかの関数 を、使いやすいように 修正 し、ai2 VS ai11sai11s敗北 する 試合経過 を表示しました。

本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

以下のリンクは、今回の記事で更新した marubatsu.py です。

以下のリンクは、今回の記事で更新した ai.py です。

次回の記事

  1. 以前の記事で説明したように、この中で 最も局面の数少ないオセロ でも、局面の数は $10^{28}$ 以上ある言われています

  2. 本記事の説明 は、元の定義少し意訳 しています。原文の定義 を知りたい方は、http://fragrieu.free.fr/SearchingForSolutions.pdf8 ページ を見て下さい

  3. 具体例 を知りたい方は、https://www.jstage.jst.go.jp/article/sugaku/65/1/65_0651093/_pdf/-char/ja96 ページ を見て下さい

  4. 必敗の局面 では、すべての合法手最善手 です

  5. 目的 として 作られた」という 表現 になっているのは、将棋囲碁 も、現時点 では 弱解決強解決されていない からです

  6. 局面の種類5478 であることは、探索型の AI説明の際示します

2
0
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
0