【はじめてのPython】オセロのCPUを作る⑤

ITスキル

こんにちは。ヒトツメです。
またもかなり間が空いてしまいましたが、オセロのCPUを作っていこうと思います。
前回は、ミニマックス法まで実装できたので、枝刈りをしてアルファベータ法を実装したいと思います。

【はじめてのPython】オセロのCPUを作る④
こんにちは。ヒトツメです。またも少し間が空いてしまいましたが、今回は前回学んだ再帰関数を使って、前々回に取り上げたアルファベータ法を実装していきたいと思います。 考え方の整理とActionの修正 そこで、実際に再帰関数を用いて、3手先の局面
新しいタブで開く)
スポンサーリンク

どういう場合に枝刈りするか

前回までの考え方の整理として、どのような場合に枝刈りをするのかという話から入りたいと思いますが、そもそも、前回実装したミニマックス法では、3手先の局面をすべからくすべて探索していました。
これにより、双方が最善手を選んだ場合における、自分の石が最も多くなる場合を探索することに成功しました。

この図で行くと、オレンジの8個の局面をすべて探索したということです。
しかしながら、オレンジの左から3つ目が4点になっている時点で、青の左から2つ目は4点以上が確定します。この時点で、オレンジの4つ目は点数計算をしなくてよくなります。

このように不要な部分を枝刈りしていくのが、アルファベータ法です。

実装してみる

そこで実際に実装してみるわけですが、再帰関数を使う部分など、大枠はミニマックス法と全く変わりません。
違うのは、点数の更新をするタイミングで、今まで出ている点数との比較が発生するという部分だけです。

    def minimax(self, game, stone, zero_count):
        if numpy.count_nonzero(game == 0.0) == 0 or numpy.count_nonzero(game == 0.0) <= zero_count - 3:
            stone = -1 * stone
            return numpy.count_nonzero(game == self.CPU_stone)
        else:
            best_score = (-float('inf'))
            act = placableAct(game, stone)
            for action in act:
                [row, clm] = action
                copy_game = game.copy()
                nextgame(copy_game, row, clm, stone)
                stone = -1 * stone
                score = -1 * self.minimax(copy_game, stone, zero_count)
                stone = -1 * stone
                if score > best_score:
                    best_score = score
            return best_score
    def alphabeta(self, game, stone, zero_count, alpha, beta):
        if numpy.count_nonzero(game == 0.0) == 0 or numpy.count_nonzero(game == 0.0) <= zero_count - 3:
            stone = -1 * stone
            return numpy.count_nonzero(game == self.CPU_stone)
        else:
            act = placableAct(game, stone)
            for action in act:
                [row, clm] = action
                copy_game = game.copy()
                nextgame(copy_game, row, clm, stone)
                stone = -1 * stone
                score = -1 * self.alphabeta(copy_game, stone, zero_count, -beta, -alpha)
                stone = -1 * stone
                if score > alpha:
                    alpha = score
                if alpha >= beta:
                    return alpha
            return alpha

並べて書くとこの通りで、ほとんど違いはありません。
違うのは、アルファベータ法では、アルファとベータを引数にとり、すでに出ている点数と比較しながら、スコアを更新している点です。

「if score > alpha:」とすることで、今まで出てきた点数よりも自分の手が高くなる場合にのみ更新を行い、「if alpha >= beta:」とすることで、これ以上点数が高得点になることが期待できなくなったタイミングで、スコアの返却を行っています。
再帰呼び出しの際にalphaとbetaを入れ替えることで、先手と後手を入れ替えています。

アクションの部分でも、ミニマックス法と異なるところはほとんどなく、次のような記載になります(ミニマックス法との比較のため、ミニマックス法での手の選択の部分をコメントアウトしています)。

#score = self.minimax(self.copygame, self.stone, zero_count)
score = self.alphabeta(self.copygame, self.stone, zero_count, -float('inf'), float('inf'))

実行してみると、きちんと動くことが確認できます。

さいごに

一応、オセロのCPUづくりは今回で完了となります。
これをさらに改良していくとなると、3手先を読んだ状態での状態評価を行わなければならず、機械学習が必要になるため、その基礎部分がない状態で作っていくと、説明が非常に煩雑になる可能性が高いためです。
掻い摘んで仕組みだけ説明しておくと、3手先の自分の石の数ではなく、3手先の状態での「勝ちやすさ」を評価基準とし、その「勝ちやすさ」の評価を、機械学習で行うということです。

Pythonは機械学習に向いた言語だといわれていますが、ほかにもできることはたくさんありますし、開発環境などを考えると、ほかにもいろいろと着手するべきことがあるということです。

もちろん、オセロをPythonで作った以上、機械学習の実装はやりたいので、いつかその過程も乗せれればと思います。

コメント

タイトルとURLをコピーしました