SgfParser 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SGF::Gametree do
4
+
5
+ let(:game) { get_first_game_from 'spec/data/ff4_ex.sgf' }
6
+ let(:root_node) {
7
+ SGF::Node.new({FF: '4', AP: 'Primiview:3.1',
8
+ GM: '1', SZ: '19', GN: 'Gametree 1: properties',
9
+ US: 'Arno Hollosi'})
10
+ }
11
+ subject { game }
12
+
13
+ it 'should throw up if initialized with a non-Node argument' do
14
+ expect { SGF::Gametree.new('I am a string') }.to raise_error(ArgumentError)
15
+ expect { SGF::Gametree.new(SGF::Node.new) }.not_to raise_error
16
+ end
17
+
18
+ it 'has game-level information' do
19
+ expect(subject.name).to eq 'Gametree 1: properties'
20
+ expect(subject.data_entry).to eq 'Arno Hollosi'
21
+ end
22
+
23
+ it 'should raise errors' do
24
+ expect { subject.opening }.to raise_error(SGF::NoIdentityError)
25
+ expect { subject.nonexistent_identity }.to raise_error(SGF::NoIdentityError)
26
+ end
27
+
28
+ it 'has meta information' do
29
+ expect(subject.inspect).to match(/SGF::Gametree/)
30
+ expect(subject.inspect).to match(/#{game.object_id}/)
31
+ end
32
+
33
+ context "When talking about nodes" do
34
+ it "begins on the root node" do
35
+ expect(subject.current_node).to eq root_node
36
+ end
37
+
38
+ it "should have a nice way to go to children[0]" do
39
+ subject.next_node
40
+ expect(subject.current_node).to eq subject.root.children[0]
41
+ end
42
+
43
+ it "should have a way of setting an arbitrary node to the current node" do
44
+ subject.current_node = subject.root.children[3]
45
+ expect(subject.current_node.properties.keys).to match_array %w(B C N)
46
+ expect(subject.current_node.children.size).to eq 6
47
+ end
48
+
49
+ end
50
+
51
+ context "blocks" do
52
+ it "should use preorder traversal for each" do
53
+ game = get_first_game_from 'spec/data/example1.sgf'
54
+ array = []
55
+ game.each { |node| array << node }
56
+ expect(array[0].c).to eq "root"
57
+ expect(array[1].c).to eq "a"
58
+ expect(array[2].c).to eq "b"
59
+ end
60
+
61
+ it "should go through all nodes, even if block returns 'nil' (puts, anyone?)" do
62
+ game = SGF::Gametree.new root_node
63
+ game.root.add_children SGF::Node.new(B: "dd")
64
+ nodes = []
65
+ game.each { |node| nodes << node; nil }
66
+ expect(nodes.size).to eq 2
67
+ end
68
+ end
69
+
70
+ context "Slices" do
71
+
72
+ it "should return a node-only gametree if slice is [0..0]" do
73
+ game = SGF::Gametree.new root_node
74
+ slice = game.slice(0..0)
75
+ expect(slice).to be_instance_of SGF::Gametree
76
+ expect(slice.root).to eq root_node
77
+ end
78
+
79
+ it "should slice through a one-branch list" do
80
+ node = SGF::Node.new
81
+ game = SGF::Gametree.new node
82
+ node.pw = 'Aldric'
83
+ child1 = SGF::Node.new b: "qq"
84
+ child2 = SGF::Node.new a: "rn"
85
+ child3 = SGF::Node.new b: "nr"
86
+ node.add_children child1
87
+ child1.add_children child2
88
+ child2.add_children child3
89
+ slice = game.slice(1..3)
90
+ expect(slice).to be_instance_of SGF::Gametree
91
+ root = slice.root
92
+ expect(root.b).to eq 'qq'
93
+ expect(root.parent).to be_nil
94
+ expect(root.children[0].a).to eq 'rn'
95
+ expect(root.children[0].children[0].b).to eq 'nr'
96
+ end
97
+
98
+ end
99
+ end
@@ -1,90 +1,196 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
- describe "SGF::Node" do
3
+ RSpec.describe SGF::Node do
4
4
 
5
- before :each do
6
- @node = SGF::Node.new
7
- end
5
+ let(:node) { SGF::Node.new }
6
+ subject { node }
8
7
 
9
- it "should be a valid node" do
10
- @node.class.should == SGF::Node
11
- @node.properties.should == {}
12
- @node.parent.should == nil
13
- @node.children.should == []
14
- end
8
+ context 'inspect' do
9
+ subject { node.inspect }
10
+ it { is_expected.to match(/#{node.object_id}/) }
11
+ it { is_expected.to match(/SGF::Node/) }
12
+ it { is_expected.to match(/Has no parent/) }
15
13
 
16
- it "should store properties" do
17
- @node.add_properties "PB" => "Dosaku"
18
- @node.properties.should == {"PB" => "Dosaku"}
19
- end
14
+ it "should give you a relatively useful inspect" do
15
+ node.add_properties({C: "Oh hi", PB: "Dosaku", AE: "[dd][gh]"})
16
+ is_expected.to match(/3 Properties/)
20
17
 
21
- it "should link to a parent" do
22
- parent = SGF::Node.new
23
- @node.parent = parent
24
- @node.parent.should == parent
25
- end
18
+ node.add_children SGF::Node.new, SGF::Node.new
19
+ expect(node.inspect).to match(/2 Children/)
26
20
 
27
- it "should link to children" do
28
- child1 = SGF::Node.new
29
- child2 = SGF::Node.new
30
- child3 = SGF::Node.new
31
- @node.add_children child1, child2, child3
32
- @node.children.should == [child1, child2, child3]
33
- end
21
+ node.parent = SGF::Node.new
22
+ expect(node.inspect).to match(/Has a parent/)
23
+ end
34
24
 
35
- it "should link to children, who should get new parents" do
36
- child1 = SGF::Node.new
37
- child2 = SGF::Node.new
38
- child3 = SGF::Node.new
39
- @node.add_children child1, child2, child3
40
- @node.children.each { |child| child.parent.should == @node }
41
25
  end
42
26
 
43
- it "should allow concatenation of properties" do
44
- @node.add_properties "TC" => "Hello,"
45
- @node.add_properties "TC" => " world!"
46
- @node.properties["TC"].should == "Hello, world!"
27
+ it "should properly show a string version of the node" do
28
+ subject.add_properties({"C" => "Oh hi]", "PB" => "Dosaku"})
29
+ expect(subject.to_s).to eq ";C[Oh hi\\]]\nPB[Dosaku]"
47
30
  end
48
31
 
49
- it "should give you the properties based on method given" do
50
- @node.add_properties "PW" => "The Tick"
51
- @node.add_properties "PB" => "Batmanuel"
52
- @node.pw.should == "The Tick"
53
- @node.pb.should == "Batmanuel"
32
+ it "should properly show a string version of the node if identities are symbols" do
33
+ subject.add_properties({C: "Oh hi]", PB: "Dosaku"})
34
+ expect(subject.to_s).to eq ";C[Oh hi\\]]\nPB[Dosaku]"
54
35
  end
55
36
 
56
- it "should allow you to change a property completely" do
57
- @node.add_properties "RE" => "This is made up"
58
- @node.properties["RE"] = "This is also made up"
59
- @node.re.should == "This is also made up"
60
- @node.re = "And that too"
61
- @node.re.should == "And that too"
37
+ context "Heredity" do
38
+
39
+ let(:parent) { SGF::Node.new }
40
+ let(:child1) { SGF::Node.new }
41
+ let(:child3) { SGF::Node.new }
42
+ let(:child2) { SGF::Node.new }
43
+
44
+ it "should link to a parent" do
45
+ subject.parent = parent
46
+ expect(subject.parent).to eq parent
47
+ end
48
+
49
+ it "should link to children" do
50
+ node.add_children child1, child2, child3
51
+ expect(node.children).to eq [child1, child2, child3]
52
+ end
53
+
54
+ it "should link to children, who should get new parents" do
55
+ node.add_children child1, child2, child3
56
+ node.children.each { |child| expect(child.parent).to eq node }
57
+ end
58
+
59
+ it "should not be the child of many nodes" do
60
+ parent2 = SGF::Node.new
61
+ parent.add_children node
62
+ parent2.add_children node
63
+ expect(node.parent).to eq(parent2)
64
+ expect(parent2.children).to include(node)
65
+ expect(parent.children).to_not include(node)
66
+ end
67
+
68
+ it "should become a child of its new parent" do
69
+ node.parent = parent
70
+ expect(parent.children).to include node
71
+ end
72
+
62
73
  end
63
74
 
64
- it "should implement [] as a shortcut to read properties" do
65
- @node.add_properties "PB" => "Dosaku"
66
- @node["PB"].should == "Dosaku"
67
- @node[:PB].should == "Dosaku"
75
+ context "Properties" do
76
+
77
+ it "should store properties" do
78
+ node.add_properties PB: "Dosaku"
79
+ expect(node.properties).to eq({"PB" => "Dosaku"})
80
+ end
81
+
82
+ it "should allow concatenation of properties" do
83
+ node.add_properties "TC" => "Hello,"
84
+ node.add_properties "TC" => " world!"
85
+ expect(node.properties["TC"]).to eq "Hello, world!"
86
+ end
87
+
88
+ it "should give you the properties based on method given" do
89
+ node.add_properties "PW" => "The Tick"
90
+ node.add_properties "PB" => "Batmanuel"
91
+ expect(node.pw).to eq "The Tick"
92
+ expect(node.pb).to eq "Batmanuel"
93
+ end
94
+
95
+ it "should allow you to change a property completely" do
96
+ node.add_properties "RE" => "This is made up"
97
+ node.properties["RE"] = "This is also made up"
98
+ expect(node.re).to eq "This is also made up"
99
+ node.re = "And that too"
100
+ expect(node.re).to eq "And that too"
101
+ node[:RE] = 'kokolegorille'
102
+ expect(node[:RE]).to eq 'kokolegorille'
103
+ end
104
+
105
+ it "should implement [] as a shortcut to read properties" do
106
+ node.add_properties "PB" => "Dosaku"
107
+ expect(node["PB"]).to eq "Dosaku"
108
+ expect(node[:PB]).to eq "Dosaku"
109
+ end
110
+
68
111
  end
69
112
 
70
- it "should give you a relatively useful inspect" do
71
- @node.inspect.should match /#{@node.object_id}/
72
- @node.inspect.should match /SGF::Node/
113
+ context "Node depth" do
114
+
115
+ it "should get a node depth number one more by the parent when attached to a parent" do
116
+ child = SGF::Node.new
117
+ node.add_children child
118
+ expect(node.depth).to eq 0
119
+ expect(child.depth).to eq 1
120
+ end
121
+
122
+ it "should properly set depth if a parent is passed to initializer" do
123
+ node2 = SGF::Node.new parent: node
124
+ expect(node2.parent).to eq node
125
+ expect(node2.depth).to eq 1
126
+ end
127
+
128
+ it "should properly update depth when parentage changes" do
129
+ link1 = SGF::Node.new
130
+ link2 = SGF::Node.new
131
+ link2.parent = link1
132
+ link1.parent = node
133
+ expect(node.parent).to be_nil
134
+ expect(link1.parent).to eq node
135
+ expect(link2.parent).to eq link1
136
+ expect(node.depth).to eq 0
137
+ expect(link1.depth).to eq 1
138
+ expect(link2.depth).to eq 2
139
+ end
140
+
141
+ it "should properly update depth if parent is set to nil / parent is removed" do
142
+ parent = SGF::Node.new
143
+ child = SGF::Node.new
144
+
145
+ node.add_children child
146
+ node.parent = parent
147
+ expect(node.depth).to eq 1
148
+ expect(child.depth).to eq 2
149
+
150
+ node.parent = nil
151
+ expect(node.depth).to eq 0
152
+ expect(child.depth).to eq 1
153
+
154
+ node.parent = parent
155
+ expect(node.depth).to eq 1
156
+ expect(child.depth).to eq 2
157
+
158
+ node.remove_parent
159
+ expect(node.depth).to eq 0
160
+ expect(child.depth).to eq 1
161
+ end
162
+
163
+ it "should properly update depth when childhood changes" do
164
+ link1 = SGF::Node.new
165
+ link2 = SGF::Node.new
166
+ link3 = SGF::Node.new
167
+ link1.add_children link2
168
+ link2.add_children link3
169
+ node.add_children link1
170
+ node.add_children link3
171
+ expect(node.depth).to eq 0
172
+ expect(link1.depth).to eq 1
173
+ expect(link2.depth).to eq 2
174
+ expect(link3.depth).to eq 1
175
+ end
176
+ end
73
177
 
74
- @node.add_properties({"C" => "Oh hi", "PB" => "Dosaku", "AE" => "[dd][gh]"})
75
- @node.inspect.should match /3 Properties/
178
+ context "self-consistency" do
179
+ it "should only track changes from its current parent" do
180
+ link1 = SGF::Node.new
181
+ link2 = SGF::Node.new
76
182
 
77
- @node.add_children SGF::Node.new, SGF::Node.new
78
- @node.inspect.should match /2 Children/
183
+ link3 = SGF::Node.new
184
+ link1.add_children link2
185
+ node.parent = link2
186
+ expect(node.depth).to eq 2
79
187
 
80
- @node.inspect.should match /Has no parent/
81
- @node.parent = SGF::Node.new
82
- @node.inspect.should match /Has a parent/
83
- end
188
+ link1.depth = 2
189
+ expect(node.depth).to eq 4
84
190
 
85
- it "should properly show a string version of the node" do
86
- @node.add_properties({"C" => "Oh hi]", "PB" => "Dosaku"})
87
- @node.to_str.should == ";C[Oh hi\\]]\nPB[Dosaku]"
191
+ node.parent = link3
192
+ link1.depth = 6
193
+ expect(node.depth).to eq 1
194
+ end
88
195
  end
89
-
90
- end
196
+ end
@@ -1,117 +1,119 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
- describe "SGF::Parser" do
3
+ RSpec.describe SGF::Parser do
4
4
 
5
- it "should give an error if the first two character are not (;" do
6
- parser = sgf_parser
7
- expect { parser.parse ';)' }.to raise_error SGF::MalformedDataError
8
- end
5
+ let(:parser) { SGF::Parser.new }
6
+
7
+ describe "when given invalid SGF files" do
8
+ it "should give an error if the first two character are not (;" do
9
+ expect { parser.parse ';)' }.to raise_error SGF::MalformedDataError
10
+ end
11
+
12
+ it "should not give an error if it is told to sit down and shut up" do
13
+ expect { parser.parse(';)', false) }.to_not raise_error
14
+ end
15
+
16
+ it "should properly parse a file that has many AB/AW/AE values in a single node" do
17
+ invalid_sgf = "(;AB[dd]AB[aa])"
18
+ collection = parser.parse invalid_sgf
19
+
20
+ node = SGF::Node.new AB: %w[dd aa]
21
+ expected = SGF::Collection.new
22
+ expected.root.add_children node
9
23
 
10
- it "should not give an error if it is told to sit down and shut up" do
11
- parser = sgf_parser
12
- expect { parser.parse ';)', false}.to_not raise_error
24
+ expect(collection).to eq expected
25
+ expect(collection.errors).to include "Multiple AB identities are present in a single node. A property should only exist once per node."
26
+ end
13
27
  end
14
28
 
15
29
  it "should parse a simple node" do
16
- parser = sgf_parser
17
- tree = parser.parse ";PW[5]", false
18
- tree.root.children[0].pw.should == "5"
30
+ collection = parser.parse ";PW[Dosaku]", false
31
+ expect(collection.root.children[0].pw).to eq "Dosaku"
19
32
  end
20
33
 
21
34
  it "should parse a node with two properties" do
22
- parser = sgf_parser
23
- tree = parser.parse ";PB[Aldric]PW[Bob]", false
24
- tree.root.children[0].pb.should == "Aldric"
25
- tree.root.children[0].pw.should == "Bob"
35
+ collection = parser.parse ";PB[Aldric]PW[Bob]", false
36
+ expect(collection.root.children[0].pb).to eq "Aldric"
37
+ expect(collection.root.children[0].pw).to eq "Bob"
38
+ end
39
+
40
+ it "should apparently ignore line breaks in the middle of a property name -- is this right?" do
41
+ collection = parser.parse ";P\nB[Aldric]P\n\n\nW[Bob]", false
42
+ expect(collection.root.children[0].pb).to eq "Aldric"
43
+ expect(collection.root.children[0].pw).to eq "Bob"
26
44
  end
27
45
 
28
46
  it "should parse two nodes with one property each" do
29
- parser = sgf_parser
30
- tree = parser.parse ";PB[Aldric];PW[Bob]", false
31
- node = tree.root.children[0]
32
- node.pb.should == "Aldric"
33
- node.children[0].pw.should == "Bob"
47
+ collection = parser.parse ";PB[Aldric];PW[Bob]", false
48
+ node = collection.root.children[0]
49
+ expect(node.pb).to eq "Aldric"
50
+ expect(node.children[0].pw).to eq "Bob"
34
51
  end
35
52
 
36
53
  it "should parse a tree with a single branch" do
37
- parser = sgf_parser
38
- tree = parser.parse "(;FF[4]PW[Aldric]PB[Bob];B[qq])"
39
- node = tree.root.children[0]
40
- node.pb.should == "Bob"
41
- node.pw.should == "Aldric"
42
- node.children[0].b.should == "qq"
54
+ collection = parser.parse "(;FF[4]PW[Aldric]PB[Bob];B[qq])"
55
+ node = collection.root.children[0]
56
+ expect(node.pb).to eq "Bob"
57
+ expect(node.pw).to eq "Aldric"
58
+ expect(node.children[0].b).to eq "qq"
43
59
  end
44
60
 
45
61
  it "should parse a tree with two branches" do
46
- parser = sgf_parser
47
- tree = parser.parse "(;FF[4](;C[main])(;C[branch]))"
48
- node = tree.root.children[0]
49
- node.ff.should == "4"
50
- node.children.size.should == 2
51
- node.children[0].c.should == "main"
52
- node.children[1].c.should == "branch"
62
+ collection = parser.parse "(;FF[4](;C[main])(;C[branch]))"
63
+ node = collection.root.children[0]
64
+ expect(node.ff).to eq "4"
65
+ expect(node.children.size).to eq 2
66
+ expect( node.children[0].c).to eq "main"
67
+ expect(node.children[1].c).to eq "branch"
53
68
  end
54
69
 
55
- it "should parse a comment with a ] inside" do
56
- parser = sgf_parser
57
- tree = parser.parse "(;C[Oh hi\\] there])"
58
- tree.root.children[0].c.should == "Oh hi] there"
70
+ it "should parse a comment with a \\] inside" do
71
+ collection = parser.parse "(;C[Oh hi\\] there])"
72
+ expect(collection.root.children[0].c).to eq "Oh hi] there"
59
73
  end
60
74
 
61
75
  it "should parse a multi-property identity well" do
62
- parser = sgf_parser
63
- tree = parser.parse "(;FF[4];AW[bd][cc][qr])"
64
- tree.root.children[0].children[0].aw.should == ["bd", "cc", "qr"]
76
+ collection = parser.parse "(;FF[4];AW[bd][cc][qr])"
77
+ expect(collection.root.children[0].children[0].aw).to eq %w(bd cc qr)
65
78
  end
66
79
 
67
80
  it "should parse multiple trees" do
68
- parser = sgf_parser
69
- tree = parser.parse "(;FF[4]PW[Aldric];AW[dd][cc])(;FF[4]PW[Hi];PB[ad])"
70
- tree.root.children.size.should == 2
71
- tree.root.children[0].children[0].aw.should == ["dd", "cc"]
72
- tree.root.children[1].children[0].pb.should == "ad"
81
+ collection = parser.parse "(;FF[4]PW[Aldric];AW[dd][cc])(;FF[4]PW[Hi];PB[ad])"
82
+ expect(collection.root.children.size).to eq 2
83
+ expect(collection.root.children[0].children[0].aw).to eq %w(dd cc)
84
+ expect(collection.root.children[1].children[0].pb).to eq "ad"
73
85
  end
74
86
 
75
87
  it "should not put (; into the identity when separated by line breaks" do
76
- parser = sgf_parser
77
- tree = parser.parse "(;FF[4]\n\n(;B[dd])(;B[da]))"
78
- game = tree.root.children[0]
79
- game.children.size.should == 2
80
- game.children[0].b.should == "dd"
81
- game.children[1].b.should == "da"
88
+ collection = parser.parse "(;FF[4]\n\n(;B[dd])(;B[da]))"
89
+ game = collection.root.children[0]
90
+ expect(game.children.size).to eq 2
91
+ expect(game.children[0].b).to eq "dd"
92
+ expect( game.children[1].b).to eq "da"
82
93
  end
83
94
 
84
95
  it "should parse the simplified sample SGF" do
85
- parser = sgf_parser
86
- tree = parser.parse SIMPLIFIED_SAMPLE_SGF
87
- root = tree.root
88
- root.children.size.should == 2
96
+ collection = parser.parse SIMPLIFIED_SAMPLE_SGF
97
+ root = collection.root
98
+ expect(root.children.size).to eq 2
89
99
  node = root.children[0].children[0]
90
- node.ar.should == ["aa:sc", "sa:ac", "aa:sa", "aa:ac", "cd:cj", "gd:md", "fh:ij", "kj:nh"]
100
+ expect(node.ar).to eq %w(aa:sc sa:ac aa:sa aa:ac cd:cj gd:md fh:ij kj:nh)
91
101
  end
92
102
 
93
103
  it "should parse a file if given a file handler as input" do
94
- parser = sgf_parser
95
104
  file = File.open 'spec/data/simple.sgf'
96
- tree = parser.parse file
97
- game = tree.games.first
98
- game.white_player.should == "redrose"
99
- game.black_player.should == "tartrate"
105
+ collection = parser.parse file
106
+ game = collection.gametrees.first
107
+ expect(game.white_player).to eq "redrose"
108
+ expect(game.black_player).to eq "tartrate"
100
109
  end
101
110
 
102
- it "should parse a file if given a local path as input" do
103
- parser = sgf_parser
111
+ it "should parse a file if given a local path as input" do
104
112
  local_path = 'spec/data/simple.sgf'
105
- tree = parser.parse local_path
106
- game = tree.games.first
107
- game.white_player.should == "redrose"
108
- game.black_player.should == "tartrate"
109
- end
110
-
111
- private
112
-
113
- def sgf_parser
114
- SGF::Parser.new
113
+ collection = parser.parse local_path
114
+ game = collection.gametrees.first
115
+ expect(game.white_player).to eq "redrose"
116
+ expect(game.black_player).to eq "tartrate"
115
117
  end
116
118
 
117
119
  end