AI實現的兩種方案,暴力推演與因果率
阿新 • • 發佈:2018-12-23
AI實現的兩種方案,暴力推演與因果率
學習PYTHON兩個月,寫個小遊戲練手。也為以後找工作做儲備。
從最簡單的九格棋入手。
九格棋玩法簡單,橫向,縱向,斜向三子連線則為勝。
基本設計構件有:
一、GUI介面。
介面我選用PYGAME做的。因為PYGAME中沒有提供按鈕等相關控制元件,所以只好自己做一下簡單的按鈕、資訊框等。
二、遊戲規則。
需要裁判來限定落子規則,判定勝負結果。
三、AI
最簡單的方法是找出每一步走法只的每種可能性,遍歷這些可能性,並遞迴推演到最後一步(九格棋最多至9步)。將推演的勝負結果儲存,然後計算每種走法的勝負數,及勝負概率。
這種方法可謂簡單除暴。只有9層的推演,生成50萬條記錄,單執行緒用時6小時。
這種方法生成的資料最全面完整,簡單使用勝率排序就能達到很好的效果。但是有些特別情況,單一勝率就不是最好的選擇,例如勝率49%,負率51%,雖然勝率很高,但負率更高,這種冒險走法就不是最好的選擇。這時就要加入其它引數做綜合排序。
暴力推演效果雖好,但用時太長,只適合可能性較少的模型。試想一下,如果用這種方法去推演五子棋,跳棋,象棋,圍棋,要產生多大的資料,又要耗時多久?顯然是不現實的。所以需要用因果率方案實現AI,具體方法就是有限推演和互動經驗。
在九格棋遊戲中也會記錄每次對局的過程和結果,做為綜合排序的一項引數。
製作過程:
先製作單人單機,製作介面和控制元件。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @File : 九子棋_單人.py # @Author: Jin peng # @Date : 2018/12/10 0010 # @Desc : # import pygame # from pygame.locals import * import sys,time,os from 控制元件類 import * from 裁判類 import * class 棋子類(pygame.sprite.Sprite): # 黑方=黑方 # 白方=白方 def __init__(self, 父, x, y, style='黑方'): super().__init__() self.父 = 父 self.style = style if style == '黑方': self.image = 黑方 else: self.image = 白方 self.rect = self.image.get_rect() # self.time1 = round(time.time()) self.rect.centerx = x # pygame.rect.Rect.centerx self.rect.centery = y # self.update() def update(self): self.父.blit(self.image, self.rect) def 畫格(bg1): for i in range(4): pygame.draw.line(bg1, [100, 100, 100], [i*200, 0], [i*200, 600], 3) pygame.draw.line(bg1, [100, 100, 100], [0,i * 200], [600,i * 200], 3) # pygame.image.save(bg1,'bg111.jpg') def 退出程式(): pygame.quit() sys.exit() def checkForQuit(): for event in pygame.event.get(QUIT): # get all the QUIT events 退出程式() # terminate if any QUIT events are present for event in pygame.event.get(KEYUP): # get all the KEYUP events if event.key == K_ESCAPE: 退出程式() # terminate if the KEYUP event was for the Esc key pygame.event.post(event) # put the other KEYUP event objects back def 顯示所有按鈕(): for theclass in 按鈕類.allobj: theclass.顯示() def 檢測移動(): for event in pygame.event.get(MOUSEMOTION): mx, my = event.pos for theclass in 按鈕類.allobj + 文字框類.allobj: movein = theclass.進入(mx, my) if movein: 音效1.play() break def 檢測按下(): for event in pygame.event.get(MOUSEBUTTONDOWN): # 發生滑鼠左鍵按下事件,傳遞給所有按鈕類控制元件 if event.button == 1: mx, my = event.pos for theclass in 按鈕類.allobj: click = theclass.點選(mx, my) if click: 執行按鈕(theclass) break # 發生滑鼠滾輪事件,傳遞給所有文字框類控制元件 elif event.button in [4,5]: mx, my = event.pos for theclass in 文字框類.allobj: theclass.滾動(mx, my,event.button) # print('上滾') def 檢測放開(): for event in pygame.event.get(MOUSEBUTTONUP): if event.button == 1: mx, my = event.pos if 0 < mx < 600 and 0 < my < 600: x = mx // 200 y = my // 200 落子(x, y) for theclass in 按鈕類.allobj: theclass.放開() def 落子(x, y): global 狀態, 落子方 ok = 裁判.down(y,x,落子方) if ok: px = x * 200 + 100 py = y * 200 + 100 新棋子 = 棋子類(screen, px, py, 落子方) 棋子組.add(新棋子) 音效2.play() 文字框.print(落子方+' x='+str(x)+' y='+str(y)) 遊戲狀態() return else: 音效3.play() # print(棋子組.sprites()) def 遊戲狀態(): global 狀態, 落子方, 步數 落子方1 = 落子方 狀態, 落子方, 步數 = 裁判.遊戲狀態() msn = 狀態 if 狀態 == '等待': msn = '等待 ' + 落子方 + ' 落子' elif 狀態 == '勝利': msn = 落子方1 + ' 勝利!' 音效勝利.play() 文字框.print(msn) else: 文字框.print(msn) 資訊框1.標題 = msn 資訊框1.更新() if 步數>1: 悔棋按鈕.更改狀態(1) 儲存按鈕.更改狀態(1) else: 悔棋按鈕.更改狀態(4) 儲存按鈕.更改狀態(4) def 執行按鈕(obj): if obj == 退出按鈕: 退出程式() elif obj == 開始按鈕: if 開始按鈕.標題=='開始': if 裁判.開始(): 開始按鈕.標題 = '結束' 棋子組.empty() 遊戲狀態() elif 開始按鈕.標題=='結束': if 裁判.結束(): 開始按鈕.標題 = '開始' 棋子組.empty() 遊戲狀態() # 開始遊戲按鈕.更改狀態(4) elif obj == 悔棋按鈕: if 裁判.悔棋(): 棋子組.remove(棋子組.sprites()[-1]) # print(棋子組.sprites()) 遊戲狀態() elif obj == 儲存按鈕: 走棋資料=裁判.返回儲存() now=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) csvdata=[[now],走棋資料] csvfile=open('save.csv','a') csvfile.write(str(csvdata)+'\n') csvfile.close() 文字框.print('儲存為'+now) elif obj == 讀取按鈕: 存檔=讀取存檔(1) if not 存檔: return 文字框.print('讀取' + str(存檔[0])) # print(存檔[1], '----') 裁判.開始() 棋子組.empty() 遊戲狀態() for i in 存檔[1]: 落子(i[1],i[0]) def 讀取存檔(n=0): filename='save.csv' if not (os.path.isfile(filename)): 文字框.print('沒有儲存資料') return csvfile=open('save.csv','r') csvdata=csvfile.readlines() if n!=0: return eval(csvdata[n]) # print(type(csvdata)) # csvdata=[] # csvdata=temp # i=0 # for line in csvfile: # i+=1 # # line = csvfile.readline() # linedata=eval(line) # if n!=0: # return linedata # csvdata.append(linedata) # print(line,'----',i) return csvdata pygame.mixer.pre_init(44100, 16, 2, 4096) pygame.init() screen = pygame.display.set_mode((800, 600), 0, 32) pygame.display.set_caption('九子棋_單人單機') clock = pygame.time.Clock() thefont = pygame.font.Font('FZSHJW.TTF', 18) 音效1 = pygame.mixer.Sound("音效_進入.wav") 音效2 = pygame.mixer.Sound("音效_落子.wav") 音效3 = pygame.mixer.Sound("音效_錯誤.wav") 音效勝利 = pygame.mixer.Sound("音效_勝利.wav") 音樂1 = pygame.mixer.music.load('音樂1.wav') pygame.mixer.music.play(-1) bg1 = pygame.image.load('bg1.jpg') # 畫格(bg1) bg2 = pygame.image.load('bg2.jpg') 黑方 = pygame.image.load('棋子1.png') 白方 = pygame.image.load('棋子2.png') 棋子組 = pygame.sprite.Group() 控制元件組 = pygame.sprite.Group() 開始按鈕 = 按鈕類(父=screen, x=620, y=20, 標題='開始', 寬=80, 高=40) 退出按鈕 = 按鈕類(父=screen, x=710, y=20, 標題='退出', 寬=80, 高=40) 悔棋按鈕 = 按鈕類(父=screen, x=620, y=70, 標題='悔棋', 寬=80, 高=40, 狀態=1) 交換按鈕 = 按鈕類(父=screen, x=710, y=70, 標題='交換', 寬=80, 高=40, 狀態=4) 儲存按鈕 = 按鈕類(父=screen, x=620, y=120, 標題='儲存', 寬=80, 高=40, 狀態=4) 讀取按鈕 = 按鈕類(父=screen, x=710, y=120, 標題='讀取', 寬=80, 高=40) 資訊框1 = 標籤類(父=screen, x=620, y=170, 標題='計時', 寬=170, 高=40) 文字框 = 文字框類(父=screen, x=620, y=220, 寬=170, 高=320) 控制元件組.add(開始按鈕) 控制元件組.add(退出按鈕) 控制元件組.add(悔棋按鈕) 控制元件組.add(交換按鈕) 控制元件組.add(儲存按鈕) 控制元件組.add(讀取按鈕) 控制元件組.add(資訊框1) 控制元件組.add(文字框) 裁判 = 裁判類() 狀態, 落子方, 步數 = '', '', 0 文字框.print(裁判.game_str) # for i in range(20): # 文字框.print(str(i)) 遊戲狀態() # print(開始遊戲按鈕.主體.get_rect().x) def main(): while True: checkForQuit() screen.blit(bg1, (0, 0)) screen.blit(bg2, (600, 0)) # 顯示所有按鈕() 檢測移動() 檢測按下() 檢測放開() 棋子組.update() 控制元件組.update() pygame.display.update() clock.tick(30) if __name__ == '__main__': main()
控制元件類
#!/usr/bin/env python # -*- coding: utf-8 -*- # @File : 控制元件類.py # @Author: Jin peng # @Date : 2018/12/10 0010 # @Desc : import pygame from pygame.locals import * class 按鈕類(pygame.sprite.Sprite): allobj = [] # 此類所有按鈕 def __init__(self, 父=pygame.display, x=0, y=0, 寬=100, 高=30, 標題='按鈕', 狀態=1): super().__init__() self.父 = 父 self.x = x self.y = y self.寬 = 寬 self.高 = 高 self.標題 = 標題 self.狀態 = 狀態 # 0不可見,1正常,2滑鼠進入或選中,3點選,4無效 self.邊 = 2 self.字型大小 = 16 self.底色 = [225, 245, 255] # 當前背景色 self.底色1 = [225, 245, 255] # 原始背景色 self.底色2 = [205, 225, 255] # 啟用背景色 self.底色3 = [200, 200, 250] # 點選背景色 self.底色4 = [180, 180, 180] # 無效背景色 self.邊色 = [150, 180, 190] self.字色 = [20, 20, 20] self.更新() self.allobj.append(self) def 更新(self): if self.狀態 == 0: # del self.主體 self.主體 = None return elif self.狀態 == 1: self.底色 = self.底色1 elif self.狀態 == 2: self.底色 = self.底色2 elif self.狀態 == 3: self.底色 = self.底色3 elif self.狀態 == 4: self.底色 = self.底色4 self.主體 = pygame.Surface((self.寬, self.高), flags=SRCALPHA) self.主體.fill(self.邊色) self.主體.fill(self.底色, (self.邊, self.邊, self.寬 - self.邊 * 2, self.高 - self.邊 * 2)) thefont = pygame.font.Font('FZSHJW.TTF', self.字型大小) textSurf = thefont.render(self.標題, True, self.字色) self.主體.blit(textSurf, [(self.主體.get_width() - textSurf.get_width()) // 2, (self.主體.get_height() - textSurf.get_height()) // 2]) def update(self, *args): if self.狀態 != 0: self.父.blit(self.主體, (self.x, self.y)) def 進入(self, mx, my): if self.狀態 in [1, 2]: if self.x < mx < (self.x + self.寬) and self.y < my < (self.y + self.高): 狀態 = 2 movein = True else: 狀態 = 1 movein = False if self.狀態 != 狀態: self.更改狀態(狀態) return movein def 點選(self, mx, my): if self.狀態 in [1, 2]: if self.x < mx < (self.x + self.寬) and self.y < my < (self.y + self.高): self.更改狀態(3) self.run() return True def run(self): # print(self.標題, '被點選') pass def 放開(self): if self.狀態 == 3: self.更改狀態(1) def 更改狀態(self, 狀態): self.狀態 = 狀態 self.更新() class 標籤類(pygame.sprite.Sprite): allobj = [] # 此類所有控制元件 def __init__(self, 父=pygame.display, x=0, y=0, 寬=100, 高=30, 標題='標籤', 狀態=1): super().__init__() self.父 = 父 self.x = x self.y = y self.寬 = 寬 self.高 = 高 self.標題 = 標題 self.狀態 = 狀態 # 0不可見,1正常,2滑鼠進入或選中,3點選,4無效 self.邊 = 2 self.字型大小 = 16 self.水平對齊 = '左' self.垂直對齊 = '下' self.底色 = [225, 245, 255] # 當前背景色 self.底色1 = [225, 245, 255] # 原始背景色 self.底色2 = [35, 55, 65] # 啟用背景色 self.底色3 = [145, 165, 175] # 點選背景色 self.底色4 = [80, 80, 80] # 無效背景色 self.邊色 = [150, 180, 190] self.字色 = [20, 20, 20] self.更新() self.allobj.append(self) def 更新(self): if self.狀態 == 0: # del self.主體 self.主體 = None return elif self.狀態 == 1: self.底色 = self.底色1 elif self.狀態 == 2: self.底色 = self.底色2 elif self.狀態 == 3: self.底色 = self.底色3 elif self.狀態 == 4: self.底色 = self.底色4 self.主體 = pygame.Surface((self.寬, self.高), flags=SRCALPHA) self.主體.fill(self.邊色) self.主體.fill(self.底色, (self.邊, self.邊, self.寬 - self.邊 * 2, self.高 - self.邊 * 2)) thefont = pygame.font.Font('FZSHJW.TTF', self.字型大小) textSurf = thefont.render(self.標題, True, self.字色) self.主體.blit(textSurf, [(self.主體.get_width() - textSurf.get_width()) // 2, (self.主體.get_height() - textSurf.get_height()) // 2]) def 更改狀態(self, 狀態): self.狀態 = 狀態 self.更新() def update(self, *args): if self.狀態 != 0: self.父.blit(self.主體, (self.x, self.y)) class 文字框類(pygame.sprite.Sprite): allobj = [] # 此類所有控制元件 def __init__(self, 父=pygame.display, x=0, y=0, 寬=100, 高=30, 標題='文字框', 狀態=1): super().__init__() self.父 = 父 self.x = x self.y = y self.寬 = 寬 self.高 = 高 self.標題 = 標題 self.內容 = [] self.狀態 = 狀態 # 0不可見,1正常,2滑鼠進入或選中,3點選,4無效 self.邊 = 2 self.滾動條寬=5 self.滾動條高 = 高 self.滾動條 = pygame.Rect(self.寬, 0, self.滾動條寬, self.滾動條高) self.字型大小 = 12 self.字型 = 'FZSHJW.TTF' self.thefont = pygame.font.Font(self.字型, self.字型大小) self.字間距=2 self.行間距=3 self.字距=self.字型大小+self.字間距 self.行距=self.字型大小+self.行間距 self.每行字數 = (self.寬 - self.滾動條寬 - self.邊 * 2) // self.字型大小 self.最大行數 = (self.高 - self.邊 * 2) // self.行距 self.起始行 = 0 self.記憶行數=100 self.水平對齊 = '左' self.垂直對齊 = '下' self.底色 = [225, 245, 255] # 當前背景色 self.底色1 = [225, 245, 255] # 原始背景色 self.底色2 = [255,255,255] # 啟用背景色 self.底色3 = [145, 165, 175] # 點選背景色 self.底色4 = [80, 80, 80] # 無效背景色 self.邊色 = [150, 180, 190] self.滾動條色=[155, 155, 155] self.字色 = [20, 20, 20] self.更新() self.allobj.append(self) def 更新(self): if self.狀態 == 0: self.主體 = None return elif self.狀態 == 1: self.底色 = self.底色1 elif self.狀態 == 2: self.底色 = self.底色2 elif self.狀態 == 3: self.底色 = self.底色3 elif self.狀態 == 4: self.底色 = self.底色4 self.主體 = pygame.Surface((self.寬, self.高), flags=SRCALPHA) self.主體.fill(self.邊色) self.主體.fill(self.底色, (self.邊, self.邊, self.寬 - self.邊 * 2, self.高 - self.邊 * 2)) self.print() def 更改狀態(self, 狀態): self.狀態 = 狀態 self.更新() def 進入(self, mx, my): if self.狀態 in [1, 2]: if self.x < mx < (self.x + self.寬) and self.y < my < (self.y + self.高): 狀態 = 2 movein = True else: 狀態 = 1 movein = False if self.狀態 != 狀態: self.更改狀態(狀態) return movein def 滾動(self,mx,my,方向=4): if self.狀態 !=2: return False if len(self.內容)<self.最大行數: return False if not (self.x < mx < (self.x + self.寬) and self.y < my < (self.y + self.高)): return False if 方向==4: self.起始行-=1 if self.起始行<0: self.起始行=0 elif 方向==5: self.起始行 += 1 if self.起始行 > len(self.內容)-self.最大行數: self.起始行 = len(self.內容)-self.最大行數 # print(self.起始行) self.print() def print(self, msn=''): self.主體.fill(self.底色, (self.邊, self.邊, self.寬 - self.邊 * 2, self.高 - self.邊 * 2)) if msn: while len(msn) > self.每行字數: tempstr = msn[:self.每行字數] self.內容.append(tempstr) msn = msn[self.每行字數:] self.內容.append(msn) if len(self.內容)>self.記憶行數: self.內容=self.內容[-self.記憶行數:] if len(self.內容)>self.最大行數: self.起始行 = len(self.內容) - self.最大行數 else: self.起始行=0 # print(self.起始行) ii=0 for i in self.內容[self.起始行:]: textSurf = self.thefont.render(i, True, self.字色) self.主體.blit(textSurf, [3, ii * self.行距+3]) ii+=1 if len(self.內容)>self.最大行數: self.滾動條高=self.最大行數/len(self.內容)*self.高-self.邊*2 else: self.滾動條高 = self.高 - self.邊 * 2 self.滾動條=pygame.Rect(0,0,self.滾動條寬,self.滾動條高) self.滾動條.right=self.寬-self.邊 if len(self.內容)>0: self.滾動條.top = self.邊+self.起始行/len(self.內容)*self.高 self.主體.fill(self.滾動條色,self.滾動條) def update(self, *args): if self.狀態 != 0: self.父.blit(self.主體, (self.x, self.y))
裁判類
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @File : 裁判類.py
# @Author: Jin peng
# @Date : 2018/12/10 0010
# @Desc :
class 裁判類():
game_str='''
遊戲規則:二人遊戲。
雙方分別執黑子、白子在3*3的9格棋盤上落子。
甲方執黑子,乙方執白子。
甲方先落子,然後雙方輪流落子。
棋子只能落在棋盤格內。
棋盤每格只能放一子,每格不能重複落子。
獲勝條件:一方在棋盤內橫向、豎向、斜向有三子連續為勝。
遊戲內容:使己方三子連續,並阻止對方三子連續。
'''
'''
遊戲資料模型:
棋盤是3*3列表
列表中對應的值為0、1、2
0表示空位,沒有落子。
1表示甲方落的黑子。
2表示乙方落的白子。
STEP記錄落子的順序和位置。
self.__step [x,y,落子方]
'''
def __init__(self):
self.__棋盤=[]
self.__step=[]
self.__step_n=0
self.__downing=0
self.__落子方=''
self.__狀態='等待開始'
def down(self,x,y,落子方):
if self.__狀態!='等待':
return False
if 落子方!=self.__落子方:
return False
if x<0 or x>2 or y<0 or y>2 :
return False
if self.__棋盤[x][y]!=0:
return False
self.__棋盤[x][y]=self.__downing
if self.win(x,y):
self.__狀態 = '勝利'
elif self.__step_n>=9:
self.__狀態 = '平局'
else:
self.__狀態 = '等待'
self.__step.append([x,y,self.__downing])
self.__step_n+=1
self.__who()
# print(self.__step)
# for i in self.__棋盤:
# print(i)
return True
def __who(self):
self.__downing=self.__step_n%2
if self.__downing==1:
self.__落子方 ='黑方'
else:
self.__落子方 = '白方'
self.__downing=2
def win(self,x,y):
if self.__棋盤[0][y]==self.__downing and \
self.__棋盤[1][y] == self.__downing and \
self.__棋盤[2][y] == self.__downing:
return True
if self.__棋盤[x][0]==self.__downing and \
self.__棋盤[x][1] == self.__downing and \
self.__棋盤[x][2] == self.__downing:
return True
if self.__棋盤[0][0]==self.__downing and \
self.__棋盤[1][1] == self.__downing and \
self.__棋盤[2][2] == self.__downing:
return True
if self.__棋盤[0][2]==self.__downing and \
self.__棋盤[1][1] == self.__downing and \
self.__棋盤[2][0] == self.__downing:
return True
return False
def 遊戲狀態(self):
return self.__狀態,self.__落子方,self.__step_n
pass
def 開始(self):
self.__棋盤=[[0 for i in range(3)] for i in range(3)]
self.__step=[]
self.__step_n=1
self.__downing=1
self.__落子方=''
self.__狀態='等待'
self.__who()
return True
def 結束(self):
self.__棋盤=[]
self.__step=[]
self.__step_n=0
self.__downing=0
self.__落子方=''
self.__狀態='等待開始'
return True
def 悔棋(self):
if self.__step_n<2:
return False
the=self.__step[-1]
# print(the)
self.__棋盤[the[0]][the[1]]=0
self.__step.pop()
self.__step_n-=1
self.__狀態 = '等待'
self.__who()
return True
def 返回儲存(self):
return self.__step
def 可選位置(self):
tmp=[]
for i in range(len(self.__棋盤)):
for j in range(len(self.__棋盤[i])):
if self.__棋盤[i][j]==0:
tmp.append([i,j])
return tmp
暴力推演主要程式碼:
def 推演(self):
累計 = 總數=0
total = 1
self.總計數 = [[0, 0, 0, 0, 0]]
# [當前層已驗證數,當前層總數,到此層累計總數,此層所用時間,單步平均時間]
for i in range(9, 0, -1):
total = total * i
總數+=total
self.總計數.append([0, total, 總數, 0, 0])
# print(i, total, 總數)
推演資料鏈 = []
self.推演起始層時間 = time.time()
while self.一層:
s1 = self.一層[0]
if s1[-1] > 0 or s1[-2] > 0:
# 此步勝利或平局,不能再向下推
self.一層.pop(0)
continue
if s1[0] > 1:
# 層數起過2,需要將此層之前的步鏈找出
推演資料鏈 = self.返還推演資料鏈(s1)
if 裁判.開始():
# 清空棋盤,重新開始
棋子組.empty()
遊戲狀態()
# 走出步鏈
for link in 推演資料鏈:
落子(link[5], link[4])
pass
落子(s1[5], s1[4])
self.up_id = s1[2]
可選位置2 = 裁判.可選位置()
self.走法數 = len(可選位置2)
# print('落子資料鏈', self.落子資料鏈)
# for cc in self.落子資料鏈:
# print(self.ai_data[cc[0]][cc[1]])
while 可選位置2:
i = 可選位置2[0]
落子(i[1], i[0])
重新整理窗體()
累計 += 1
self.總計數[self.第幾步][0] +=1
self.the_id += 1
self.x = i[0]
self.y = i[1]
狀態=裁判.遊戲狀態()[0]
if 狀態 == '勝利':
# self.勝利數 = 1
勝利了 = 1
self.__更改資料鏈勝率(s1)
elif 狀態 == '平局':
# self.平局數 = 1
平局了 = 1
self.__更改資料鏈平率(s1)
else:
勝利了 = 0
平局了 = 0
self.二層.append([ \
self.第幾步, self.走法數, self.the_id, self.up_id, self.x, self.y, \
self.勝利數, self.平局數, self.失敗數, 勝利了, 平局了])
if len(self.ai_data) <= self.第幾步:
self.ai_data.append([self.第幾步])
# self.ai_data[self.第幾步].append([ \
# self.第幾步, self.走法數, self.the_id, self.up_id, self.x, self.y, \
# self.勝利數, self.平局數, self.失敗數,勝利了,平局了])
self.ai_data[self.第幾步] = self.二層.copy()
if 裁判.悔棋():
棋子組.remove(棋子組.sprites()[-1])
遊戲狀態()
可選位置2.pop(0)
self.一層.pop(0)
if len(self.一層) > 0:
if 裁判.悔棋():
棋子組.remove(棋子組.sprites()[-1])
遊戲狀態()
else:
# self.save_txt(self.二層)
self.一層 = self.二層.copy()
# self.ai_data.append(self.二層)
self.二層 = []
self.the_id = 0
self.__顯示推演用時()
# self.save_txt(self.ai_data, 'alldata.txt')
msn1 = '全部步數:' + str(self.第幾步) + ' / 9'
資訊框1.標題 = msn1
資訊框1.更新()
self.第幾步 += 1
# print(self.ai_data)
# 文字框.print('已推演全部步數:' + str(len(self.ai_data))+ \
# '當前步數:' + str(len(self.ai_data[-1])))
msn2 = '當前步數:' + str(self.總計數[self.第幾步][0]) \
+ ' / ' + str(self.總計數[self.第幾步][1])
# msn3 = '總數:' + str(累計)\
# + ' / ' + str(self.總計數[9][2])
msn3 = '總數:' + str(self.總計數[self.第幾步][0]+self.總計數[self.第幾步-1][2])\
+ ' / ' + str(self.總計數[9][2])
資訊框2.標題 = msn2
資訊框2.更新()
資訊框3.標題 = msn3
資訊框3.更新()
# 文字框.print(str(總計數[self.第幾步]))
pass
開始按鈕.更改狀態(1)
文字框.print('推演結束!')
起初用列表儲存遞推結果,覺得浪費記憶體,改用指標,但沒有明顯提升效能。看來已經是單執行緒極限了。
推演生成資料庫23M
AI演算法及綜合排序:
class AI9():
# 資料結構:
# ID,上級ID,層數,下級總數,已推下級數,
# X,Y,結果1勝2平,勝利數,平局數,
# 失敗數,勝率,和率,負率
def __init__(self):
self.ai_data = []
self.第幾層 = 1
self.走法數 = 0
self.the_id = 0
self.up_id = 0
self.AI方='黑方'
self.step_list=[]
self.conn=sqlite3.connect('試驗資料.db')
# rs=conn.cursor()
# conn.commit()
# conn.close()
def 響應(self):
def tolist(onelist):
newlist = []
for i in onelist:
newlist.append(list(i))
return newlist
# 狀態=game.遊戲狀態標緻
狀態, 落子方, 第幾步=game.裁判.遊戲狀態()
if 狀態 == '勝利':
奇步id=[]
偶步id=[]
for i in self.step_list:
if i[2]%2==1:
奇步id.append(i[0])
else:
偶步id.append(i[0])
if 第幾步%2==1:
sqlstr='update ai_data set 勝經驗=勝經驗+1 where id in %s'%str(tuple(偶步id))
self.conn.execute(sqlstr)
sqlstr='update ai_data set 負經驗=負經驗+1 where id in %s'%str(tuple(奇步id))
self.conn.execute(sqlstr)
self.conn.commit()
else:
sqlstr='update ai_data set 勝經驗=勝經驗+1 where id in %s'%str(tuple(奇步id))
print(sqlstr)
self.conn.execute(sqlstr)
sqlstr='update ai_data set 負經驗=負經驗+1 where id in %s'%str(tuple(偶步id))
self.conn.execute(sqlstr)
self.conn.commit()
if 落子方=='白方':
if self.AI方=='白方':
msn='人類獲勝'
else:
msn='AI獲勝'
else:
if self.AI方 == '黑方':
msn = '人類獲勝'
else:
msn = 'AI獲勝'
game.資訊框1.標題 = msn
game.資訊框1.更新()
return
if 狀態 == '平局':
平局id = []
for i in self.step_list:
平局id.append(i[0])
sqlstr='update ai_data set 平經驗=平經驗+1 where id in %s'%str(tuple(平局id))
self.conn.execute(sqlstr)
self.conn.commit()
if 狀態!= '等待' or 落子方!=self.AI方:
return
落子鏈 = game.裁判.返回儲存()
# print('落子鏈',落子鏈)
# print('step_list', self.step_list)
最佳位置=[]
if len(self.step_list)+1==len(落子鏈):
#找對方落子id
urlstr='''
select * from ai_data where
uid=%d
and 層數=%d
and x=%d
and y=%d '''\
%(self.up_id,第幾步-1,落子鏈[-1][1],落子鏈[-1][0])
# print(urlstr)
走法 = list(self.conn.execute(urlstr))
self.step_list.append(走法[0])
self.up_id = 走法[0][0]
# print(走法)
urlstr= 'select * from ai_data where uid=%d and 層數=%d' % (self.up_id, 第幾步)
走法all=tolist(self.conn.execute(urlstr))
走法=self.綜合排序(走法all)
最佳位置 = 走法[0]
if self.AI方==game.落子方:
self.step_list.append(最佳位置)
self.up_id=最佳位置[0]
game.落子(最佳位置[5],最佳位置[6])
# print('走法[0]',走法[0])
# print(self.step_list)
# print(self.up_id)
def 綜合排序(self,onelist):
def 加序號列(onelist, 權重=1):
newlist = onelist
nob = 權重
for i in newlist:
i.append(nob)
nob += 1
return newlist
def 刪除序號列(onelist, nob):
for i in onelist:
del i[-nob:]
return onelist
正順序 = True
負順序 = False
勝負結果 = sorted(onelist, key=lambda mylist: mylist[8], reverse=正順序)
for i in 勝負結果:
if i[8]==9:
return 勝負結果
break
# 勝負結果 = 加序號列(勝負結果, 權重=-20)
# self.printlist('勝負結果',勝負結果)
排序勝算 = sorted(
onelist,
key=lambda mylist: mylist[9] - mylist[11],
reverse=正順序)
排序勝算 = 加序號列(排序勝算, 權重=1)
排序勝率 = sorted(
onelist,
key=lambda mylist: mylist[9] / (1+mylist[9] + mylist[10] + mylist[11]),
reverse=正順序)
排序勝率 = 加序號列(排序勝率)
排序勝經驗 = sorted(
onelist,
key=lambda mylist: mylist[12] - mylist[14],
reverse=正順序)
排序勝經驗 = 加序號列(排序勝經驗)
排序負數 = sorted(onelist, key=lambda mylist: mylist[11], reverse=負順序)
排序負數 = 加序號列(排序負數)
排序負經驗 = sorted(onelist, key=lambda mylist: mylist[14], reverse=負順序)
排序負經驗 = 加序號列(排序負經驗)
總序 = sorted(
onelist,
key=lambda mylist: mylist[-1] + mylist[-2] + mylist[-3] + mylist[-4]*2 + mylist[-5]*2,
reverse=負順序)
self.printlist('總序',總序)
刪除序號列(總序, 5)
return 總序
程式原始碼及所有資料與大家分享
https://pan.baidu.com/s/1oo-UCfalKJ52joME0TAzfA
有興趣的朋友可加好友,共同研究。qq:3152506