程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 用C語言實現SGF格式圍棋棋譜解析器

用C語言實現SGF格式圍棋棋譜解析器

編輯:關於C語言

這是本人(liigo)獨立實現的SGF格式圍棋棋譜文件解析器,本文介紹其實現細節。網絡上肯定可以找到完善的開源的SGF解析器,這是毋庸置疑的,我不直接使用它們,也不參考它們的實現代碼,而是自己獨立編碼實現,是有原因的,因為我想自己重復發明輪子,並且認為這樣更有助於提高我的編碼能力。(關於我的“一定要學會重復發明輪子”的不成熟的論調,今後我將會專門撰文表述。)

  我(liigo)開發的這個SGF解析器,采用基於事件的簡單API,類似於XML解析器中的SAX(Simple API for XML)。這種解析器的核心是:由用戶事先提供一系列回調函數,解析器在解析的過程中,依次調用相關的回調函數並傳入相應參數,用戶程序在回調函數中做出相應的處理。此類解析器屬於輕量級的解析器,解析速度快,占用內存少,結構清晰易於實現,只是相對來說不如基於DOM的解析器方便使用。

  SGF格式,Smart Game Format,被設計用來記錄多種游戲類棋譜的通用格式,在圍棋領域被發揚光大,是用於描述圍棋棋譜的最重要也最通用的形式。它是純文本的、基於樹(TREE)的結構,便於識別、存儲和傳輸。其格式簡潔實用,也非常易於編程解析。SGF格式官方規范網址為:http://www.red-bean.com/sgf/。(說到圍棋棋譜,不得不贊歎一下,它只需用一幅圖就可以完整還原一盤棋從始至終的風雲變幻;作為對比,象棋一幅圖只能描述對弈中某一時刻的場景。)

  SGF的主要結構由樹(GameTree)、節點序列(Sequence)、節點(Node)、屬性(Property)等組成。其中“屬性”為最重要的基本單位,它由屬性標識(PropIdent)和屬性值(PropValue)組成。由分號“;”分隔的多個屬性,稱為節點。多個節點順序排列稱為節點序列。由括號“(”“)”括起來的節點序列,稱為樹,樹中可包含子樹。SGF的EBNF定義如下(參見html#ebnf-def">http://www.red-bean.com/sgf/sgf4.html#ebnf-def):

view plaincopy to clipboardprint?
Collection = GameTree { GameTree }  
GameTree   = "(" Sequence { GameTree } ")" 
Sequence   = Node { Node }  
Node       = ";" { Property }  
Property   = PropIdent PropValue { PropValue }  
PropIdent  = UcLetter { UcLetter }  
PropValue  = "[" CValueType "]" 
CValueType = (ValueType | Compose)  
ValueType  = (None | Number | Real | Double | Color | SimpleText | Text | Point  | Move | Stone) 
Collection = GameTree { GameTree }
GameTree   = "(" Sequence { GameTree } ")"
Sequence   = Node { Node }
Node       = ";" { Property }
Property   = PropIdent PropValue { PropValue }
PropIdent  = UcLetter { UcLetter }
PropValue  = "[" CValueType "]"
CValueType = (ValueType | Compose)
ValueType  = (None | Number | Real | Double | Color | SimpleText | Text | Point  | Move | Stone)

  以下是一個簡單的有一定代表性的SGF文本,先讓大家有一個感性認識:

+ expand sourceview plaincopy to clipboardprint?
(;FF[4]GM[1]SZ[19]FG[257:Figure 1]PM[1]  
PB[Takemiya Masaki]BR[9 dan]PW[Cho Chikun]  
WR[9 dan]RE[W+Resign]KM[5.5]TM[28800]DT[1996-10-18,19]  
EV[21st Meijin]RO[2 (final)]SO[Go World #78]US[Arno Hollosi]  
;B[pd];W[dp];B[pp];W[dd];B[pj];W[nc];B[oe];W[qc];B[pc];W[qd]  
(;B[qf];W[rf];B[rg];W[re];B[qg];W[pb];B[ob];W[qb]  
(;B[mp];W[fq];B[ci];W[cg];B[dl];W[cn];B[qo];W[ec];B[jp];W[jd]  
;B[ei];W[eg];B[kk]LB[qq:a][dj:b][ck:c][qp:d]N[Figure 1]  
 
;W[me]FG[257:Figure 2];B[kf];W[ke];B[lf];W[jf];B[jg]  
(;W[mf];B[if];W[je];B[ig];W[mg];B[mj];W[mq];B[lq];W[nq]  
(;B[lr];W[qq];B[pq];W[pr];B[rq];W[rr];B[rp];W[oq];B[mr];W[oo];B[mn]  
(;W[nr];B[qp]LB[kd:a][kh:b]N[Figure 2]  
 
;W[pk]FG[257:Figure 3];B[pm];W[oj];B[ok];W[qr];B[os];W[ol];B[nk];W[qj]  
;B[pi];W[pl];B[qm];W[ns];B[sr];W[om];B[op];W[qi];B[oi]  
(;W[rl];B[qh];W[rm];B[rn];W[ri];B[ql];W[qk];B[sm];W[sk];B[sh];W[og]  
;B[oh];W[np];B[no];W[mm];B[nn];W[lp];B[kp];W[lo];B[ln];W[ko];B[mo]  
;W[jo];B[km]N[Figure 3])  
 
(;W[ql]VW[ja:ss]FG[257:Dia. 6]MN[1];B[rm];W[ph];B[oh];W[pg];B[og];W[pf]  
;B[qh];W[qe];B[sh];W[of];B[sj]TR[oe][pd][pc][ob]LB[pe:a][sg:b][si:c]  
N[Diagram 6]))  
 
(;W[no]VW[jj:ss]FG[257:Dia. 5]MN[1];B[pn]N[Diagram 5]))  
 
(;B[pr]FG[257:Dia. 4]MN[1];W[kq];B[lp];W[lr];B[jq];W[jr];B[kp];W[kr];B[ir]  
;W[hr]LB[is:a][js:b][or:c]N[Diagram 4]))  
 
(;W[if]FG[257:Dia. 3]MN[1];B[mf];W[ig];B[jh]LB[ki:a]N[Diagram 3]))  
 
(;W[oc]VW[aa:sk]FG[257:Dia. 2]MN[1];B[md];W[mc];B[ld]N[Diagram 2]))  
 
(;B[qe]VW[aa:sj]FG[257:Dia. 1]MN[1];W[re];B[qf];W[rf];B[qg];W[pb];B[ob]  
;W[qb]LB[rg:a]N[Diagram 1])) 
(;FF[4]GM[1]SZ[19]FG[257:Figure 1]PM[1]
PB[Takemiya Masaki]BR[9 dan]PW[Cho Chikun]
WR[9 dan]RE[W+Resign]KM[5.5]TM[28800]DT[1996-10-18,19]
EV[21st Meijin]RO[2 (final)]SO[Go World #78]US[Arno Hollosi]
;B[pd];W[dp];B[pp];W[dd];B[pj];W[nc];B[oe];W[qc];B[pc];W[qd]
(;B[qf];W[rf];B[rg];W[re];B[qg];W[pb];B[ob];W[qb]
(;B[mp];W[fq];B[ci];W[cg];B[dl];W[cn];B[qo];W[ec];B[jp];W[jd]
;B[ei];W[eg];B[kk]LB[qq:a][dj:b][ck:c][qp:d]N[Figure 1]

;W[me]FG[257:Figure 2];B[kf];W[ke];B[lf];W[jf];B[jg]
(;W[mf];B[if];W[je];B[ig];W[mg];B[mj];W[mq];B[lq];W[nq]
(;B[lr];W[qq];B[pq];W[pr];B[rq];W[rr];B[rp];W[oq];B[mr];W[oo];B[mn]
(;W[nr];B[qp]LB[kd:a][kh:b]N[Figure 2]

;W[pk]FG[257:Figure 3];B[pm];W[oj];B[ok];W[qr];B[os];W[ol];B[nk];W[qj]
;B[pi];W[pl];B[qm];W[ns];B[sr];W[om];B[op];W[qi];B[oi]
(;W[rl];B[qh];W[rm];B[rn];W[ri];B[ql];W[qk];B[sm];W[sk];B[sh];W[og]
;B[oh];W[np];B[no];W[mm];B[nn];W[lp];B[kp];W[lo];B[ln];W[ko];B[mo]
;W[jo];B[km]N[Figure 3])

(;W[ql]VW[ja:ss]FG[257:Dia. 6]MN[1];B[rm];W[ph];B[oh];W[pg];B[og];W[pf]
;B[qh];W[qe];B[sh];W[of];B[sj]TR[oe][pd][pc][ob]LB[pe:a][sg:b][si:c]
N[Diagram 6]))

(;W[no]VW[jj:ss]FG[257:Dia. 5]MN[1];B[pn]N[Diagram 5]))

(;B[pr]FG[257:Dia. 4]MN[1];W[kq];B[lp];W[lr];B[jq];W[jr];B[kp];W[kr];B[ir]
;W[hr]LB[is:a][js:b][or:c]N[Diagram 4]))

(;W[if]FG[257:Dia. 3]MN[1];B[mf];W[ig];B[jh]LB[ki:a]N[Diagram 3]))

(;W[oc]VW[aa:sk]FG[257:Dia. 2]MN[1];B[md];W[mc];B[ld]N[Diagram 2]))

(;B[qe]VW[aa:sj]FG[257:Dia. 1]MN[1];W[re];B[qf];W[rf];B[qg];W[pb];B[ob]
;W[qb]LB[rg:a]N[Diagram 1]))

  熟悉編寫文本解析器的程序員朋友應該都清楚,根據EBNF定義,編寫對應的解析器,是相當簡單和直觀的,貌似只是一項翻譯性的工作。本人實現SGF解析器,再次印證了這個觀點,大部分情況下,我只是按部就班地將EBNF翻譯為C語言代碼而已,呵呵。

  我首先設計了“SGFParseContext”結構,用於保存解析器工作期間的相關數據:

view plaincopy to clipboardprint?
typedef struct _tagSGFParseContext  
{  
        void* pUserData;  
        int treeIndex;  
 
        PFN_ON_TREE pfnOnTree;  
        PFN_ON_TREE_END pfnOnTreeEnd;  
        PFN_ON_NODE pfnOnNode;  
        PFN_ON_NODE_END pfnOnNodeEnd;  
        PFN_ON_PROPERTY pfnOnProperty;  
 
        char idBuffer[16];  
        char* valueBuffer;  
        int valueBufferSize;  
}  
SGFParseContext; 
typedef struct _tagSGFParseContext
{
        void* pUserData;
        int treeIndex;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved