LoginSignup
0
0

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

Last updated at Posted at 2023-09-11

前回のオセロから、マスごとに評価値を設定して、コンピュータが置けるなかで一番評価値が高いマスに置く、というプログラムを作りました。
もちろん激ヨワです。オセロ苦手な自分でも勝てます。ここから、評価値に「置いたとき相手が置ける場所の数」「相手に囲まれているマス」などを重み付けして足すことで、強くしていきます。

予定は、
①自分で重み調整
②AIにパラメータとして与えて重み調整←やっとAI!!!
③何手か先まで予測させて、ミニマックス法かαβ法
④何手か先まで予測を前提として、AIを使って重み調整

楽しみです。9月中には完成させたい。

↓ソースコード

オセロ(人vsコンピュータ)
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=[ 70,-20, 60, -10, -10, 60,-20, 70,
             -20,-60,-30, -5, -5,-30,-60,-20,
              60,-30, 25,  10,  10, 25,-30, 60,
              -10, -5,  10,-25,-25,  10, -5, -10,
              -10, -5,  10,-25,-25,  10, -5, -10,
              60,-30, 25,  10,  10, 25,-30, 60,
             -20,-60,-30, -5, -5,-30,-60,-20,
              70,-20, 60, -10, -10, 60,-20, 70]
cnt_cannot=0
com_place=19

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):
    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)
                #裏返す
                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)
            
            def decide_place(can_place):#コンピュータの置く場所を決定
                global com_place
                tmp_max=-100
                max_value=-100
                for i in range(0,len(can_place)):
                    tmp_max=place_value[can_place[i]]
                    if tmp_max>max_value:
                        max_value=tmp_max
                        com_place=can_place[i]
                    else:
                        pass
                
            can_place=[]
            finish=False
            global cnt_can
            global cnt_cannot
            self.label_win["text"]=""
                #1の番
            if cnt_can!=0:#置ける
                run_can=run(player1,board)
                cnt_can,tmp_can_place=check(player2,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
                decide_place(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
0
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
0