簡 述
雖然計算機已經幾乎破解了五子棋的取勝祕籍,甚至給出了取勝的具體方案,然而,對人來說,五子棋還是非常有玩頭的。
我們往往有五子棋的技巧性和全局觀遠遠比不上象棋,圍棋之類的感覺:
這個真不一定,先說技巧性:五子棋、象棋、圍棋的最初級技巧都是死活題。圍棋那高難度的生死題我就不多說了。而象棋如果只是說鐵門栓天地炮等等殺法,其實還是很好掌握的;如果加上各種基礎的殘局估計差不多。五子棋的話,坂田三手勝與天狗道場,或者是貼吧裏邊各種變態殺法題,也不敢說簡單。
扯遠啦~,這篇文章主要是要用python來實現五子棋的人機對戰,可以趣味性地玩一下,遠沒有到不可戰勝的程度。
問題描述
人機對弈算法屬於策略型人工智能算法,本遊戲中設置了人機對弈的遊戲模式,整個程序我們有幾個大的問題需要解決:
1)、計算機需要判斷勝負
2)、計算機落子的邏輯
第一個問題的核心思想是要設置對局結束的判斷邏輯,在這部分我們只需要寫出五子相連的判斷條件;
第二個問題的核心思想是要比較不同落子的優劣勢,需要評估每一步的勝算。
其算法如下:
- 寫出獲勝邏輯或者設置所有獲勝組合
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
● |
獲勝邏輯:一個二維座標上,判斷上下、左右、兩個45度直線,是否有五個相同的直連棋子。
- 評估棋格獲勝分數
在計算機下棋之前,會計算空白棋格上的獲勝分數,根據分數高低獲取最佳位置。計算機會將棋子下在獲勝分數最高的地方。
當已放置4顆棋子時,必須在第五個空棋格上設置絕對高的分值。
- 計算機的攻擊與防守
計算機計算獲勝分值越高的棋格,就能確定能讓自己的棋子最有可能達成聯機的位置,也就是最佳進攻位置,而一旦計算機能確定自己的最高分值的位置,計算機就具備了進攻能力。同理,計算機能計算出玩家的最大分值位置,並搶先玩家獲得該位置,這樣計算機就具有了防禦的能力。
代碼實現
- 棋盤
棋盤是我們整個遊戲的落子範圍,需要提前定義好大小:
# 畫棋盤
def GobangWin():
gw = GraphWin('AI Gobang', GRID_WIDTH*COLUMN, GRID_WIDTH*ROW)
gw.setBackground('gray')
for j in range(0, GRID_WIDTH*COLUMN+1, GRID_WIDTH):
l = Line(Point(j, 0), Point(j, GRID_WIDTH*COLUMN))
l.draw(gw)
for i in range(0, GRID_WIDTH*ROW+1, GRID_WIDTH):
l = Line(Point(0, i), Point(GRID_WIDTH*ROW, i))
l.draw(gw)
return gw
- 棋子
在棋盤上畫一個棋子:
col =(255, 0, 0)
surf.fill((255, 255, 255))
pygame.gfxdraw.aacircle(surf, x, y, 30, col)
pygame.gfxdraw.filled_circle(surf, x, y, 30, col)
-
落子
通過鼠標點擊的位置記錄落子,這裏核心是要實現的點擊鼠標獲取座標,可以使用Graphics
from graphics import *
#設置畫布窗口名和尺寸
win = GraphWin('hehe', 666, 666)
#關閉畫布窗口
win.getMouse()
win.close()
#畫點
pt = Point(100, 100)
pt.draw(win)
#畫圓
cir = Circle(Point(200, 200), 75)
cir.draw(win)
cir.setOutline('red') #外圍輪廓顏色
cir.setFill('yellow') #填充顏色
#畫線
line = Line(Point(650, 100), Point(250, 100))
line.draw(win)
#畫矩形
rect = Rectangle(Point(300, 300), Point(400, 400))
rect.setFill('red') #填充顏色
rect.draw(win)
#畫橢圓
oval = Oval(Point(450, 450), Point(600, 600))
oval.setFill('red') #填充顏色
oval.draw(win)
#顯示文字
message = Text(Point(win.getWidth()/2, 20), 'Click anywhere to quit.')
message.draw(win)
-
判斷輸贏
一個二維座標上,判斷上下、左右、兩個45度直線,是否有五個相同的直連棋子,只要五子相連則遊戲結束。或者遍歷每一種獲勝情況都可以做出判斷:
# 四種情況
def is_GameOver(list_now):
for c in range(COLUMN):
for r in range(ROW):
if r < ROW - 4 and (r, c) in list_now and (r+1, c) in list_now and (r+2, c) in list_now and (r+3, c) in list_now and (r+4, c) in list_now:
return True
elif c < COLUMN - 4 and (r, c) in list_now and (r, c+1) in list_now and (r, c+2) in list_now and (r, c+3) in list_now and (r, c+4) in list_now:
return True
elif r < ROW - 4 and c < COLUMN - 4 and (r, c) in list_now and (r+1, c+1) in list_now and (r+2, c+2) in list_now and (r+3, c+3) in list_now and (r+4, c+4) in list_now:
return True
elif r > 3 and c < COLUMN - 4 and (r, c) in list_now and (r-1, c+1) in list_now and (r-2, c+2) in list_now and (r-3, c+3) in list_now and (r-4, c+4) in list_now:
return True
return False
演示操作
下面直接看一下實際操作結果吧~
這裏執行起來還是挺慢的,在執行邏輯的判斷方面還有很多可以優化的地方。
呼~,傻兒子玩得還不錯,還好贏了,另外這個不是深度學習的實現辦法,如果想嘗試深度學習方法戳這裏:
AlphaZero實戰:從零學下五子棋(附代碼)
https://github.com/junxiaosong/AlphaZero_Gomoku
| 作者Info:
【作者】:A字頭
【原創公衆號】:數據札記倌(Data_Groom)
【簡介】:這是一個堅持原創的技術公衆號,每天堅持推送各種 Python 基礎/進階文章,數據分析,爬蟲實戰,機器學習算法,不定期分享各類學習資源。
【福利】:送你新人大禮包一份,關注微信公衆號,後臺回覆:“CSDN” 即可獲取!