0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

pythonでオセロ(人vsコンピュータ)作ってみた2

Last updated at Posted at 2023-09-16

前回作ったオセロコンピュータに、新しく評価基準を追加しました。
①マスごとの評価値
②そこに置いたときに、次のターン相手が置ける場所の数←New
③そこに置いたときの、相手の最善手の評価値(マスごとに設定)←New

①~③の評価の比重は、自分で調整しました。これだけでもある程度戦えるようになってきました。
ここから、マスごとの評価値、①~③の比重を、戦わせながらコンピュータ自身に調整させます。やっとAIっぽいことできる!!!!!
AIについては本で仕組みや計算方法を学んだだけで、実践は初めて。ドキドキとワクワク。頑張ります。

↓ソースコード

オセロ(人vsコンピュータ)ver.2
import tkinter
from tkinter import *

"""
コンピュータが置くときの評価基準
・マスごとの評価値
・置いたときに相手が置ける場所の数
・置いたときの相手の置ける最善手の評価値
"""

class player:
    def __init__(self):
        self.name="name"
        self.disc=0
                
player1=player()
player2=player()
player1.name="aaa"
player1.disc=1
player2.name="bbb"
player2.disc=2

can_place=[]
cnt_can=4
#マスごとの評価値
place_value=[110,-20, 60,-10,-10, 60,-20,110,
             -20,-60,-30, -5, -5,-30,-60,-20,
              60,-30, 25, 10, 10, 25,-30, 60,
             -10, -5, 10, 10, 10, 10, -5,-10,
             -10, -5, 10, 10, 10, 10, -5,-10,
              60,-30, 25, 10, 10, 25,-30, 60,
             -20,-60,-30, -5, -5,-30,-60,-20,
             110,-20, 60,-10,-10, 60,-20,110]
cnt_cannot=0
com_place=19
cnt_opp_can=0

for i in range(0,64):
    if i==19 or i==26 or i==37 or i==44:
        can_place.append(3)
    else:
        can_place.append(0)

#ボードの初期化
board=[]#壁も含めた盤面
Re_board=[]#壁をのぞいた盤面
for i in range(0,100):#白石は2、黒石は1、空白は0
    if (0<=i and i<=9) or (90<=i and i<=99) or i%10==0 or i%10==9:
        board.append(5)
    elif i==44 or i==55:
        board.append(2)
        Re_board.append(i)
    elif i==45 or i==54:
        board.append(1)
        Re_board.append(i)
    else:
        board.append(0)
        Re_board.append(i)

class Application(tkinter.Frame):
    def __init__(self,win=None):
        super().__init__(win,width=680,height=480,borderwidth=1,relief='groove')
        self.win=win
        self.pack()
        self.pack_propagate(0)
        self.create_widgets()
        self.create_button(board,can_place)
        
    def create_widgets(self):
        self.label_turn=Label(self,text="あなたが先攻です\n黒を置いてください",font=("MSゴシック", "15"),justify="left")
        self.label_turn.place(x=500,y=50)
        self.label_cnt1=Label(self,text="黒:2枚",font=("MSゴシック", "20"))
        self.label_cnt1.place(x=500,y=120)
        self.label_cnt2=Label(self,text="白:2枚",font=("MSゴシック", "20"))
        self.label_cnt2.place(x=500,y=160)
        self.label_win=Label(self,text="",font=("MSゴシック", "15"),justify="left")
        self.button_com=Button(self,text="決定",font=("MSゴシック", "20"),command=self.com_run)
        self.button_com.place(x=500,y=250)
        self.button_com.configure(bg="yellow")
       
    def create_button(self,board,can_place):
        self.button=[]
        for i in range(0,8):
            for j in range(0,8):
                if board[(i+1)*10+j+1]==2:
                    self.button.append(Button(self,text="",font=("MSゴシック","45"),fg="white",command=self.btn_clicked(self.button,i*8+j)))
                elif board[(i+1)*10+j+1]==1:
                    self.button.append(Button(self,text="",font=("MSゴシック","45"),command=self.btn_clicked(self.button,i*8+j)))
                elif can_place[i*8+j]==3:
                    self.button.append(Button(self,text="",font=("MSゴシック","20"),fg="yellow",command=self.btn_clicked(self.button,i*8+j)))
                else:
                    self.button.append(Button(self,command=self.btn_clicked(self.button,i*8+j)))
                self.button[i*8+j].place(x=j*60,y=i*60,width=60,height=60,)
                self.button[i*8+j].configure(bg="green")
    
    #人間の番
    def btn_clicked(self,button,place):
        def inner():
            def show_board(board,button,can_place,player,finish):#盤面をウィンドウに表示
                if player==player1:
                    self.label_turn["text"]="「決定」を\n押してください"
                else:
                    self.label_turn["text"]="あなたの番です"
                cnt1,cnt2=cnt_disc(board)
                self.label_cnt1["text"]="黒:"+str(cnt1)+""
                self.label_cnt2["text"]="白:"+str(cnt2)+""
                if (cnt1+cnt2==64) or (cnt1==0) or (cnt2==0) or finish==True:
                    self.label_turn["text"]=""
                    if cnt1>cnt2:
                        self.label_win["text"]=f'あなたの勝利です'
                        self.label_win.place(x=500,y=200)
                    elif cnt1<cnt2:
                        self.label_win["text"]=f'コンピュータの勝利です'
                        self.label_win.place(x=500,y=200)
                    else:
                        self.label_win["text"]="引き分けです"
                        self.label_win.place(x=500,y=200)
                else:
                    pass
                for i in range(0,8):
                    for j in range(0,8):
                        button[i*8+j].destroy()
                        
                self.create_button(board,can_place)
                for i in range(0,8):
                    for j in range(0,8):
                        button[i*8+j].place(x=j*60,y=i*60,width=60,height=60,)
                        button[i*8+j].configure(bg="green")
                
            def reverse(place_a,board,player):#裏返す関数、戻り値→裏返せる枚数
                dir=(-11,-10,-9,-1,1,9,10,11)#方向ベクトル
                sum_cnt_rev=0#そこに置いたときに裏返せる枚数
                for i in dir:
                    tmp_place=place_a#placeの初期化
                    cnt_rev=0#その方向においての裏返せる枚数
                    while board[tmp_place+i]!=player.disc:
                        if board[tmp_place+i]==(int(player.disc)-1.5)*-1+1.5:#ディスクが1なら2,2なら1(相手のディスクがある)
                            tmp_place+=i
                            cnt_rev+=1
                        else:#0(空きマス)か5(壁)
                            cnt_rev=0
                            break
                    for j in range(1,cnt_rev+1):
                        board[place_a+j*i]=player.disc
                    sum_cnt_rev+=cnt_rev
                return sum_cnt_rev
            
            def check(player,board,Re_board):#まだ置ける場所があるかチェックする関数,戻り値→置ける場所の数、座標
                dir=(-11,-10,-9,-1,1,9,10,11)#方向ベクトル
                cnt_can=0#置ける場所の数
                can_place=[]
                for place in Re_board:
                    sum_cnt_rev=0#そこに置いたときに裏返せる枚数
                    if board[place]==0:
                        for i in dir:
                            tmp_place=place#placeの初期化
                            cnt_rev=0#その方向においての裏返せる枚数
                            while board[tmp_place+i]!=player.disc:
                                if board[tmp_place+i]==(int(player.disc)-1.5)*-1+1.5:#ディスクが1なら2,2なら1(相手のディスクがある)
                                    tmp_place+=i
                                    cnt_rev+=1
                                else:#0(空きマス)か5(壁)
                                    cnt_rev=0
                                    break
                            sum_cnt_rev+=cnt_rev
                    else:
                        pass
                    if sum_cnt_rev!=0:
                        cnt_can+=1
                        can_place.append(place)
                    else:
                        pass
                return (cnt_can,can_place)

            def run(player,board):#実行する関数,戻り値→実行ok:1、実行no:0
                #石を置く
                
                a=int(place)
                place_a=(a//8+1)*10+(a%8+1)
                #裏返す
                if board[place_a]==0:
                    sum_cnt_rev=reverse(place_a,board,player)
                    if sum_cnt_rev==0:
                        return 0
                    else:
                        board[place_a]=int(player.disc)
                        return 1
                else:
                    return 0
            
            def cnt_disc(board):
                cnt1=0
                cnt2=0
                for i in Re_board:
                    if board[i]==1:
                        cnt1+=1
                    elif board[i]==2:
                        cnt2+=1
                    else:
                        pass
                return (cnt1,cnt2)
            
            def decide_place(board,can_place):#コンピュータの置く場所を決定
                global com_place
                max_value=-1000
                value=-1000
                tmp_place=[]#置ける場所をコピー
                tmp_board=[]#ボードをコピー
                for i in range(0,len(can_place)):
                    tmp_board.clear()
                    for j in range(0,10):
                        for h in range(0,10):
                            tmp_board.append(board[j*10+h])
                    tmp_place.append((can_place[i]//8+1)*10+can_place[i]%8+1)#8進数から10進数に変換
                    sum_cnt_rev=reverse(tmp_place[i],tmp_board,player2)#仮の盤面を裏返す
                    if sum_cnt_rev==0 or tmp_board[tmp_place[i]]!=0:
                        pass
                    else:
                        tmp_board[tmp_place[i]]=2
                    tmp_cnt_can,prov_place=check(player1,tmp_board,Re_board)#人間が置ける場所の数、座標を入手
                    value_board_com=0#置いたときの盤面を評価
                    value_board_human=0
                    for j in  range(0,8):
                        for h in range(0,8):
                            if tmp_board[(j+1)*10+h+1]==2:
                                value_board_com+=place_value[j*8+h]
                            elif tmp_board[(j+1)*10+h+1]==1:
                                value_board_human+=place_value[j*8+h]
                            else:
                                pass
                    value=tmp_cnt_can*-10+value_board_com+value_board_human*-1#その場所に置いたときの評価値
                    if value>max_value:
                        max_value=value
                        com_place=can_place[i]
                    else:
                        pass
                
            can_place=[]
            finish=False
            global cnt_can
            global cnt_cannot
            self.label_win["text"]=""
            if cnt_can!=0:#置ける
                run_can=run(player1,board)#実行、成功→1
                cnt_can,tmp_can_place=check(player2,board,Re_board)#置ける場所の数、座標を入手
                for i in range(0,len(tmp_can_place)):#座標を10進数→8進数に変換
                    tmp_can_place[i]=(tmp_can_place[i]//10-1)*8+tmp_can_place[i]%10-1
                decide_place(board,tmp_can_place)
                cnt=0
                for i in range(0,8):
                    for j in range(0,8):
                        for r in range(0,len(tmp_can_place)):
                            if tmp_can_place[r]==i*8+j:
                                cnt+=1
                            else:
                                pass
                        if cnt!=0:
                            can_place.append(3)
                        else:
                            can_place.append(0)
                        cnt=0 
                cnt_cannot=0
                if run_can==1:
                    show_board(board,button,can_place,player1,finish)
                else:
                    pass
            else:#置けない
                run_can=1
                cnt_can=1
                if cnt_cannot==1:#相手も置けなかった
                    finish=True
                    show_board(board,button,can_place,player1,finish)
                else:
                    self.label_win["text"]="黒をどこにも置けません\n「決定」を押してください"
                    self.label_win.place(x=500,y=200)
                cnt_cannot+=1
            
        return inner
    
    #コンピュータの番
    def com_run(self):
        def show_board(board,can_place,finish):#盤面をウィンドウに表示
            self.label_turn["text"]="あなたの番です"
            cnt1,cnt2=cnt_disc(board)
            self.label_cnt1["text"]="黒:"+str(cnt1)+""
            self.label_cnt2["text"]="白:"+str(cnt2)+""
            if (cnt1+cnt2==64) or (cnt1==0) or (cnt2==0) or finish==True:
                self.label_turn["text"]=""
                if cnt1>cnt2:
                    self.label_win["text"]=f'黒の勝利です'
                    self.label_win.place(x=500,y=200)
                elif cnt1<cnt2:
                    self.label_win["text"]=f'白の勝利です'
                    self.label_win.place(x=500,y=200)
                else:
                    self.label_win["text"]="引き分けです"
                    self.label_win.place(x=500,y=200)
            else:
                pass
            for i in range(0,8):
                for j in range(0,8):
                    self.button[i*8+j].destroy()
                    
            self.create_button(board,can_place)
            for i in range(0,8):
                for j in range(0,8):
                    self.button[i*8+j].place(x=j*60,y=i*60,width=60,height=60,)
                    self.button[i*8+j].configure(bg="green")
            
        def reverse(place_a,board,player):#裏返す関数、戻り値→裏返せる枚数
            dir=(-11,-10,-9,-1,1,9,10,11)#方向ベクトル
            sum_cnt_rev=0#そこに置いたときに裏返せる枚数
            for i in dir:
                tmp_place=place_a#placeの初期化
                cnt_rev=0#その方向においての裏返せる枚数
                while board[tmp_place+i]!=player.disc:
                    if board[tmp_place+i]==(int(player.disc)-1.5)*-1+1.5:#ディスクが1なら2,2なら1(相手のディスクがある)
                        tmp_place+=i
                        cnt_rev+=1
                    else:#0(空きマス)か5(壁)
                        cnt_rev=0
                        break
                for j in range(1,cnt_rev+1):
                    board[place_a+j*i]=player.disc
                sum_cnt_rev+=cnt_rev
            return sum_cnt_rev
        
        def check(player,board,Re_board):#まだ置ける場所があるかチェックする関数,戻り値→置ける場所の数、座標
            dir=(-11,-10,-9,-1,1,9,10,11)#方向ベクトル
            cnt_can=0#置ける場所の数
            can_place=[]
            for place in Re_board:
                sum_cnt_rev=0#そこに置いたときに裏返せる枚数
                if board[place]==0:
                    for i in dir:
                        tmp_place=place#placeの初期化
                        cnt_rev=0#その方向においての裏返せる枚数
                        while board[tmp_place+i]!=player.disc:
                            if board[tmp_place+i]==(int(player.disc)-1.5)*-1+1.5:#ディスクが1なら2,2なら1(相手のディスクがある)
                                tmp_place+=i
                                cnt_rev+=1
                            else:#0(空きマス)か5(壁)
                                cnt_rev=0
                                break
                        sum_cnt_rev+=cnt_rev
                else:
                    pass
                if sum_cnt_rev!=0:
                    cnt_can+=1
                    can_place.append(place)
                else:
                    pass
            return (cnt_can,can_place)
        
        def run(player,board):#実行する関数,戻り値→実行ok:1、実行no:0
            #場所を決めて、石を置く
            a=com_place
            
            place_a=(a//8+1)*10+(a%8+1)
            #裏返す
            sum_cnt_rev=reverse(place_a,board,player)
            if sum_cnt_rev==0 or board[place_a]!=0:
                return 0
            else:
                board[place_a]=int(player.disc)
                return 1
        
        def cnt_disc(board):
            cnt1=0
            cnt2=0
            for i in Re_board:
                if board[i]==1:
                    cnt1+=1
                elif board[i]==2:
                    cnt2+=1
                else:
                    pass
            return (cnt1,cnt2)
            
        can_place=[]
        finish=False
        global cnt_can
        global cnt_cannot
        self.label_win.pack_forget()
            #1の番
        if cnt_can!=0:#置けた場合
            run_can=run(player2,board)
            cnt_can,tmp_can_place=check(player1,board,Re_board)
            for i in range(0,len(tmp_can_place)):
                tmp_can_place[i]=(tmp_can_place[i]//10-1)*8+tmp_can_place[i]%10-1
            cnt=0
            for i in range(0,8):
                for j in range(0,8):
                    for r in range(0,len(tmp_can_place)):
                        if tmp_can_place[r]==i*8+j:
                            cnt+=1
                        else:
                            pass
                    if cnt!=0:
                        can_place.append(3)
                    else:
                        can_place.append(0)
                    cnt=0 
            if run_can==1:
                show_board(board,can_place,finish)
            else:
                pass
        else:
            run_can=1
            cnt_can=1
            if cnt_cannot==1:#相手も置けなかった
                finish=True
                show_board(board,can_place,finish)
            else:
                self.label_win["text"]="コンピュータは\nどこにも置けません"
                self.label_win.place(x=500,y=200)
                self.label_turn["text"]="あなたの番です"
            cnt_cannot+=1
            
win=Tk()
win.title("オセロ")
win.geometry('700x500')
app=Application(win=win)
app.mainloop()
0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?