這是「圍棋 AI 學習筆記」系列的第一篇,從一個很老、但很直觀的問題開始:看著一盤棋,電腦要怎麼判斷哪裡是誰的地?
在 AlphaGo 用神經網路直接「看」出形勢之前,早期的圍棋程式多半靠手寫規則來估算勢力範圍。Bruno Bouzy 在 2003 年發表的 5/21 演算法[1]就是其中一個經典做法:借用影像處理的「數學形態學」,把每顆棋子的影響力向外擴散,再把邊緣不穩的部分削掉,剩下來的就是比較確定的地盤。它的膨脹步驟改自 Zobrist 在 1969 年提出的勢力模型[2],Bouzy 再補上對應的侵蝕步驟;這個方法用在他自己的圍棋程式 Indigo 裡,後來也被 GNU Go 採用。
互動演示
下面的棋盤可以自己擺子,拉動步驟滑桿或按播放,看數值怎麼一步步變化。滑鼠移到格點上,會顯示那一步是怎麼算出來的。
演算法
整個過程只有三個階段,每一步都是同時更新所有格點(用上一步的值計算,不會邊算邊影響)。
1. 初始化
黑子設為 +64,白子設為 −64,空點設為 0,這是原論文[1]沿用 Zobrist 模型的設定。正值代表黑方的影響力,負值代表白方的。
2. 膨脹(Dilation)5 次
對每個格點:
- 如果它 ≥ 0,而且上下左右四鄰沒有負值,就加上四鄰中正值的個數。
- 負值那一方對稱處理:如果它 ≤ 0,而且四鄰沒有正值,就減去四鄰中負值的個數。
- 四鄰正負都有的點不動,代表雙方勢力在這裡碰頭。
膨脹讓影響力像水波一樣往外擴散,一次一格。5 次之後,單顆子的影響會涵蓋距離 5 以內的菱形範圍,越靠近棋子數值越大。
3. 侵蝕(Erosion)21 次
對每個格點:
- 如果它是正值,就減去四鄰中 ≤ 0 的個數,最多減到 0。
- 負值那一方對稱處理:加上四鄰中 ≥ 0 的個數,最多加到 0。
侵蝕從勢力範圍的邊緣往內削。邊緣的點跟 0 或對方相鄰,會一直被扣分;被自己人包圍的點則不受影響。
結果
做完 26 步之後,正值的空點算黑地,負值的空點算白地。
為什麼是 5 和 21?
21 不是隨便挑的。Bouzy 在論文[1]裡讓侵蝕次數 E 和膨脹次數 D 滿足:
E = D × (D − 1) + 1
D = 5 時剛好是 21(論文也提到 Indigo 依對局階段使用 4/13 或 5/21)。這個關係的效果是:一顆孤零零的子,侵蝕會剛好把膨脹出來的範圍全部削掉,不會憑空生出地來。在上面的演示選「單顆子」播放到最後就能看到:5 次膨脹後擴散出一大片,21 次侵蝕後所有空點都回到 0。
換句話說,單顆子只有「影響力」、沒有「地」;要有好幾顆子互相支援、把一塊區域圍起來,那塊區域裡的點才撐得過侵蝕。這很符合下棋的直覺。
可以觀察的幾件事
- 兩子對峙:兩顆子中間那一列在整個過程中都是 0。膨脹時雙方在這裡碰頭,誰也進不去,就成了分界線。
- 角落小盤面:試著把一方的子往邊角靠,或在對方勢力裡多放一顆,看最後的地盤怎麼變。棋盤邊線不算鄰居,所以邊角的點比中央更不容易被侵蝕。
- 打開「顯示數字」:膨脹階段數值增加得很快,侵蝕階段每一步只扣個位數。可以把滑鼠移到勢力邊緣的點,看它是被哪些鄰居扣分的。
侷限
5/21 只看棋子的位置,不懂死活:一顆被包圍、其實已經死掉的子,還是會被當成有影響力的子。它也不分辨誰下一手,以及哪裡還有劫或打入的餘地。所以它比較適合當「大致的形勢判斷」,而不是精確的終局數地。後面的筆記會再看更進一步的方法怎麼處理這些問題。
參考資料
- Bruno Bouzy (2003). "Mathematical morphology applied to computer Go." International Journal of Pattern Recognition and Artificial Intelligence, 17(2), 257–268. PDF
- Albert L. Zobrist (1969). "A model of visual organization for the game of Go." Proceedings of the AFIPS Spring Joint Computer Conference, 103–111.