Start_python’s diary

ふたり暮らし

アラフィフ夫婦のフリーランスプラン

Python FlaskとBrythonで三目並べの人工知能AIを作る(後編)

f:id:Start_python:20210306161352j:plain

ゲームスタート

 

 

はじめに

前回の続きです。前編では2手目まで条件式で指示を与えていました。

3手目からはミニマックス法もどきで、コンピュータ側の全通りの手を調べて勝つ手があればそれを打ちます。次のプレイヤーのターンも全通り調べて、負ける手があればそこには打たないようにします。(深さが2層のミニマックス法です)

コンピュータを完璧にしてプレイヤーがまったく勝てないと面白くないと思うので(本当は邪魔くさい)ある条件の負ける手は打ってしまうように考えます。

 

3手目以降

  • 「元の位置(self.koma_no)」から大きさ(self.koma)を調べる
  • 「元の位置(self.koma_no)」候補は最大6つ
  • 「移動先(self.masu_no)」候補は最大9つ
  •   合計54パターンを総調べ
  •   勝てる手があればその位置に打つ
  •   次の相手の手まで読む
  •   負ける手があればその手は打たない
  •   勝負が決まらなければ・・・
  •   負ける手しかなければ・・・

この流れで考えていきます。

 

先手・後手 共通

「元の位置(self.koma_no)」から大きさ(self.koma)を調べる

# 「元の位置(self.koma_no)」から大きさ(self.koma)を調べる
for i in range(9):
    if str(self.board[i])[0] == "2":
        if document[str(i)].classList.contains("red_l"):
            self.koma_no = str(i)
            self.koma = "red_l"
            # 仮で打ってみる
            self.max_value()
            if self.masu_no != 9 and self.masu_no >= 0:
                return
        elif document[str(i)].classList.contains("red_m"):
            self.koma_no = str(i)
            self.koma = "red_m"
            # 仮で打ってみる
            self.max_value()
            if self.masu_no != 9 and self.masu_no >= 0:
                return
        elif document[str(i)].classList.contains("red_s"):
            self.koma_no = str(i)
            self.koma = "red_s"
            # 仮で打ってみる
            self.max_value()
            if self.masu_no != 9 and self.masu_no >= 0:
                return
if document["box6"].classList.contains("red_l"):
    self.koma_no = "box6"
    self.koma = "red_l"
    # 仮で打ってみる
    self.max_value()
    if self.masu_no != 9 and self.masu_no >= 0:
        return
if document["box5"].classList.contains("red_m"):
    self.koma_no = "box5"
    self.koma = "red_m"
    # 仮で打ってみる
    self.max_value()
    if self.masu_no != 9 and self.masu_no >= 0:
        return
if document["box2"].classList.contains("red_m"):
    self.koma_no = "box2"
    self.koma = "red_m"
    # 仮で打ってみる
    self.max_value()
    if self.masu_no != 9 and self.masu_no >= 0:
        return
if document["box4"].classList.contains("red_s"):
    self.koma_no = "box4"
    self.koma = "red_s"
    # 仮で打ってみる
    self.max_value()
    if self.masu_no != 9 and self.masu_no >= 0:
        return
if document["box1"].classList.contains("red_s"):
    self.koma_no = "box1"
    self.koma = "red_s"
    # 仮で打ってみる
    self.max_value()
    if self.masu_no != 9 and self.masu_no >= 0:
        return

 

「元の位置(self.koma_no)」候補は最大6つ
「移動先(self.masu_no)」候補は最大9つ
合計54パターンを総調べ

for j in range(9):
    # 盤面をコピー
    self.board_2 = self.board[:]
    # 盤面を記録
    if self.koma == "red_s":
        if self.board_2[j] < 1:
            self.board_2[j] += 2
            if self.koma_no[0:3] != "box":
                self.board_2[int(self.koma_no)] -= 2
    elif self.koma == "red_m":
        if self.board_2[j] < 10:
            self.board_2[j] += 20
            if self.koma_no[0:3] != "box":
                self.board_2[int(self.koma_no)] -= 20
    elif self.koma == "red_l":
        if self.board_2[j] < 100:
            self.board_2[j] += 200
            if self.koma_no[0:3] != "box":
                self.board_2[int(self.koma_no)] -= 200

 

勝てる手があればその位置に打つ

# 「元の位置」の駒を消す
document[self.koma_no].classList.remove(self.koma)
# 駒を打つ
document[str(self.masu_no)].classList.add(self.koma)

# 盤面を記録
if self.koma == "red_s":
    self.board[self.masu_no] += 2
    if self.koma_no[0:3] != "box":
        self.board[int(self.koma_no)] -= 2
elif self.koma == "red_m":
    self.board[self.masu_no] += 20
    if self.koma_no[0:3] != "box":
        self.board[int(self.koma_no)] -= 20
elif self.koma == "red_l":
    self.board[self.masu_no] += 200
    if self.koma_no[0:3] != "box":
        self.board[int(self.koma_no)] -= 200

 

次の相手の手まで読む

# 「元の位置(self.koma_no)」から大きさ(self.koma)を調べる
if document["box9"].classList.contains("blue_l"):
    self.koma_no2 = "box9"
    self.koma2 = "blue_l"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -100
        return
if document["box12"].classList.contains("blue_l"):
    self.koma_no2 = "box12"
    self.koma2 = "blue_l"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -100
        return
if document["box8"].classList.contains("blue_m"):
    self.koma_no2 = "box8"
    self.koma2 = "blue_m"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -10
        return
if document["box11"].classList.contains("blue_m"):
    self.koma_no2 = "box11"
    self.koma2 = "blue_m"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -10
        return
if document["box7"].classList.contains("blue_s"):
    self.koma_no2 = "box7"
    self.koma2 = "blue_s"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -1
        return
if document["box10"].classList.contains("blue_s"):
    self.koma_no2 = "box10"
    self.koma2 = "blue_s"
    # 仮で打ってみる
    self.min_value2()
    if self.masu_no != 9:
        self.masu_no = -1
        return

'''
# ここをコメント解除するとコンピュータが強くなりすぎる
for i in range(9):
    if str(self.board[i])[0] == "1":
        if document[str(i)].classList.contains("blue_l"):
            self.koma_no2 = str(i)
            self.koma2 = "blue_l"
            # 仮で打ってみる
            self.min_value2()
            if self.masu_no != 9:
                self.masu_no = -100
                return
        elif document[str(i)].classList.contains("blue_m"):
            self.koma_no2 = str(i)
            self.koma2 = "blue_m"
            # 仮で打ってみる
            self.min_value2()
            if self.masu_no != 9:
                self.masu_no = -10
                return
        elif document[str(i)].classList.contains("blue_s"):
            self.koma_no2 = str(i)
            self.koma2 = "blue_s"
            # 仮で打ってみる
            self.min_value2()
            if self.masu_no != 9:
                self.masu_no = -1
                return
'''

 

負ける手があればその手は打たない

# 赤が打てるか調べる
if (self.koma == "red_l" and self.board[j] < 100) \
or (self.koma == "red_m" and self.board[j] < 10) \
or (self.koma == "red_s" and self.board[j] < 1):

    # 悪い手は打たない
    if self.koma_no == "4" and  self.koma == "red_l" and j == 7:
        pass
    else:
        self.koma_no1 = self.koma_no
        self.koma1 = self.koma
        self.masu_no1 = j

self.masu_no = 9

# 盤面をコピー
self.board_1 = self.board_2[:]
# 相手のターンへ
self.min_value()
# 次で負けなければそこに打つ
if self.masu_no == 9:
    self.koma_no0 = self.koma_no1
    self.koma0 = self.koma1
    self.masu_no0 = self.masu_no1
elif self.masu_no1 < 9:
    # 負ける手しかない場合用
    self.koma_no3 = self.koma_no1
    self.koma3 = self.koma1
    self.masu_no3 = self.masu_no1
    self.board_3 = self.board_2[:]

 

勝負が決まらなければ・・・
負ける手しかなければ・・・

if self.masu_no == 9:
    # 勝負が決まらない場合
    self.koma_no = self.koma_no0
    self.koma = self.koma0
    self.masu_no = self.masu_no0
    document[self.koma_no].classList.remove(self.koma)
    timer.set_timeout(self.AI_set, 100)
                
elif  self.masu_no >= 0:
    # 勝てる手がある場合
    document[self.koma_no].classList.remove(self.koma)
    timer.set_timeout(self.AI_set, 100)

elif self.masu_no < 0 and self.masu_no0 == 9:
    # 負ける手しかない場合
    self.koma_no = self.koma_no3
    self.koma = self.koma3
    self.masu_no = self.masu_no3
    document[self.koma_no].classList.remove(self.koma)
    timer.set_timeout(self.AI_set, 100)

elif self.masu_no < 0 and self.masu_no0 >= 0:
    # 負ける手がある場合
    self.koma_no = self.koma_no0
    self.koma = self.koma0
    self.masu_no = self.masu_no0
    document[self.koma_no].classList.remove(self.koma)
    timer.set_timeout(self.AI_set, 100)

else:
    alert("これが出たらバグ!else:")
    alert(self.koma_no + " , " + self.koma + " , " + str(self.masu_no))
    alert(self.koma_no0 + " , " + self.koma0 + " , " + str(self.masu_no0))

 

まとめ

それなりに強いコンピュータが出来上がりました。2手目までは指示を与えることでランダム性がでて楽しめると思います。コンピュータの弱点としては「2手目まで『小』の駒にはかぶせてこない」「勝敗が見つからなかった場合の手があまい」「プレイヤーが盤面上の駒を移動する手は読まない」などがあります。

 

いろいろ調べているとAIを最初にゲームに使ったのはナムコの「パックマン」らしいです。また「ゼビウス」ではプレイヤーの上手さによって難易度が変わったらしいです。(初めて知りました)

news.denfaminicogamer.jp

f:id:Start_python:20200321105251j:plain

 

プレイヤーの実力により難易度を変化させるのはすごく良いアイデアです。プレイヤーの連勝した数で徐々にコンピュータを強くする、連敗したら徐々にコンピュータを弱くするようにすれば、誰でも楽しめる三目並べが出来そうです。

 

 

↓よかったらポチッとしていってください。

にほんブログ村 IT技術ブログ Pythonへ
にほんブログ村