本文首發地址:http://blog.csdn.net/liigo/archive/2009/09/22/4582018.aspx
轉載請註明出處:http://blog.csdn.net/liigo
作者:liigo, 2009/09/22
在下圍棋時,當一個棋子落到棋盤上,它會對周圍對方棋子的死活產生影響,如果對方棋子沒有氣了(死了),必須從棋盤上拿掉(提子)。這個過程體現到圍棋軟體中,就需要通過程式碼判斷某一個棋子或某一片棋子的死活狀態,進而把死掉的棋子從棋盤上移除。
進一步分析的話,剛落下的棋子,只可能威脅到周圍與其緊臨的上下左右方向的四個對方棋子(也可能是三個或兩個,如在邊角的話),而這四個方向上的對方棋子,可能是孤立的一個棋子,也可能是多個棋子相連的一片棋子(一塊棋)。要判斷一塊棋是否還活著,需要逐個檢查這塊棋中的每一個棋子:如果其中某個棋子旁邊沒有棋子,說明這塊棋至少還有一口氣,因而確定它還沒有死;如果檢查完這塊棋中的所有棋子,始終不能找到一口氣,可以確定這整塊棋都死掉了。看上去這是一個需要採用遞迴處理的情況。遞迴處理整塊棋時,切記需要記錄已經處理過的棋子,不能重複處理同一個棋子,否則可能會導致迴圈遞迴、死遞迴的情況發生。一旦確定了棋子的生死,從棋盤上拿掉它是很容易的事情,只是在程式中做一些標記而已。
注意,我(liigo)這裡說的某塊棋“還活著”,並不等同於圍棋術語中的“已做活”,而只是表示這塊棋“暫時還沒有死”,至於將來會不會死,不在現在的處理範圍之內。別忘了,我們的目標是“如果棋子死了,把它從棋盤上拿掉”,既然還沒死(或沒死絕),又何必管它呢(如果非要提前提子,反而違反圍棋規則了)。
以下C/C++原始碼,實現了上面說到的自動提子功能。
//處理剛落下的子對周邊對方子的死活影響<br />void processLiving(int row, int col)<br />{<br />StoneColor color = m_board[row-1][col-1];<br />assert(color != SC_BLANK);<br />StoneColor otherColor = (color == SC_BLACK ? SC_WHITE : SC_BLACK);<br />if(m_killedStones[m_stoneIndex] == NULL)<br />m_killedStones[m_stoneIndex] = new BufferedMem(20);<br />BufferedMem* pKilledStones = m_killedStones[m_stoneIndex];<br />pKilledStones->Empty();<br />//如果周邊是對方的子, 則檢查其死活情況, 死了的拿掉<br />BufferedMem stoneIndexList;<br />if(row>1 && m_board[row-1-1][col-1]==otherColor && checkLiving(row-1,col,color,&stoneIndexList)==false)<br />processDeadStones(&stoneIndexList);<br />stoneIndexList.Empty();<br />if(row<19 && m_board[row+1-1][col-1]==otherColor && checkLiving(row+1,col,color,&stoneIndexList)==false)<br />processDeadStones(&stoneIndexList);<br />stoneIndexList.Empty();<br />if(col>1 && m_board[row-1][col-1-1]==otherColor && checkLiving(row,col-1,color,&stoneIndexList)==false)<br />processDeadStones(&stoneIndexList);<br />stoneIndexList.Empty();<br />if(col<19 && m_board[row-1][col+1-1]==otherColor && checkLiving(row,col+1,color,&stoneIndexList)==false)<br />processDeadStones(&stoneIndexList);<br />InvalidateRect(m_hWnd, NULL, true);<br />}
//檢查row/col所在的子的死活, color為另一方子的顏色.<br />//返回 1 表示活著(沒死), 返回 0 表示死了, 返回 -1 表示生死未定<br />int checkLiving(int row, int col, StoneColor color, BufferedMem* pStoneIndexList)<br />{<br />int index = RowColToIndex(row, col);<br />if(m_board[row-1][col-1] == SC_BLANK) //有氣, 所以活著<br />return 1;<br />if(m_board[row-1][col-1] == color) //這是對方的子<br />return -1;<br />int* pIndex = (int*) pStoneIndexList->GetData();<br />for(int i = 0, n = pStoneIndexList->GetDataSize()/sizeof(int); i < n; i++)<br />{<br />if(pIndex[i] == index)<br />return -1; //已經處理過該子了<br />}<br />pStoneIndexList->AppendMem(&index, sizeof(index));<br />//遞迴檢查周邊己方的子, 只要整塊有一口氣就說明活著<br />//這裡存在重複檢查的情況, 需要最佳化<br />if(row > 1 && m_board[row-1-1][col-1] != color && checkLiving(row-1, col, color, pStoneIndexList)==1)<br />return 1;<br />if(row < 19 && m_board[row+1-1][col-1] != color && checkLiving(row+1, col, color, pStoneIndexList)==1)<br />return 1;<br />if(col > 1 && m_board[row-1][col-1-1] != color && checkLiving(row, col-1, color, pStoneIndexList)==1)<br />return 1;<br />if(col < 19 && m_board[row-1][col+1-1] != color && checkLiving(row, col+1, color, pStoneIndexList)==1)<br />return 1;<br />return 0;<br />}
void processDeadStones(BufferedMem* deathStoneIndexList)<br />{<br />int* pIndex = (int*) deathStoneIndexList->GetData();<br />StoneColor* pBoard = &m_board[0][0];<br />for(int i = 0, n = deathStoneIndexList->GetDataSize()/sizeof(int); i < n; i++)<br />pBoard[pIndex[i]] = SC_BLANK;<br />//把死掉的子記錄下來, 供向前打譜時使用<br />if(deathStoneIndexList->GetDataSize() > 0)<br />{<br />BufferedMem* pKilledStones = m_killedStones[m_stoneIndex];<br />assert(pKilledStones);<br />pKilledStones->AppendMem(deathStoneIndexList->GetData(), deathStoneIndexList->GetDataSize());//此處可能會有重複,但無所謂<br />}<br />}
以上代碼來自我(liigo)最近開發的“M8圍棋譜”軟體,此項目已在 Google Code 上開源:http://code.google.com/p/m8weiqipu/。
如有錯漏疏忽之處,誠請批評指正。