kantan-sgf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Brian Jones
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,54 @@
1
+ ~~ ABOUT ~~~~~*
2
+
3
+ KANTAN means "simple" or "easy" in
4
+ that wacky Japanese language.
5
+
6
+ There doesn't seem to be any such
7
+ thing as a dedicated Ruby SGF project.
8
+ I know because I've been looking for
9
+ a few years.
10
+
11
+ If I missed one, sorry.
12
+
13
+ Anyways, I got sick of searching around,
14
+ and an SGF parser applies to the
15
+ project I am working on, so I broke down
16
+ and wrote this.
17
+
18
+ It doesn't do node parsing (yet).
19
+ I just upgraded it using Treetop grammar
20
+ to parse the file, so a good majority of
21
+ the data is pulled out now. Some of it
22
+ will get refined with more internal handling,
23
+ but for now enjoy!
24
+
25
+ ~~ Requirements ~~~~~*
26
+
27
+ Requires the Treetop gem to run.
28
+
29
+ $ gem install treetop
30
+
31
+ ~~ USAGE ~~~~~*
32
+
33
+ You pretty much run it like so:
34
+
35
+ # Load and parse
36
+ sgf = KantanSgf::Sgf.new('data/stoic-bojo.sgf')
37
+ sgf.parse
38
+
39
+ # Pull back properties
40
+ puts sgf.player_black
41
+ puts sgf.player_white
42
+ puts sgf.komi
43
+ puts sgf.result
44
+
45
+ # do some magic with the move hash
46
+ for move in sgf.move_list
47
+ puts "%s: (%i, %i)" % [move[:color], move[:x], move[:y]]
48
+ end
49
+
50
+ Note that the move data is stored as:
51
+ * color: 'B' or 'W'
52
+ * x, y: Integer value from 0..board_size - 1
53
+ * time: Clock time left
54
+ * ot_stones: Overtime stones
data/Rakefile ADDED
File without changes
data/data/game-01.sgf ADDED
@@ -0,0 +1,31 @@
1
+ (
2
+ ;
3
+ FF[4]
4
+ EV[Female Guksu,13,Korea,]
5
+ RO[1]
6
+ PB[Lee MinJin]
7
+ BR[5p]
8
+ PW[Park JiEun]
9
+ WR[9p]
10
+ KM[6.5]
11
+ DT[2008-03-11]
12
+ PC[Korea]
13
+ RE[B+R]
14
+ SO[http://gobase.org/games/korea/female/guksu/13/game-01.sgf]
15
+ AP[sgf2misc:3.1.9]
16
+ ;B[qd];W[dd];B[pp];W[dp];B[fc];W[cf];B[db];W[od];B[oc];W[pd]
17
+ ;B[pc];W[qe];B[qc];W[nd];B[mc];W[qn];B[nq];W[pf];B[pk];W[qp]
18
+ ;B[qq];W[rq];B[qo];W[rp];B[po];W[ro];B[pn];W[qm];B[qr];W[pm]
19
+ ;B[om];W[ol];B[pl];W[nm];B[on];W[rk];B[pi];W[iq];B[cn];W[fp]
20
+ ;B[bp];W[cm];B[dm];W[cl];B[cq];W[bn];B[bo];W[dn];B[co];W[en]
21
+ ;B[er];W[ge];B[mj];W[hj];B[hl];W[gl];B[hh];W[hk];B[fg];W[fd]
22
+ ;B[gc];W[hf];B[hg];W[jg];B[dg];W[cg];B[di];W[ch];B[jh];W[kh]
23
+ ;B[ji];W[ki];B[kj];W[jj];B[kk];W[nc];B[nb];W[ih];B[ii];W[hi]
24
+ ;B[ig];W[ij];B[jf];W[ih];B[kg];W[lg];B[jh];W[ji];B[md];W[me]
25
+ ;B[kd];W[cc];B[hd];W[km];B[nl];W[ml];B[nk];W[dh];B[eh];W[ei]
26
+ ;B[fi];W[ej];B[kq];W[kp];B[lp];W[jp];B[gq];W[lo];B[lq];W[fq]
27
+ ;B[fr];W[gp];B[mf];W[mb];B[lb];W[le];B[nf];W[rd];B[rc];W[ne]
28
+ ;B[lf];W[ke];B[je];W[rf];B[sd];W[rj];B[kf];W[qh];B[jl];W[cb]
29
+ ;B[jm];W[hm];B[hn];W[io];B[gm];W[il];B[im];W[hl];B[fj];W[fk]
30
+ ;B[in];W[gn];B[go];W[fn];B[hp];W[ho];B[jo];W[ip];B[ko]
31
+ )
@@ -0,0 +1,250 @@
1
+ (;GM[1]FF[4]CA[UTF-8]AP[CGoban:3]ST[2]
2
+ RU[Japanese]SZ[19]KM[0.50]TM[1800]OT[5x30 byo-yomi]
3
+ PW[stoic]PB[bojo]WR[3k]BR[4k]DT[2008-11-30]PC[The KGS Go Server at http://www.gokgs.com/]C[bojo [4k\]: hi
4
+ stoic [3k\]: hi!
5
+ ]RE[B+2.50]
6
+ ;B[pc]BL[1792.458]
7
+ ;W[pp]WL[1794.464]
8
+ ;B[cd]BL[1786.722]
9
+ ;W[qe]WL[1787.185]
10
+ ;B[dq]BL[1775.793]
11
+ ;W[od]WL[1768.345]
12
+ ;B[oc]BL[1747.164]
13
+ ;W[nd]WL[1758.788]
14
+ ;B[nc]BL[1743.67]
15
+ ;W[qj]WL[1738.284]
16
+ ;B[pd]BL[1726.526]
17
+ ;W[of]WL[1681.06]
18
+ ;B[jp]BL[1639.907]
19
+ ;W[do]WL[1663.982]
20
+ ;B[fp]BL[1631.746]
21
+ ;W[cq]WL[1654.511]
22
+ ;B[dp]BL[1626.011]
23
+ ;W[cp]WL[1646.782]
24
+ ;B[eo]BL[1623.1]
25
+ ;W[dn]WL[1643.852]
26
+ ;B[cr]BL[1620.583]
27
+ ;W[br]WL[1641.17]
28
+ ;B[dr]BL[1616.752]
29
+ ;W[cj]WL[1630.233]
30
+ ;B[ed]BL[1584.282]
31
+ ;W[md]WL[1611.942]
32
+ ;B[mc]BL[1580.724]
33
+ ;W[hc]WL[1603.668]
34
+ ;B[kc]BL[1560.629]
35
+ ;W[he]WL[1590.915]
36
+ ;B[ke]BL[1543.078]
37
+ ;W[en]WL[1587.257]
38
+ ;B[fn]BL[1538.112]
39
+ ;W[fm]WL[1576.929]
40
+ ;B[gm]BL[1528.599]
41
+ ;W[gl]WL[1558.097]
42
+ ;B[fl]BL[1523.783]
43
+ ;W[em]WL[1547.426]
44
+ ;B[gn]BL[1508.898]
45
+ ;W[gk]WL[1541.47]
46
+ ;B[ej]BL[1504.11]
47
+ ;W[dh]WL[1533.93]
48
+ ;B[cg]BL[1489.237]
49
+ ;W[fh]WL[1513.371]
50
+ ;B[ci]BL[1476.168]
51
+ ;W[di]WL[1511.16]
52
+ ;B[ch]BL[1449.339]
53
+ ;W[dg]WL[1439.636]
54
+ ;B[cf]BL[1441.658]
55
+ ;W[pq]WL[1399.774]
56
+ ;B[ql]BL[1402.308]
57
+ ;W[qn]WL[1371.667]
58
+ ;B[qg]BL[1395.566]
59
+ ;W[pe]WL[1290.265]
60
+ ;B[pj]BL[1372.501]
61
+ ;W[pi]WL[1198.698]
62
+ ;B[oj]BL[1318.808]
63
+ ;W[qi]WL[1169.751]
64
+ ;B[ol]BL[1314.671]
65
+ ;W[qk]WL[1160.616]
66
+ ;B[pl]BL[1299.257]
67
+ ;W[lp]WL[1132.469]
68
+ ;B[lq]BL[1281.075]
69
+ ;W[mq]WL[1106.595]
70
+ ;B[kq]BL[1277.244]
71
+ ;W[ln]WL[1088.387]
72
+ ;B[ll]BL[1245.779]
73
+ ;W[km]WL[1069.595]
74
+ ;B[nh]BL[1215.502]
75
+ ;W[kg]WL[990.928]
76
+ ;B[lh]BL[1196.657]
77
+ ;W[kh]WL[906.956]
78
+ ;B[lg]BL[1191.135]
79
+ ;W[kf]WL[886.502]
80
+ ;B[lf]BL[1188.459]
81
+ ;W[je]WL[880.939]
82
+ ;B[ld]BL[1182.558]
83
+ ;W[oi]WL[832.87]
84
+ ;B[ni]BL[1177.604]
85
+ ;W[lj]WL[785.912]
86
+ ;B[mk]BL[1161.47]
87
+ ;W[kl]WL[750.526]
88
+ ;B[oh]BL[1156.4]
89
+ ;W[ph]WL[741.468]
90
+ ;B[pg]BL[1153.321]
91
+ ;W[og]WL[738.531]
92
+ ;B[rg]BL[1150.763]
93
+ ;W[rh]WL[701.128]
94
+ ;B[rk]BL[1145.239]
95
+ ;W[rj]WL[689.609]
96
+ ;B[rl]BL[1141.614]
97
+ ;W[rf]WL[685.214]
98
+ ;B[sh]BL[1063.241]
99
+ ;W[si]WL[659.907]
100
+ ;B[sg]BL[1059.723]
101
+ ;W[qh]WL[650.385]
102
+ ;B[sk]BL[1054.989]
103
+ ;W[sf]WL[632.39]
104
+ ;B[sj]BL[1049.175]
105
+ ;W[qf]WL[615.379]
106
+ ;B[ri]BL[1044.751]
107
+ ;W[go]WL[576.126]
108
+ ;B[gp]BL[1036.254]
109
+ ;W[si]WL[573.861]
110
+ ;B[co]BL[1024.166]
111
+ ;W[pf]WL[554.652]
112
+ ;B[bo]BL[1020.167]
113
+ ;W[bm]WL[515.418]
114
+ ;B[ck]BL[925.688]
115
+ ;W[bj]WL[429.279]
116
+ ;B[dj]BL[919.363]
117
+ ;W[bk]WL[381.195]
118
+ ;B[cn]BL[907.87]
119
+ ;W[cm]WL[366.769]
120
+ ;B[el]BL[904.885]
121
+ ;W[dm]WL[365.266]
122
+ ;B[bq]BL[901.476]
123
+ ;W[dk]WL[340.13]
124
+ ;B[im]BL[892.108]
125
+ ;W[jn]WL[324.538]
126
+ ;B[io]BL[888.293]
127
+ ;W[hm]WL[300.362]
128
+ ;B[hn]BL[881.802]
129
+ ;W[hl]WL[291.957]
130
+ ;B[in]BL[878.121]
131
+ ;W[il]WL[277.59]
132
+ ;B[ff]BL[873.851]
133
+ ;W[gg]WL[271.55]
134
+ ;B[gd]BL[870.995]
135
+ ;W[fb]WL[264.46]
136
+ ;B[fc]BL[864.486]
137
+ ;W[eb]WL[256.088]
138
+ ;B[gb]BL[857.77]
139
+ ;W[gc]WL[229.165]
140
+ ;B[hb]BL[847.356]
141
+ ;W[ib]WL[198.916]
142
+ ;B[ic]BL[843.043]
143
+ ;W[hd]WL[191.117]
144
+ ;B[jb]BL[841.065]
145
+ ;W[ec]WL[175.606]
146
+ ;B[fd]BL[837.8]
147
+ ;W[bc]WL[174.381]
148
+ ;B[cc]BL[790.076]
149
+ ;W[cb]WL[160.963]
150
+ ;B[bb]BL[766.773]
151
+ ;W[ab]WL[129.073]
152
+ ;B[ba]BL[736.205]
153
+ ;W[ca]WL[104.992]
154
+ ;B[db]BL[708.763]
155
+ ;W[aa]WL[99.323]
156
+ ;B[dc]BL[705.328]
157
+ ;W[bd]WL[97.071]
158
+ ;B[be]BL[691.701]
159
+ ;W[ad]WL[95.193]
160
+ ;B[da]BL[686.344]
161
+ ;W[nm]WL[76.997]
162
+ ;B[pn]BL[681.103]
163
+ ;W[po]WL[62.894]
164
+ ;B[on]BL[678.213]
165
+ ;W[nl]WL[39.376]
166
+ ;B[nk]BL[674.879]
167
+ ;W[no]WL[28.175]
168
+ ;B[lk]BL[642.766]
169
+ ;W[kk]WL[22.144]
170
+ ;B[li]BL[639.436]
171
+ ;W[ki]WL[17.945]
172
+ ;B[gf]BL[636.261]
173
+ ;W[hf]WL[15.471]
174
+ ;B[rd]BL[631.134]
175
+ ;W[qd]WL[12.752]
176
+ ;B[qc]BL[628.157]
177
+ ;W[jd]WL[7.529]
178
+ ;B[jc]BL[613.093]
179
+ ;W[kd]WL[5.897]
180
+ ;B[le]BL[610.468]
181
+ ;W[mr]WL[4.232]
182
+ ;B[kr]BL[602.191]
183
+ ;W[df]WL[30]OW[5]
184
+ ;B[de]BL[598.237]
185
+ ;W[ag]WL[30]OW[5]
186
+ ;B[bg]BL[588.912]
187
+ ;W[ah]WL[30]OW[5]
188
+ ;B[af]BL[584.436]
189
+ ;W[bi]WL[30]OW[5]
190
+ ;B[so]BL[574.598]
191
+ ;W[ro]WL[30]OW[5]
192
+ ;B[sm]BL[562.012]
193
+ ;W[sp]WL[30]OW[5]
194
+ ;B[sn]BL[559.893]
195
+ ;W[rp]WL[30]OW[5]
196
+ ;B[nn]BL[545.779]
197
+ ;W[mm]WL[30]OW[5]
198
+ ;B[mn]BL[542.459]
199
+ ;W[lm]WL[30]OW[5]
200
+ ;B[mo]BL[539.489]
201
+ ;W[np]WL[30]OW[5]
202
+ ;B[mp]BL[534.382]
203
+ ;W[lo]WL[30]OW[5]
204
+ ;B[oo]BL[528.951]
205
+ ;W[op]WL[30]OW[5]
206
+ ;B[mj]BL[524.453]
207
+ ;W[pk]WL[30]OW[5]
208
+ ;B[ok]BL[521.145]
209
+ ;W[kj]WL[30]OW[5]
210
+ ;B[ef]BL[515.481]
211
+ ;W[fg]WL[30]OW[5]
212
+ ;B[jm]BL[508.218]
213
+ ;W[jl]WL[30]OW[5]
214
+ ;B[jo]BL[505.946]
215
+ ;W[kn]WL[30]OW[5]
216
+ ;B[eg]BL[478.246]
217
+ ;W[eh]WL[30]OW[5]
218
+ ;B[ae]BL[466.784]
219
+ ;W[re]WL[30]OW[5]
220
+ ;B[rc]BL[463.459]
221
+ ;W[rm]WL[30]OW[5]
222
+ ;B[ng]BL[436.579]
223
+ ;W[nf]WL[30]OW[5]
224
+ ;B[me]BL[433.462]
225
+ ;W[ne]WL[30]OW[5]
226
+ ;B[mf]BL[430.536]
227
+ ;W[lr]WL[30]OW[5]
228
+ ;B[ls]BL[425.674]
229
+ ;W[ms]WL[30]OW[5]
230
+ ;B[ks]BL[423.798]
231
+ ;W[pm]WL[30]OW[5]
232
+ ;B[om]BL[419.794]
233
+ ;W[qm]WL[30]OW[5]
234
+ ;B[id]BL[406.893]
235
+ ;W[ie]WL[30]OW[5]
236
+ ;B[an]BL[401.121]
237
+ ;W[am]WL[30]OW[5]
238
+ ;B[bn]BL[396.109]
239
+ ;W[ge]WL[30]OW[5]
240
+ ;B[fe]BL[393.014]
241
+ ;W[rn]WL[30]OW[5]
242
+ ;B[sl]BL[390.425]
243
+ ;W[bh]WL[30]OW[5]
244
+ ;B[sd]BL[383.494]
245
+ ;W[kp]WL[30]OW[5]
246
+ ;B[iq]BL[365.597]
247
+ ;W[]WL[30]OW[5]
248
+ ;B[]BL[365.597]TW[ba][bb][ac][oe][if][jf][hg][ig][jg][pg][qg][rg][sg][gh][hh][ih][jh][sh][ai][ei][fi][gi][hi][ii][ji][ri][aj][dj][ej][fj][gj][hj][ij][jj][ak][ck][ek][fk][hk][ik][jk][al][bl][cl][dl][el][fl][qo][qp][nq][oq][qq][rq][sq][nr][or][pr][qr][rr][sr][ns][os][ps][qs][rs][ss]TB[ea][fa][ga][ha][ia][ja][ka][la][ma][na][oa][pa][qa][ra][sa][eb][fb][ib][kb][lb][mb][nb][ob][pb][qb][rb][sb][ec][lc][sc][dd][ce][ee][bf][mg][mh][mi][nj][ao][fo][go][ho][ap][bp][cp][ep][hp][ip][aq][cq][eq][fq][gq][hq][jq][ar][br][er][fr][gr][hr][ir][jr][as][bs][cs][ds][es][fs][gs][hs][is][js]C[stoic [3k\]: thanks
249
+ bojo [3k\]: thank you
250
+ ])
@@ -0,0 +1,17 @@
1
+ require '../lib/kantan-sgf'
2
+
3
+ # Load and parse
4
+ #sgf = KantanSgf::Sgf.new('../data/game-01.sgf')
5
+ sgf = KantanSgf::Sgf.new('../data/stoic-bojo.sgf')
6
+ sgf.parse
7
+
8
+ # Pull back properties
9
+ puts sgf.player_black
10
+ puts sgf.player_white
11
+ puts sgf.komi
12
+ puts sgf.result
13
+
14
+ # do some magic with the move hash
15
+ #for move in sgf.move_list
16
+ # puts "%s: (%i, %i)" % [move[:color], move[:x], move[:y]]
17
+ #end
@@ -0,0 +1,427 @@
1
+ module SgfGrammar
2
+ include Treetop::Runtime
3
+
4
+ def root
5
+ @root || :node
6
+ end
7
+
8
+ module Node0
9
+ def sp
10
+ elements[1]
11
+ end
12
+
13
+ def sp
14
+ elements[3]
15
+ end
16
+
17
+ def sp
18
+ elements[5]
19
+ end
20
+ end
21
+
22
+ module Node1
23
+ def value
24
+ get([elements[2]])
25
+ end
26
+
27
+ def get(e)
28
+ a = []
29
+ if !e.nil?
30
+ e.each do |el|
31
+ if el.respond_to?(:value)
32
+ a << el.value
33
+ else
34
+ a += get(el.elements) if !el.nil?
35
+ end
36
+ end
37
+ end
38
+ a
39
+ end
40
+ end
41
+
42
+ def _nt_node
43
+ start_index = index
44
+ if node_cache[:node].has_key?(index)
45
+ cached = node_cache[:node][index]
46
+ @index = cached.interval.end if cached
47
+ return cached
48
+ end
49
+
50
+ i0, s0 = index, []
51
+ if input.index('(', index) == index
52
+ r1 = (SyntaxNode).new(input, index...(index + 1))
53
+ @index += 1
54
+ else
55
+ terminal_parse_failure('(')
56
+ r1 = nil
57
+ end
58
+ s0 << r1
59
+ if r1
60
+ r2 = _nt_sp
61
+ s0 << r2
62
+ if r2
63
+ s3, i3 = [], index
64
+ loop do
65
+ r4 = _nt_chunk
66
+ if r4
67
+ s3 << r4
68
+ else
69
+ break
70
+ end
71
+ end
72
+ r3 = SyntaxNode.new(input, i3...index, s3)
73
+ s0 << r3
74
+ if r3
75
+ r5 = _nt_sp
76
+ s0 << r5
77
+ if r5
78
+ if input.index(')', index) == index
79
+ r6 = (SyntaxNode).new(input, index...(index + 1))
80
+ @index += 1
81
+ else
82
+ terminal_parse_failure(')')
83
+ r6 = nil
84
+ end
85
+ s0 << r6
86
+ if r6
87
+ r7 = _nt_sp
88
+ s0 << r7
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ if s0.last
95
+ r0 = (SyntaxNode).new(input, i0...index, s0)
96
+ r0.extend(Node0)
97
+ r0.extend(Node1)
98
+ else
99
+ self.index = i0
100
+ r0 = nil
101
+ end
102
+
103
+ node_cache[:node][start_index] = r0
104
+
105
+ return r0
106
+ end
107
+
108
+ module Chunk0
109
+ def sp
110
+ elements[1]
111
+ end
112
+
113
+ def sp
114
+ elements[3]
115
+ end
116
+ end
117
+
118
+ module Chunk1
119
+ def value
120
+ get([elements[2]])
121
+ end
122
+
123
+ def get(e)
124
+ a = []
125
+ if !e.nil?
126
+ e.each do |el|
127
+ if el.respond_to?(:value)
128
+ a << el.value
129
+ else
130
+ a += get(el.elements) if !el.nil?
131
+ end
132
+ end
133
+ end
134
+ a
135
+ end
136
+ end
137
+
138
+ def _nt_chunk
139
+ start_index = index
140
+ if node_cache[:chunk].has_key?(index)
141
+ cached = node_cache[:chunk][index]
142
+ @index = cached.interval.end if cached
143
+ return cached
144
+ end
145
+
146
+ i0, s0 = index, []
147
+ if input.index(';', index) == index
148
+ r1 = (SyntaxNode).new(input, index...(index + 1))
149
+ @index += 1
150
+ else
151
+ terminal_parse_failure(';')
152
+ r1 = nil
153
+ end
154
+ s0 << r1
155
+ if r1
156
+ r2 = _nt_sp
157
+ s0 << r2
158
+ if r2
159
+ s3, i3 = [], index
160
+ loop do
161
+ r4 = _nt_property_set
162
+ if r4
163
+ s3 << r4
164
+ else
165
+ break
166
+ end
167
+ end
168
+ r3 = SyntaxNode.new(input, i3...index, s3)
169
+ s0 << r3
170
+ if r3
171
+ r5 = _nt_sp
172
+ s0 << r5
173
+ end
174
+ end
175
+ end
176
+ if s0.last
177
+ r0 = (SyntaxNode).new(input, i0...index, s0)
178
+ r0.extend(Chunk0)
179
+ r0.extend(Chunk1)
180
+ else
181
+ self.index = i0
182
+ r0 = nil
183
+ end
184
+
185
+ node_cache[:chunk][start_index] = r0
186
+
187
+ return r0
188
+ end
189
+
190
+ module PropertySet0
191
+ def property
192
+ elements[0]
193
+ end
194
+
195
+ def sp
196
+ elements[2]
197
+ end
198
+ end
199
+
200
+ module PropertySet1
201
+ def value
202
+ {
203
+ :property => elements[0].text_value,
204
+ :data => elements[1].text_value[1..-2]
205
+ }
206
+ end
207
+ end
208
+
209
+ def _nt_property_set
210
+ start_index = index
211
+ if node_cache[:property_set].has_key?(index)
212
+ cached = node_cache[:property_set][index]
213
+ @index = cached.interval.end if cached
214
+ return cached
215
+ end
216
+
217
+ i0, s0 = index, []
218
+ r1 = _nt_property
219
+ s0 << r1
220
+ if r1
221
+ s2, i2 = [], index
222
+ loop do
223
+ r3 = _nt_property_bracket
224
+ if r3
225
+ s2 << r3
226
+ else
227
+ break
228
+ end
229
+ end
230
+ r2 = SyntaxNode.new(input, i2...index, s2)
231
+ s0 << r2
232
+ if r2
233
+ r4 = _nt_sp
234
+ s0 << r4
235
+ end
236
+ end
237
+ if s0.last
238
+ r0 = (SyntaxNode).new(input, i0...index, s0)
239
+ r0.extend(PropertySet0)
240
+ r0.extend(PropertySet1)
241
+ else
242
+ self.index = i0
243
+ r0 = nil
244
+ end
245
+
246
+ node_cache[:property_set][start_index] = r0
247
+
248
+ return r0
249
+ end
250
+
251
+ module PropertyBracket0
252
+ end
253
+
254
+ def _nt_property_bracket
255
+ start_index = index
256
+ if node_cache[:property_bracket].has_key?(index)
257
+ cached = node_cache[:property_bracket][index]
258
+ @index = cached.interval.end if cached
259
+ return cached
260
+ end
261
+
262
+ i0, s0 = index, []
263
+ if input.index('[', index) == index
264
+ r1 = (SyntaxNode).new(input, index...(index + 1))
265
+ @index += 1
266
+ else
267
+ terminal_parse_failure('[')
268
+ r1 = nil
269
+ end
270
+ s0 << r1
271
+ if r1
272
+ s2, i2 = [], index
273
+ loop do
274
+ i3 = index
275
+ if input.index(Regexp.new('[^\\[\\]]'), index) == index
276
+ r4 = (SyntaxNode).new(input, index...(index + 1))
277
+ @index += 1
278
+ else
279
+ r4 = nil
280
+ end
281
+ if r4
282
+ r3 = r4
283
+ else
284
+ r5 = _nt_property_bracket
285
+ if r5
286
+ r3 = r5
287
+ else
288
+ self.index = i3
289
+ r3 = nil
290
+ end
291
+ end
292
+ if r3
293
+ s2 << r3
294
+ else
295
+ break
296
+ end
297
+ end
298
+ r2 = SyntaxNode.new(input, i2...index, s2)
299
+ s0 << r2
300
+ if r2
301
+ if input.index(']', index) == index
302
+ r6 = (SyntaxNode).new(input, index...(index + 1))
303
+ @index += 1
304
+ else
305
+ terminal_parse_failure(']')
306
+ r6 = nil
307
+ end
308
+ s0 << r6
309
+ end
310
+ end
311
+ if s0.last
312
+ r0 = (SyntaxNode).new(input, i0...index, s0)
313
+ r0.extend(PropertyBracket0)
314
+ else
315
+ self.index = i0
316
+ r0 = nil
317
+ end
318
+
319
+ node_cache[:property_bracket][start_index] = r0
320
+
321
+ return r0
322
+ end
323
+
324
+ module Property0
325
+ def sp
326
+ elements[1]
327
+ end
328
+ end
329
+
330
+ def _nt_property
331
+ start_index = index
332
+ if node_cache[:property].has_key?(index)
333
+ cached = node_cache[:property][index]
334
+ @index = cached.interval.end if cached
335
+ return cached
336
+ end
337
+
338
+ i0 = index
339
+ s1, i1 = [], index
340
+ loop do
341
+ if input.index(Regexp.new('[A-Z]'), index) == index
342
+ r2 = (SyntaxNode).new(input, index...(index + 1))
343
+ @index += 1
344
+ else
345
+ r2 = nil
346
+ end
347
+ if r2
348
+ s1 << r2
349
+ else
350
+ break
351
+ end
352
+ end
353
+ if s1.empty?
354
+ self.index = i1
355
+ r1 = nil
356
+ else
357
+ r1 = SyntaxNode.new(input, i1...index, s1)
358
+ end
359
+ if r1
360
+ r0 = r1
361
+ else
362
+ i3, s3 = index, []
363
+ if input.index(Regexp.new('[A-Z]'), index) == index
364
+ r4 = (SyntaxNode).new(input, index...(index + 1))
365
+ @index += 1
366
+ else
367
+ r4 = nil
368
+ end
369
+ s3 << r4
370
+ if r4
371
+ r5 = _nt_sp
372
+ s3 << r5
373
+ end
374
+ if s3.last
375
+ r3 = (SyntaxNode).new(input, i3...index, s3)
376
+ r3.extend(Property0)
377
+ else
378
+ self.index = i3
379
+ r3 = nil
380
+ end
381
+ if r3
382
+ r0 = r3
383
+ else
384
+ self.index = i0
385
+ r0 = nil
386
+ end
387
+ end
388
+
389
+ node_cache[:property][start_index] = r0
390
+
391
+ return r0
392
+ end
393
+
394
+ def _nt_sp
395
+ start_index = index
396
+ if node_cache[:sp].has_key?(index)
397
+ cached = node_cache[:sp][index]
398
+ @index = cached.interval.end if cached
399
+ return cached
400
+ end
401
+
402
+ s0, i0 = [], index
403
+ loop do
404
+ if input.index(Regexp.new('[\\r\\n\\t ]'), index) == index
405
+ r1 = (SyntaxNode).new(input, index...(index + 1))
406
+ @index += 1
407
+ else
408
+ r1 = nil
409
+ end
410
+ if r1
411
+ s0 << r1
412
+ else
413
+ break
414
+ end
415
+ end
416
+ r0 = SyntaxNode.new(input, i0...index, s0)
417
+
418
+ node_cache[:sp][start_index] = r0
419
+
420
+ return r0
421
+ end
422
+
423
+ end
424
+
425
+ class SgfGrammarParser < Treetop::Runtime::CompiledParser
426
+ include SgfGrammar
427
+ end
@@ -0,0 +1,68 @@
1
+ grammar SgfGrammar
2
+ rule node
3
+ '(' sp chunk* sp ')' sp {
4
+ def value
5
+ get([elements[2]])
6
+ end
7
+
8
+ def get(e)
9
+ a = []
10
+ if !e.nil?
11
+ e.each do |el|
12
+ if el.respond_to?(:value)
13
+ a << el.value
14
+ else
15
+ a += get(el.elements) if !el.nil?
16
+ end
17
+ end
18
+ end
19
+ a
20
+ end
21
+ }
22
+ end
23
+
24
+ rule chunk
25
+ ';' sp property_set* sp {
26
+ def value
27
+ get([elements[2]])
28
+ end
29
+
30
+ def get(e)
31
+ a = []
32
+ if !e.nil?
33
+ e.each do |el|
34
+ if el.respond_to?(:value)
35
+ a << el.value
36
+ else
37
+ a += get(el.elements) if !el.nil?
38
+ end
39
+ end
40
+ end
41
+ a
42
+ end
43
+ }
44
+ end
45
+
46
+ rule property_set
47
+ property property_bracket* sp {
48
+ def value
49
+ {
50
+ :property => elements[0].text_value,
51
+ :data => elements[1].text_value[1..-2]
52
+ }
53
+ end
54
+ }
55
+ end
56
+
57
+ rule property_bracket
58
+ '[' ( [^\[\]] / property_bracket )* ']'
59
+ end
60
+
61
+ rule property
62
+ [A-Z]+ / [A-Z] sp
63
+ end
64
+
65
+ rule sp
66
+ [\r\n\t ]*
67
+ end
68
+ end
@@ -0,0 +1,132 @@
1
+ dir = File.dirname(__FILE__)
2
+ require 'rubygems'
3
+ require 'treetop'
4
+
5
+ # Grammar
6
+ require "#{dir}/../grammar/sgf-grammar"
7
+
8
+ module KantanSgf
9
+ class Sgf
10
+
11
+ attr_accessor :move_list, :properties
12
+
13
+ def initialize(file)
14
+ @file = file
15
+ @data = nil
16
+ @properties = {}
17
+
18
+ @symbol_table = {}
19
+ a = 'a'
20
+ for i in 0..18
21
+ @symbol_table.store(a, i)
22
+ a = a.succ
23
+ end
24
+
25
+ @move_list = []
26
+ end
27
+
28
+ def parse
29
+ f = File.open(@file)
30
+ @data = f.read
31
+ f.close
32
+
33
+ @sgf = SgfGrammarParser.new
34
+ results = @sgf.parse(@data)
35
+ raise "Parsing failed due to [#{@sgf.failure_reason}]." if results.nil?
36
+
37
+ # Pull the data out of the Treetop grammar parser
38
+ data = results.value
39
+
40
+ # Header info
41
+ header = data.shift
42
+ header.each do |chunk|
43
+ @properties.store(chunk[:property], chunk[:data])
44
+ end
45
+ # Footer info
46
+ footer = data.pop
47
+ footer.each do |chunk|
48
+ @properties.store(chunk[:property], chunk[:data])
49
+ end
50
+ # Moves
51
+ data.each do |chunk|
52
+ move = {}
53
+ chunk.each do |info|
54
+ case info[:property]
55
+ when 'B', 'W'
56
+ move[:color] = info[:property]
57
+ if !info[:data].empty?
58
+ move[:x] = @symbol_table[info[:data][0].chr]
59
+ move[:y] = @symbol_table[info[:data][1].chr]
60
+ end
61
+ when 'BL', 'WL'
62
+ move[:time] = info[:data]
63
+ when 'OB', 'OW'
64
+ move[:ot_stones] = info[:data]
65
+ end
66
+ end
67
+ @move_list << move
68
+ end
69
+ end
70
+
71
+ def player_black
72
+ return @properties["PB"]
73
+ end
74
+
75
+ def player_white
76
+ return @properties["PW"]
77
+ end
78
+
79
+ def rank_black
80
+ return @properties["BR"]
81
+ end
82
+
83
+ def rank_white
84
+ return @properties["WR"]
85
+ end
86
+
87
+ def board_size
88
+ return @properties.include?("SZ") ? @properties["SZ"].to_i : 19
89
+ end
90
+
91
+ def komi
92
+ return @properties["KM"]
93
+ end
94
+
95
+ def result
96
+ return @properties["RE"]
97
+ end
98
+
99
+ def handicap
100
+ return @properties["HA"]
101
+ end
102
+
103
+ def player_time
104
+ return @properties["TM"]
105
+ end
106
+
107
+ def game_date
108
+ return @properties["DT"]
109
+ end
110
+
111
+ def game_event
112
+ return @properties["EV"]
113
+ end
114
+
115
+ def game_round
116
+ return @properties["RO"]
117
+ end
118
+
119
+ def game_place
120
+ return @properties["PC"]
121
+ end
122
+
123
+ def game_rules
124
+ return @properties["RU"]
125
+ end
126
+
127
+ def game_name
128
+ return @properties["GN"]
129
+ end
130
+
131
+ end
132
+ end
@@ -0,0 +1,9 @@
1
+ module KantanSgf #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/kantan-sgf.rb ADDED
@@ -0,0 +1,4 @@
1
+ dir = File.dirname(__FILE__)
2
+
3
+ require "#{dir}/kantan-sgf/sgf"
4
+ require "#{dir}/kantan-sgf/version"
@@ -0,0 +1,25 @@
1
+ dir = File.dirname(__FILE__)
2
+ require 'rubygems'
3
+ require 'treetop'
4
+ require "test/unit"
5
+
6
+ # Grammar
7
+ Treetop.load "#{dir}/../lib/grammar/sgf-grammar.tt"
8
+
9
+ class SgfGrammarTest < Test::Unit::TestCase
10
+
11
+ def setup
12
+ @sgf = SgfGrammarParser.new
13
+ f = File.open("../data/game-01.sgf")
14
+ @data = f.read
15
+ f.close
16
+ end
17
+
18
+ def test_parse
19
+ result = @sgf.parse(@data)
20
+ #puts @sgf.failure_reason
21
+ #puts result.value.inspect
22
+ assert result
23
+ end
24
+
25
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kantan-sgf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Brian bojo Jones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-02 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: a Simple Game Format (SGF) parser
17
+ email: mojobojo@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ - LICENSE
25
+ files:
26
+ - data/game-01.sgf
27
+ - data/stoic-bojo.sgf
28
+ - examples/kantan_example.rb
29
+ - lib/grammar/sgf-grammar.rb
30
+ - lib/grammar/sgf-grammar.tt
31
+ - lib/kantan-sgf/sgf.rb
32
+ - lib/kantan-sgf/version.rb
33
+ - lib/kantan-sgf.rb
34
+ - LICENSE
35
+ - Rakefile
36
+ - README
37
+ - test/sgf-grammar_test.rb
38
+ has_rdoc: false
39
+ homepage: http://github.com/boj/kantan-sgf
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.0.1
61
+ signing_key:
62
+ specification_version: 2
63
+ summary: a Simple Game Format (SGF) parser
64
+ test_files: []
65
+