今日は
の確認をおこなっていきます。元となる発想はpart2 で解説した、1手先の全ての盤面を考え、その中で最も良さそうなもの(スコアが高いもの)を選ぶ
の、1手先をN手先にしたらより正確に最後まで予測出来、評価関数の精度が上がるよね。ということです。ただ、なんとなく考えてもわかる通り、手数が1増えるごとに考えられる盤面が指数関数的に増えていきます。
そこで、AIは可能な限り高いスコアを得るために手を打ち、相手はスコアを可能な限り低くするために手を打つことでこれに対抗すると仮定します。これを仮定する事で理論上はありうる盤面の全てを考えなくても良くなります。これがミニマックスアルゴリズムの主な考え方です。
import random
import numpy as np
# エージェントが選択したの次のステップでの盤面を取得する関数
def drop_piece(grid, col, mark, config):
"""
Args:
grid (numpy.ndarray): 現在のゲーム盤の状態。
col (int): ピースを落とす列のインデックス。
mark (int): プレイヤーの識別子(通常、1または2)。
config: ゲームの構成情報(行数、列数など)。
Returns:
numpy.ndarray: ピースを落とした後の新しいゲーム盤。
"""
# ウィンドウがヒューリスティック条件を満たしているかを確認
def check_window(window, num_discs, piece, config):
"""
`window` (連続したマスのリスト) が、特定のヒューリスティック条件を満たしているかどうかを確認します。
Args:
window (list): チェックするマスのリスト。
num_discs (int): 目的のプレイヤーのピースの数。
piece (int): プレイヤーの識別子。
config: ゲームの構成情報。
Returns:
bool: ヒューリスティック条件を満たしていればTrue、そうでなければFalse。
"""
# 指定されたヒューリスティック条件を満たすウィンドウの数を数える(仮定を使用しています!!!)
def count_windows(grid, num_discs, piece, config):
"""
指定されたゲーム盤 `grid` 上で、`num_discs` 個の `piece` と空きマスがあるようなウィンドウの数を数えます。
Args:
grid (numpy.ndarray): ゲーム盤の状態。
num_discs (int): 目的のプレイヤーのピースの数。
piece (int): プレイヤーの識別子。
config: ゲームの構成情報。
Returns:
int: ヒューリスティック条件を満たすウィンドウの総数。
"""
このあと、相手が勝にならないように、或は自分が負けにならないようにその盤面のスコアに非常に大きな仮のスコアをつけています。このすこあもヒューリスティックにつけたものです。
最後に、
の解説を少しします。これは今までの部分にNNを付け足したものです。解説が必要な部分がありますが、原文では追加で参考となるURLを載せているのでそれを読む良さそうですね。
きほん的には、今までに人間が当てずっぽうに決めたScoreを適切に変えることは評価関数の精度の上昇につながることがわかりますね。何度も対戦していく中で、そのすこあもNNに適切に決めてもらえば人間の介入を不要としてさらに性能を挙げることができる、という話です。
報酬の設定
各手を打った後、エージェントがどれだけうまくやったかを伝える報酬を与えます。
エージェントがその手でゲームに勝った場合、+1 の報酬を与えます。
エージェントが無効な手を打った場合(ゲームが終了します)、-10 の報酬を与えます。
対戦相手が次の手でゲームに勝った場合(つまり、エージェントは対戦相手の勝利を防ぐことができなかった場合)、-1 の報酬を与えます。
それ以外の場合、エージェントは 1/42 の報酬を受け取ります。
累積報酬: 各ゲームの最後に、エージェントは報酬を合計します。報酬の合計をエージェントの累積報酬と呼びます。
こうする事で、AIは負けないように、勝つこと或はゲームがそのまま続く事を目指したScoreを自分で設定することができるようになります。
そのあとの部分は、
が解説の中心となります。
実際に作成すると、学習時間は割とかかりますが、モデル自体は軽量なものが出来上がります。