Start_python’s diary

ふたり暮らし

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

Pythonで人工知能搭載の三目並べが完成しました(AIプログラミング 第7回)

こちらのサイトを参考にさせていただきまいた。

qiita.com

 

最後に全てのソースコードを載せておきます。今回新しく学べたことを解説していきます。前回「考えるマッチ箱」でコピペなしでプログラム作成してみたのですが今回「こうすればよかったんだ」と新しく気づけたことが多かったです。「下手でも動く方法を自分で考える」⇒「人の作ったソースコードを参考にする」⇒「新しい発見がある」

この流れが勉強するのには一番良さそうに感じました。

 

繰り返しリストの作成

# 盤面の初期化
board = [" "] * 9

board = [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] のリストが作成できます。

 

input関数でTrueかFalseを返す

# 先攻後攻(「1」以外は後攻になる)
my_turn = input("先攻 1 : 後攻 2 >> ") == "1"

[ 1 ]を入力すると「True」、[ 1 ]以外を入力すると「False」を返します。
※「変数 = 」の後に「==」を入れられるのがポイントです。

 

print関数で改行を使う

print('1|2|3\n-----\n4|5|6\n-----\n7|8|9\n')

1|2|3
-----
4|5|6
-----
7|8|9

が表示されます。※半角の「¥n」で改行になります。

 

リストを分解して表示する(format関数の使い方)

# 例
board = ["o","x"," ","o","o"," ","x"," "," "]
# 盤面を表示する
print('{0[0]}|{0[1]}|{0[2]}\n' \
'-----\n{0[3]}|{0[4]}|{0[5]}\n' \
'-----\n{0[6]}|{0[7]}|{0[8]}\n'.format(board))

o|x|
-----
o|o|
-----
x| | 

が表示されます。「format(a,b)」を {  } 内に「aは0、bは1」で指定できます。

 

TrueとFalseの切り替え

# ターン交代
self.my_turn = not(self.my_turn)

not()で、Trueは「False」に Falseは「True」に変わります。

 

三目並べの勝敗の判定

# 勝敗の判定
def check_state(self):
    for i in range(3):
        if self.board[i:9:3] == ["o","o","o"] or self.board[3*i:3*i+3] == ["o","o","o"] \
        or self.board[0:9:4] == ["o","o","o"] or self.board[2:7:2]  == ["o","o","o"]:
            self.state = "あなたの勝ちです"
            return
        if self.board[i:9:3] == ["x","x","x"] or self.board[3*i:3*i+3] == ["x","x","x"] \
        or self.board[0:9:4] == ["x","x","x"] or self.board[2:7:2]  == ["x","x","x"]:
            self.state = "AIの勝ちです"
            return

    if all(state != " " for state in self.board):
        self.state = "引き分けです"
        return

    self.state = "プレイ中"

タテヨコ斜めの8通り全てを調べてるのにfor文を使っています。(8通りくらいならfor文を使わずに全て並べた方がわかりやすいかもです)

all(state != " " for state in self.board)

リスト「board」の要素がすべて「" "」の時に「True」を返します。

※all関数は、全ての要素がTrueの時に「True」を返します。(andのようなも)
any関数は、いずれかの要素がTrueの時に「True」を返します。(orのようなもの)

 

ミニマックス法

if self.my_turn:
    if child_value > value:
        value = child_value
        best_value = i
else:
    if child_value < value:
        value = child_value
        best_value = i

ミニマックス法の評価点を引き継ぐ部分です。

今の局面の新しい評価点が「child_value」、新しく打った場所が「i」です。
自分のターンの時は現在持っているの評価点より新しい評価点が大きい場合は、新しい評価点に置き換えます。新しく打った場所も記録します。
相手のターンの時は現在持っているの評価点より新しい評価点が小さい場合は、新しい評価点に置き換えます。新しく打った場所も記録します。

 

 

最後に全てのソースコードを載せておきます。

三目並べでミニマックス法を使ったソースコード

class main():

    def __init__(self):
        # 盤面の初期化
        self.board = [" "] * 9
        # 先攻後攻(「1」以外は後攻になる)
        self.my_turn = input("先攻 1 : 後攻 2 >> ") == "1"

        print('1|2|3\n-----\n4|5|6\n-----\n7|8|9\n')

        # ゲーム開始
        self.state = "プレイ中"
        while self.state == "プレイ中":
            if self.my_turn:
                # プレイヤーの入力
                while True:
                    try:
                        value = int(input('あなたの番です >> ')) - 1
                        if self.board[value] == " ":
                            # 「value」に置く
                            self.board[value] = "o"
                            # 勝敗の判定
                            self.check_state()
                            # ターン交代
                            self.my_turn = not(self.my_turn)
                            break
                        else:
                            print('そこには打てません')
                    except:
                        print('入力し直してください')
            else:
                # AIの入力
                print("AI")
                # ミニマックス法で最良の手を探索
                value = self.minimax(0)
                # 「最良の手」に打つ
                self.board[value] = "x"
                # 勝敗の判定
                self.check_state()
                # ターン交代
                self.my_turn = not(self.my_turn)


            # ボードを表示する
            print('{0[0]}|{0[1]}|{0[2]}\n' \
            '-----\n{0[3]}|{0[4]}|{0[5]}\n' \
            '-----\n{0[6]}|{0[7]}|{0[8]}\n'.format(self.board))

        # 結果を表示する
        print(self.state)


    # 勝敗の判定
    def check_state(self):
        for i in range(3):
            if self.board[i:9:3] == ["o","o","o"] or self.board[3*i:3*i+3] == ["o","o","o"] \
            or self.board[0:9:4] == ["o","o","o"] or self.board[2:7:2]  == ["o","o","o"]:
                self.state = "あなたの勝ちです"
                return
            if self.board[i:9:3] == ["x","x","x"] or self.board[3*i:3*i+3] == ["x","x","x"] \
            or self.board[0:9:4] == ["x","x","x"] or self.board[2:7:2]  == ["x","x","x"]:
                self.state = "AIの勝ちです"
                return

        if all(state != " " for state in self.board):
            self.state = "引き分けです"
            return

        self.state = "プレイ中"



    # ミニマックス法で探索
    def minimax(self, depth):
        # 勝敗がついた場合
        if self.state != "プレイ中":
            # 評価点を出す
            return self.evaluate(depth)
        
        best_value = 0
        if self.my_turn:
            value = 100
        else:
            value = -100
        
        for i in range(9):
            # 「i」が空の場合
            if self.board[i] == " ":
                # 「i」に打つ
                if self.my_turn:
                    self.board[i] = "o"
                else:
                    self.board[i] = "x"
                # 勝敗の判定
                self.check_state()
                # ターン交代
                self.my_turn = not(self.my_turn)
                # 再帰
                child_value = self.minimax(depth+1)
                if self.my_turn:
                    if child_value > value:
                        value = child_value
                        best_value = i
                else:
                    if child_value < value:
                        value = child_value
                        best_value = i
                # 1つ前の手に戻す
                self.board[i] = " "
                # ターン交代
                self.my_turn = not(self.my_turn)

                # 評価点を表示する
                if depth == 0:
                    print("(打つ場所:",i+1,"評価点:",child_value,")")

        if depth == 0:
            return best_value
        else:
            return value
        
    # 評価点
    def evaluate(self, depth):
        if self.state == "AIの勝ちです":
            self.state = "プレイ中"
            return 10 - depth
        elif self.state == "あなたの勝ちです":
            self.state = "プレイ中"
            return depth - 10
        else:
            self.state = "プレイ中"
            return 0
        
if __name__ == '__main__':
    for i in range(3):
        main()

 

 

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

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