围棋规则的计算机实现
?
版權申明:本文為博主窗戶(Colin Cai)原創,歡迎轉帖。如要轉貼,必須注明原文網址http://www.cnblogs.com/Colin-Cai/p/7502410.html 作者:窗戶QQ:6679072E-mail:6679072@qq.com
提到這個名字,很多人會想到前段時間讓全世界振奮的圍棋人工智能Alphago,想曾經我也了解過一些圍棋的AI。我也正想花點時間說說alphago相關的東西,包括alphago的架構以及模型引申等,不過這篇文章里我只說圍棋規則的實現,和人工智能無關。
規則
說到圍棋規則的實現不得不先說圍棋規則,一般來說,至少有三種圍棋規則:中國規則,日本規則,應氏規則。其實還有中國古代規則,和這三種規則都有一點差別。應氏規則和中國規則實際差距非常非常小,小到很多人認為可以忽略不計。但中國規則和日本規則的差別有些大,個人認為中國規則更科學,日本規則不收單官導致了很多問題,比如盤角曲四算死棋(這一點個人覺得挺讓人吐血,因為如果盤角曲四和雙活同在,那盤角曲四的死毫無道理),再比如不提三目(這個簡直就是強盜邏輯了,至于什么叫不提三目,請自行搜索)。從這一點上,至少中國規則不會導致這樣的爭議,一切實戰解決。另外一點,日本規則的雙活不算目,這個給計算機數目帶來了問題,并且不容易解決。所以,本篇還是基于中國規則。
基本數據結構
很自然的就可以想到,可以用一個19X19的二維數組來代表棋盤上當前的棋面(一般稱枰面)。棋盤上的每個點可以有三種狀態:無子、黑子、白子。那么,這個19X19的二維數組就是基本的數據結構。
下棋
從第一步開始,黑白輪流下,無論對于誰下,其實都是要判斷這個二維數組所下坐標下的點的狀態是不是無子,如果不是無子,當然是不允許下的。
另外一點,還有一個氣緊的問題,就是說,把自己的一塊棋走成沒有氣是不允許的(應氏規則除外,它可自殺),除非可以吃子。氣緊和吃子最終可以歸結為一個算法:判斷連通的一塊棋有沒有氣。這里連通的一塊棋是狹義的,只是通過橫豎緊密的連在一起的才是一塊棋。
?
如上圖,左邊7個黑子緊密的連在一起,我們稱之未一塊。右邊這個中心標記為紅色的黑子,卻不是屬于這一塊的。
看起來稍微形式化一點的定義如下:
先定義坐標相鄰,(A,B)與(C,D)相鄰的意思是A=C且|B-D|=1,或者B=D且|A-C|=1
坐標(A,B)所在的一塊棋是一個坐標的集合S;
坐標(A,B)在S內,坐標(C,D)與(A,B)相鄰,并且(C,D)坐標上有棋子且棋子顏色和(A,B)一致,那么(C,D)也在S內。
以上的內容很像連通圖的定義,實際上,如果把相鄰的同色子的連線當成圖的邊,那么連通的一塊棋實際上就是連通圖,那么判斷一塊棋有沒有氣可以利用連通圖的遍歷,只是如果發現在遍歷的過程中找到一顆棋子有氣,那么整塊棋子都有氣。
要注意打劫,打劫的時候不可立即回提。
如上圖即為打劫。
打劫至少有兩種簡單的判斷手段:
(1)當出現提1子時,記錄當前子的坐標和提子的坐標;若下棋的時候,只提一子,并且上一步對方也是提一子,并且當前子的坐標就是上一步提子坐標,當前提子坐標就是上一步下的棋子坐標,那么則是打劫回提,是犯規的。
(2)每一步都記錄當前的棋面。如果當前下完棋子之后,棋面和上一步沒下時一模一樣,則是打劫回提。
打劫是一種絕對需要避免的同局再現,至于三劫、四劫、長生、雙提這一類導致無勝負的局面,則可以用記錄每一次的棋面,然后與幾次之前的進行對比,如果存在相同,也就是同局再現,可以判斷是無勝負,基本同判斷打劫的算法2,只是不是和上一步的比。
計算
最終計算勝負的時候,自動算十分復雜,之前網絡上的圍棋對戰平臺程序也是反復改進了很久才準確。我們這里只討論手動的方式。
首先是點掉死子。手動一個個的點掉死子自然可以,但效率太低,一般都是一點就點掉“連同”的一片死子。
如圖中兩塊死子,是希望清除其中一個子就清除掉所有其他“連通”的。
這里的連通概念和上面連通的一塊棋有點不同,這里的連通是同一個顏色或者空格在一起的一塊,而之前的只強調一個顏色的一塊。
比如上面的圖的上面那塊權掉白棋死子所在的連通塊是5個白子加旁邊六個空格。
其實依然是圖,只是遍歷圖的時候邊的定義改了一下,之前是相鄰棋子顏色相同則是邊,現在是相鄰兩個左邊不出現不是死子顏色的是邊。
這樣就可以遍歷死子,確定一個死子坐標,就可以掃掉所有與之“相連”的死子。
點掉所有死子之后數空定勝負。數空還是利用連通圖,只是這里連同圖指的是空格。如果空格的連通圖在遍歷中發現只和黑子相鄰則是黑子的空,只和白子相鄰則是白子的空,和黑白都有相鄰則是公氣,計算時得一方一半才可。數空是要依次遍歷所有的空格連通圖,直到整個棋盤上所有的空格都屬于某個遍歷出來的連通圖。
遍歷連通圖
上面基本所有的算法都可以歸結于連通圖的遍歷。圖的遍歷一般有深度遍歷和廣度遍歷,圍棋這里算連通圖采用廣度遍歷比較方便。
需要一個數據結構來記錄哪些坐標被遍歷過了,防止重復遍歷,每次遍歷了坐標之后就記錄下,這個數據結構以二維數組最合適。
建立一個空隊列,然后把開始遍歷的第一個坐標進隊。
從遍歷的第一個位置開始,每次把這個位置相鄰的坐標中所有與之相連并且沒有遍歷過的點(注意相連在不同的判斷里意義不同)進隊,并把當前坐標出隊,同時記錄該坐標已遍歷。
如此循環,直到隊空,則已遍歷了整個連通圖。
轉載于:https://www.cnblogs.com/Colin-Cai/p/7502410.html
總結
以上是生活随笔為你收集整理的围棋规则的计算机实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 昨日皇者——Symbian(塞班)
- 下一篇: win7的c盘清理方法