SgfParser 0.9.0 → 0.9.1
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.
- data/.document +5 -5
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +27 -0
- data/Rakefile +11 -26
- data/SgfParser.gemspec +71 -67
- data/VERSION +1 -1
- data/lib/sgf/parser/node.rb +0 -12
- data/lib/sgf/parser/tree.rb +9 -20
- data/lib/sgf/parser/tree_parse.rb +23 -9
- data/lib/sgf/sgfindent.rb +118 -0
- data/sample_sgf/ff4_ex_saved.sgf +45 -0
- data/sample_sgf/simple.sgf +11 -0
- data/sample_sgf/simple_saved.sgf +7 -0
- data/spec/spec_helper.rb +2 -5
- data/spec/tree_parser_spec.rb +13 -2
- data/spec/tree_spec.rb +28 -9
- metadata +57 -19
- data/.gitignore +0 -25
- data/spec/spec.opts +0 -1
- data/tryme.rb +0 -3
data/.document
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
README.rdoc
|
2
|
-
lib/**/*.rb
|
3
|
-
bin/*
|
4
|
-
features/**/*.feature
|
5
|
-
LICENSE
|
1
|
+
README.rdoc
|
2
|
+
lib/**/*.rb
|
3
|
+
bin/*
|
4
|
+
features/**/*.feature
|
5
|
+
LICENSE
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format doc
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2@jrun
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rake (0.9.2)
|
11
|
+
rcov (0.9.9)
|
12
|
+
rspec (2.6.0)
|
13
|
+
rspec-core (~> 2.6.0)
|
14
|
+
rspec-expectations (~> 2.6.0)
|
15
|
+
rspec-mocks (~> 2.6.0)
|
16
|
+
rspec-core (2.6.4)
|
17
|
+
rspec-expectations (2.6.0)
|
18
|
+
diff-lcs (~> 1.1.2)
|
19
|
+
rspec-mocks (2.6.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
jeweler
|
26
|
+
rcov
|
27
|
+
rspec
|
data/Rakefile
CHANGED
@@ -18,38 +18,23 @@ rescue LoadError
|
|
18
18
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
19
|
end
|
20
20
|
|
21
|
-
require '
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
require 'rspec/core/rake_task'
|
22
|
+
desc 'Default: run specs.'
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
desc "Run specs"
|
26
|
+
RSpec::Core::RakeTask.new do |spec|
|
27
|
+
spec.pattern = "./spec/**/*_spec.rb"
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
|
-
spec.libs << 'lib' << 'spec'
|
30
|
+
RSpec::Core::RakeTask.new(:coverage) do |spec|
|
29
31
|
spec.pattern = 'spec/**/*_spec.rb'
|
30
32
|
spec.rcov = true
|
33
|
+
spec.rcov_opts = ['--exclude', 'spec']
|
31
34
|
end
|
32
35
|
|
33
|
-
task
|
34
|
-
|
35
|
-
# In case I ever want to go back to cucumber.
|
36
|
-
#begin
|
37
|
-
# require 'cucumber/rake/task'
|
38
|
-
# Cucumber::Rake::Task.new(:features)
|
39
|
-
#
|
40
|
-
# task :features => :check_dependencies
|
41
|
-
#rescue LoadError
|
42
|
-
# task :features do
|
43
|
-
# abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
44
|
-
# end
|
45
|
-
#end
|
46
|
-
|
47
|
-
#task :default => [:spec, :features]
|
48
|
-
|
49
|
-
task :default => [:spec]
|
50
|
-
|
51
|
-
require 'rake/rdoctask'
|
52
|
-
Rake::RDocTask.new do |rdoc|
|
36
|
+
require 'rdoc/task'
|
37
|
+
RDoc::Task.new do |rdoc|
|
53
38
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
54
39
|
|
55
40
|
rdoc.rdoc_dir = 'rdoc'
|
data/SgfParser.gemspec
CHANGED
@@ -1,67 +1,71 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{SgfParser}
|
8
|
-
s.version = "0.9.
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Aldric Giacomoni"]
|
12
|
-
s.date = %q{
|
13
|
-
s.description = %q{SGFParser is a library that parses and saves SGF (Smart Game Format) files.}
|
14
|
-
s.email = %q{aldric@trevoke.net}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
60
|
-
else
|
61
|
-
s.add_dependency(%q<
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{SgfParser}
|
8
|
+
s.version = "0.9.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Aldric Giacomoni"]
|
12
|
+
s.date = %q{2011-07-29}
|
13
|
+
s.description = %q{SGFParser is a library that parses and saves SGF (Smart Game Format) files.}
|
14
|
+
s.email = %q{aldric@trevoke.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE",
|
26
|
+
"README.rdoc",
|
27
|
+
"Rakefile",
|
28
|
+
"SgfParser.gemspec",
|
29
|
+
"VERSION",
|
30
|
+
"lib/sgf/parser/node.rb",
|
31
|
+
"lib/sgf/parser/properties.rb",
|
32
|
+
"lib/sgf/parser/tree.rb",
|
33
|
+
"lib/sgf/parser/tree_parse.rb",
|
34
|
+
"lib/sgf/sgf_indent.rb",
|
35
|
+
"lib/sgf/sgfindent.rb",
|
36
|
+
"lib/sgf_parser.rb",
|
37
|
+
"sample_sgf/ff4_ex.sgf",
|
38
|
+
"sample_sgf/ff4_ex_saved.sgf",
|
39
|
+
"sample_sgf/redrose-tartrate.sgf",
|
40
|
+
"sample_sgf/simple.sgf",
|
41
|
+
"sample_sgf/simple_saved.sgf",
|
42
|
+
"sample_usage/parsing_files.rb",
|
43
|
+
"spec/node_spec.rb",
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/tree_parser_spec.rb",
|
46
|
+
"spec/tree_spec.rb"
|
47
|
+
]
|
48
|
+
s.homepage = %q{http://github.com/Trevoke/SGFParser}
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = %q{1.6.2}
|
51
|
+
s.summary = %q{A library for working with SGF files.}
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
58
|
+
s.add_development_dependency(%q<rcov>, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
62
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
63
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rcov>, [">= 0"])
|
68
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.1
|
data/lib/sgf/parser/node.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
module SgfParser
|
2
2
|
|
3
|
-
# Each part of the SGF Tree is a node. This holds links to the parent node,
|
4
|
-
# to the next node(s) in the tree, and of course, the properties of said node.
|
5
|
-
# Accessors : node.parent, node.children, node.properties
|
6
3
|
class Node
|
7
4
|
|
8
5
|
attr_accessor :parent, :children, :properties
|
@@ -19,9 +16,6 @@ module SgfParser
|
|
19
16
|
@properties.merge! args[:properties] if !args[:properties].nil?
|
20
17
|
end
|
21
18
|
|
22
|
-
# Adds one child or several children. Can be passed in as a comma-separated
|
23
|
-
# list or an array of node children. Will raise an error if one of the
|
24
|
-
# arguments is not of class Node.
|
25
19
|
def add_children *nodes
|
26
20
|
nodes.flatten!
|
27
21
|
raise "Non-node child given!" if nodes.find { |node| node.class != Node }
|
@@ -30,8 +24,6 @@ module SgfParser
|
|
30
24
|
@children.concat nodes
|
31
25
|
end
|
32
26
|
|
33
|
-
# Adds one or more properties to the node.
|
34
|
-
# Argument: a hash {'property' => 'value'}
|
35
27
|
def add_properties hash
|
36
28
|
hash.each do |key, value|
|
37
29
|
@properties[key] ||= ""
|
@@ -39,18 +31,14 @@ module SgfParser
|
|
39
31
|
end
|
40
32
|
end
|
41
33
|
|
42
|
-
# Iterates over each child of the given node
|
43
|
-
# node.each_child { |child| puts child.properties }
|
44
34
|
def each_child
|
45
35
|
@children.each { |child| yield child }
|
46
36
|
end
|
47
37
|
|
48
|
-
# Compares one node's properties to another node's properties
|
49
38
|
def == other_node
|
50
39
|
@properties == other_node.properties
|
51
40
|
end
|
52
41
|
|
53
|
-
# Making comments easier to access.
|
54
42
|
def comments
|
55
43
|
@properties["C"]
|
56
44
|
end
|
data/lib/sgf/parser/tree.rb
CHANGED
@@ -1,19 +1,10 @@
|
|
1
1
|
module SgfParser
|
2
2
|
|
3
|
-
# This is a placeholder for the root of the gametree(s). It's an abstraction,
|
4
|
-
# but it allows an easy way to save, iterate over, and compare other trees.
|
5
|
-
# Accessors: tree.root
|
6
3
|
class Tree
|
7
4
|
include Enumerable
|
8
5
|
|
9
6
|
attr_accessor :root
|
10
7
|
|
11
|
-
# Create a new tree. Can also be used to load a tree from either a file or
|
12
|
-
# a string. Raises an error if both are provided.
|
13
|
-
# options: \n
|
14
|
-
# :filename => filename \n
|
15
|
-
# !!! OR !!! \n
|
16
|
-
# :string => string \n
|
17
8
|
def initialize args={}
|
18
9
|
@root = Node.new
|
19
10
|
@sgf = ""
|
@@ -27,10 +18,9 @@ module SgfParser
|
|
27
18
|
parse unless @sgf.empty?
|
28
19
|
end
|
29
20
|
|
30
|
-
# Iterates over the tree, node by node, in preorder fashion.
|
31
|
-
# Does not support other types of iteration, but may in the future.
|
32
|
-
# tree.each { |node| puts "I am node. Hear me #{node.properties} !"}
|
33
21
|
def each order=:preorder, &block
|
22
|
+
# I know, I know. SGF is only preorder. Well, it's implemented, ain't it?
|
23
|
+
# Stop complaining.
|
34
24
|
case order
|
35
25
|
when :preorder
|
36
26
|
preorder @root, &block
|
@@ -51,10 +41,8 @@ module SgfParser
|
|
51
41
|
# tree.save :filename => file_name
|
52
42
|
def save args={}
|
53
43
|
raise ArgumentError, "No file name provided" if args[:filename].nil?
|
54
|
-
# SGF files are trees stored in pre-order traversal.
|
55
44
|
@savable_sgf = "("
|
56
45
|
@root.children.each { |child| write_node child }
|
57
|
-
# write_node @root
|
58
46
|
@savable_sgf << ")"
|
59
47
|
|
60
48
|
File.open(args[:filename], 'w') { |f| f << @savable_sgf }
|
@@ -67,11 +55,12 @@ module SgfParser
|
|
67
55
|
@savable_sgf << ";"
|
68
56
|
unless node.properties.empty?
|
69
57
|
properties = ""
|
70
|
-
node.properties.each do |
|
71
|
-
|
72
|
-
|
58
|
+
node.properties.each do |identity, property|
|
59
|
+
new_property = property[1...-1]
|
60
|
+
new_property.gsub!("]", "\\]") if identity == "C"
|
61
|
+
properties += "#{identity.to_s}[#{new_property}]"
|
73
62
|
end
|
74
|
-
@savable_sgf <<
|
63
|
+
@savable_sgf << properties
|
75
64
|
end
|
76
65
|
|
77
66
|
case node.children.size
|
@@ -104,13 +93,13 @@ module SgfParser
|
|
104
93
|
preorder(child, &block)
|
105
94
|
end
|
106
95
|
end
|
107
|
-
end
|
96
|
+
end
|
108
97
|
|
109
98
|
def method_missing method_name, *args
|
110
99
|
output = @root.children[0].properties[method_name]
|
111
100
|
super(method_name, args) if output.nil?
|
112
101
|
output
|
113
|
-
end
|
102
|
+
end
|
114
103
|
|
115
104
|
end
|
116
105
|
|
@@ -3,9 +3,8 @@ require 'stringio'
|
|
3
3
|
module SgfParser
|
4
4
|
class Tree
|
5
5
|
|
6
|
-
private
|
6
|
+
#private
|
7
7
|
|
8
|
-
# Creates a tree (truly, a linked list) from @sgf.
|
9
8
|
def parse
|
10
9
|
while char = next_character
|
11
10
|
case char
|
@@ -65,14 +64,29 @@ module SgfParser
|
|
65
64
|
|
66
65
|
def get_property
|
67
66
|
buffer = ""
|
68
|
-
while
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
while char = next_character
|
68
|
+
case char
|
69
|
+
when "]" then break unless multiple_properties?
|
70
|
+
when "\\" then
|
71
|
+
char << next_character
|
72
|
+
char = "]" if char == "\\]"
|
73
|
+
end
|
74
|
+
|
75
|
+
buffer << char
|
76
|
+
end
|
77
|
+
"[#{buffer}]"
|
78
|
+
end
|
79
|
+
|
80
|
+
def multiple_properties?
|
81
|
+
multiple_properties = false
|
82
|
+
if char = next_character
|
83
|
+
char = next_character if char == "\n"
|
84
|
+
if char == "["
|
85
|
+
multiple_properties = true
|
86
|
+
end
|
87
|
+
@stream.pos -= 1
|
88
|
+
multiple_properties
|
74
89
|
end
|
75
|
-
buffer
|
76
90
|
end
|
77
91
|
|
78
92
|
def store_character(char)
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module SgfParser
|
2
|
+
|
3
|
+
# This indents an SGF file to make it more readable. It outputs to the screen
|
4
|
+
# by default, but can be given a file as output.
|
5
|
+
# Usage: SgfParser::Indenter.new infile, outfile
|
6
|
+
class Indenter
|
7
|
+
|
8
|
+
def initialize file, out=$stdout
|
9
|
+
sgf = ""
|
10
|
+
File.open(file) { |f| sgf = f.read }
|
11
|
+
@new_string = ""
|
12
|
+
sgf.gsub! "\\\\n\\\\r", ""
|
13
|
+
sgf.gsub! "\\\\r\\\\n", ""
|
14
|
+
sgf.gsub! "\\\\r", ""
|
15
|
+
sgf.gsub! "\\\\n", ""
|
16
|
+
#sgf.gsub! "\n", ""
|
17
|
+
|
18
|
+
end_of_a_series = false
|
19
|
+
identprop = false # We are not in the middle of an identprop value.
|
20
|
+
# An identprop is an identity property - a value.
|
21
|
+
|
22
|
+
sgf_array = sgf.split(//)
|
23
|
+
iterator = 0
|
24
|
+
array_length = sgf_array.size
|
25
|
+
indent = 0
|
26
|
+
|
27
|
+
while iterator < array_length - 1
|
28
|
+
char = sgf_array[iterator]
|
29
|
+
|
30
|
+
case char
|
31
|
+
when '(' # Opening a new branch
|
32
|
+
if !identprop
|
33
|
+
@new_string << "\n"
|
34
|
+
indent += 2
|
35
|
+
#tabulate indent
|
36
|
+
@new_string << " " * indent
|
37
|
+
end
|
38
|
+
@new_string << char
|
39
|
+
|
40
|
+
when ')' # Closing a branch
|
41
|
+
@new_string << char
|
42
|
+
if !identprop
|
43
|
+
@new_string << "\n"
|
44
|
+
indent -= 2
|
45
|
+
@new_string << " " * indent
|
46
|
+
#tabulate indent
|
47
|
+
end
|
48
|
+
|
49
|
+
when ';' # Opening a new node
|
50
|
+
if !identprop
|
51
|
+
@new_string << "\n"
|
52
|
+
@new_string << " " * indent
|
53
|
+
#tabulate indent
|
54
|
+
end
|
55
|
+
@new_string << char
|
56
|
+
|
57
|
+
when '[' # Open comment?
|
58
|
+
if !identprop #If we're not inside a comment, then now we are.
|
59
|
+
identprop = true
|
60
|
+
end_of_a_series = false
|
61
|
+
end
|
62
|
+
@new_string << char
|
63
|
+
|
64
|
+
when ']' # Close comment
|
65
|
+
end_of_a_series = true # Maybe end of a series of comments.
|
66
|
+
identprop = false # That's our cue to close a comment.
|
67
|
+
@new_string << char
|
68
|
+
|
69
|
+
when "\\" # If we're inside a comment, then maybe we're about to escape a ].
|
70
|
+
# This is the whole reason we need this ugly charade of a loop.
|
71
|
+
if identprop
|
72
|
+
if sgf_array[iterator + 1] == "]"
|
73
|
+
@new_string << "\\]"
|
74
|
+
iterator += 1
|
75
|
+
else
|
76
|
+
@new_string << "\\"
|
77
|
+
end
|
78
|
+
else
|
79
|
+
#This should never happen - a backslash outside a comment ?!
|
80
|
+
#But let's not have it be told that I'm not prepared.
|
81
|
+
@new_string << "\\"
|
82
|
+
end
|
83
|
+
|
84
|
+
when "\n"
|
85
|
+
@new_string << "\n"
|
86
|
+
@new_string << " " * indent
|
87
|
+
#tabulate indent
|
88
|
+
|
89
|
+
else
|
90
|
+
# Well, I guess it's "just" a character after all.
|
91
|
+
if end_of_a_series
|
92
|
+
end_of_a_series = false
|
93
|
+
end
|
94
|
+
@new_string << char
|
95
|
+
end
|
96
|
+
|
97
|
+
iterator += 1
|
98
|
+
end
|
99
|
+
|
100
|
+
if out == $stdout
|
101
|
+
$stdout << @new_string
|
102
|
+
else
|
103
|
+
File.open(out, 'w') { |file| file << @new_string }
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def tabulate indent
|
111
|
+
indent.times { print " " }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
(;FF[4]AP[Primiview:3.1]GM[1]SZ[19]GN[Gametree 1: properties]US[Arno Hollosi](;B[pd]N[Moves, comments, annotations]C[Nodename set to: "Moves, comments, annotations"];W[dp]GW[1]C[Marked as "Good for White"];B[pp]GB[2]C[Marked as "Very good for Black"];W[dc]GW[2]C[Marked as "Very good for White"];B[pj]DM[1]C[Marked as "Even position"];W[ci]UC[1]C[Marked as "Unclear position"];B[jd]TE[1]C[Marked as "Tesuji" or "Good move"];W[jp]BM[2]C[Marked as "Very bad move"];B[gd]DO[]C[Marked as "Doubtful move"];W[de]IT[]C[Marked as "Interesting move"];B[jj];C[White "Pass" move]W[];)(;AB[dd][de][df][dg][do:gq] AW[jd][je][jf][jg][kn:lq][pn:pq]N[Setup]C[Black & white stones at the top are added as single stones.
|
2
|
+
|
3
|
+
Black & white stones at the bottom are added using compressed point lists.];AE[ep][fp][kn][lo][lq][pn:pq]C[AddEmpty
|
4
|
+
|
5
|
+
Black stones & stones of left white group are erased in FF[3\] way.
|
6
|
+
|
7
|
+
White stones at bottom right were erased using compressed point list.];AB[pd]AW[pp]PL[B]C[Added two stones.
|
8
|
+
|
9
|
+
Node marked with "Black to play".];)(;AB[dd][de][df][dg][dh][di][dj][nj][ni][nh][nf][ne][nd][ij][ii][ih][hq][gq][fq][eq][dr][ds][dq][dp][cp][bp][ap][iq][ir][is][bo][bn][an][ms][mr]AW[pd][pe][pf][pg][ph][pi][pj][fd][fe][ff][fh][fi][fj][kh][ki][kj][os][or][oq][op][pp][qp][rp][sp][ro][rn][sn][nq][mq][lq][kq][kr][ks][fs][gs][gr][er]N[Markup]C[Position set up without compressed point lists.];TR[dd][de][df][ed][ee][ef][fd:ff] MA[dh][di][dj][ej][ei][eh][fh:fj] CR[nd][ne][nf][od][oe][of][pd:pf] SQ[nh][ni][nj][oh][oi][oj][ph:pj] SL[ih][ii][ij][jj][ji][jh][kh:kj] TW[pq:ss][so][lr:ns] TB[aq:cs][er:hs][ao]C[Markup at top partially using compressed point lists (for markup on white stones); listed clockwise, starting at upper left:
|
10
|
+
- TR (triangle)
|
11
|
+
- CR (circle)
|
12
|
+
- SQ (square)
|
13
|
+
- SL (selected points)
|
14
|
+
- MA ('X')
|
15
|
+
|
16
|
+
Markup at bottom: black & white territory (using compressed point lists)];LB[dc:1][fc:2][nc:3][pc:4][dj:a][fj:b][nj:c][pj:d][gs:ABCDEFGH][gr:ABCDEFG][gq:ABCDEF][gp:ABCDE][go:ABCD][gn:ABC][gm:AB][mm:12][mn:123][mo:1234][mp:12345][mq:123456][mr:1234567][ms:12345678]C[Label (LB property)
|
17
|
+
|
18
|
+
Top: 8 single char labels (1-4, a-d)
|
19
|
+
|
20
|
+
Bottom: Labels up to 8 char length.];)(;B[qd]N[Style & text type]C[There are hard linebreaks & soft linebreaks.
|
21
|
+
Soft linebreaks are linebreaks preceeded by '\\' like this one >o\
|
22
|
+
k<. Hard line breaks are all other linebreaks.
|
23
|
+
Soft linebreaks are converted to >nothing<, i.e. removed.
|
24
|
+
|
25
|
+
Note that linebreaks are coded differently on different systems.
|
26
|
+
|
27
|
+
Examples (>ok< shouldn't be split):
|
28
|
+
|
29
|
+
linebreak 1 "": >o\
|
30
|
+
k<
|
31
|
+
linebreak 2 "": >o\
|
32
|
+
|
33
|
+
linebreak 3 "": >o\
|
34
|
+
k<
|
35
|
+
linebreak 4 "": >o\
|
36
|
+
Black stones left (in this byo-yomi period): 10];W[qq]WL[200]OW[2]C[White time left: 200 sec
|
37
|
+
White stones left: 2];B[sr]BL[87.00]OB[9]C[Black time left: 87 sec
|
38
|
+
Black stones left: 9];W[qs]WL[13.20]OW[1]C[White time left: 13.2 sec
|
39
|
+
White stones left: 1];B[rs]C[One white stone at s2 captured];W[ps];B[pr];W[or]MN[2]C[Set move number to 2];B[os]C[Two white stones captured
|
40
|
+
(at q1 & r1)];MN[112]W[pq]C[Set move number to 112];B[sq];W[rp];B[ps];W[ns];B[ss];W[nr];B[rr];W[sp];);FF[4]AP[Primiview:3.1]GM[1]SZ[19]C[Gametree 2: game-info
|
41
|
+
|
42
|
+
Game-info properties are usually stored in the root node.
|
43
|
+
If games are merged into a single game-tree, they are stored in the node\
|
44
|
+
where the game first becomes distinguishable from all other games in\
|
45
|
+
the tree.];B[pd](;)(;)(;W[ep];B[pp](;)(;))
|
@@ -0,0 +1,11 @@
|
|
1
|
+
(;GM[1]FF[4]CA[UTF-8]
|
2
|
+
SZ[19]RU[Japanese]KM[0.50]TM[1290]OT[3x30 byo-yomi]
|
3
|
+
PB[tartrate]PW[redrose]BR[7d]WR[1p]DT[2003-06-18]PC[The Kiseido Go Server (KGS) at http://kgs.kiseido.com/]C[tartrate [7d?\]: hi
|
4
|
+
tartrate [7d?\]: thx
|
5
|
+
]RE[B+Resign]
|
6
|
+
;B[jj]BL[1280.503]CR[jj]C[redrose [1p\]: hi
|
7
|
+
Jerk [7d?\]: wow
|
8
|
+
Anark [11k\]: game of the day :)
|
9
|
+
Jerk [7d?\]: really big match
|
10
|
+
]
|
11
|
+
;W[ih]WL[1277.190]CR[ih];AB[aa][bb][cc])
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
3
|
require 'sgf_parser'
|
4
|
-
require '
|
5
|
-
require '
|
4
|
+
require 'rspec'
|
5
|
+
require 'rspec/autorun'
|
6
6
|
|
7
|
-
Spec::Runner.configure do |config|
|
8
|
-
|
9
|
-
end
|
data/spec/tree_parser_spec.rb
CHANGED
@@ -6,8 +6,19 @@ describe "SgfParser::Tree.parse" do
|
|
6
6
|
@tree = SgfParser::Tree.new :filename => 'sample_sgf/ff4_ex.sgf'
|
7
7
|
end
|
8
8
|
|
9
|
-
it "should
|
10
|
-
|
9
|
+
it "should have FF in the first node" do
|
10
|
+
@tree.root.children[0].properties.keys.should include("FF")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should give an error if FF is missing from the first node" do
|
14
|
+
pending "To be coded later"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should parse properly the AW property" do
|
18
|
+
input = StringIO.new "dd][de][ef]"
|
19
|
+
tree = SgfParser::Tree.new
|
20
|
+
tree.instance_variable_set "@stream", input
|
21
|
+
tree.get_property.should == "[dd][de][ef]"
|
11
22
|
end
|
12
23
|
|
13
24
|
end
|
data/spec/tree_spec.rb
CHANGED
@@ -3,20 +3,39 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
|
3
3
|
describe "SgfParser::Tree" do
|
4
4
|
|
5
5
|
before :each do
|
6
|
-
|
6
|
+
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should parse properly" do
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
tree = SgfParser::Tree.new :filename => 'sample_sgf/ff4_ex.sgf'
|
11
|
+
tree.class.should == SgfParser::Tree
|
12
|
+
tree.root.children.size.should == 2
|
13
|
+
tree.root.children[0].children.size.should == 5
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should save a simple tree properly" do
|
17
|
+
tree = SgfParser::Tree.new :filename => 'sample_sgf/simple.sgf'
|
18
|
+
new = 'sample_sgf/simple_saved.sgf'
|
19
|
+
tree.save :filename => new
|
20
|
+
tree2 = SgfParser::Tree.new :filename => new
|
21
|
+
tree.should == tree2
|
13
22
|
end
|
14
23
|
|
15
|
-
it "should save properly" do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
it "should save the sample SGF properly" do
|
25
|
+
tree = SgfParser::Tree.new :filename => 'sample_sgf/ff4_ex.sgf'
|
26
|
+
new = 'sample_sgf/ff4_ex_saved.sgf'
|
27
|
+
tree.save :filename => new
|
28
|
+
tree2 = SgfParser::Tree.new :filename => new
|
29
|
+
tree_children = []
|
30
|
+
tree2_children = []
|
31
|
+
tree.each { |node| tree_children << node }
|
32
|
+
tree2.each { |node| tree2_children << node }
|
33
|
+
tree_children.size.should == tree2_children.size
|
34
|
+
tree_children.each_with_index do |node, index|
|
35
|
+
node.properties.should == tree2_children[index].properties
|
36
|
+
end
|
37
|
+
tree_children.should == tree2_children
|
38
|
+
tree2.should == tree
|
20
39
|
end
|
21
40
|
|
22
41
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: SgfParser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 57
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 9
|
8
|
-
-
|
9
|
-
version: 0.9.
|
9
|
+
- 1
|
10
|
+
version: 0.9.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Aldric Giacomoni
|
@@ -14,23 +15,53 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-07-29 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
-
prerelease: false
|
22
|
+
type: :development
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
name: jeweler
|
33
|
+
version_requirements: *id001
|
34
|
+
prerelease: false
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
type: :development
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
24
39
|
requirements:
|
25
40
|
- - ">="
|
26
41
|
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
name: rcov
|
47
|
+
version_requirements: *id002
|
48
|
+
prerelease: false
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
type: :development
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 13
|
27
57
|
segments:
|
28
58
|
- 1
|
29
59
|
- 2
|
30
60
|
- 9
|
31
61
|
version: 1.2.9
|
32
|
-
|
33
|
-
version_requirements: *
|
62
|
+
name: rspec
|
63
|
+
version_requirements: *id003
|
64
|
+
prerelease: false
|
34
65
|
description: SGFParser is a library that parses and saves SGF (Smart Game Format) files.
|
35
66
|
email: aldric@trevoke.net
|
36
67
|
executables: []
|
@@ -42,7 +73,10 @@ extra_rdoc_files:
|
|
42
73
|
- README.rdoc
|
43
74
|
files:
|
44
75
|
- .document
|
45
|
-
- .
|
76
|
+
- .rspec
|
77
|
+
- .rvmrc
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
46
80
|
- LICENSE
|
47
81
|
- README.rdoc
|
48
82
|
- Rakefile
|
@@ -53,47 +87,51 @@ files:
|
|
53
87
|
- lib/sgf/parser/tree.rb
|
54
88
|
- lib/sgf/parser/tree_parse.rb
|
55
89
|
- lib/sgf/sgf_indent.rb
|
90
|
+
- lib/sgf/sgfindent.rb
|
56
91
|
- lib/sgf_parser.rb
|
57
92
|
- sample_sgf/ff4_ex.sgf
|
93
|
+
- sample_sgf/ff4_ex_saved.sgf
|
58
94
|
- sample_sgf/redrose-tartrate.sgf
|
95
|
+
- sample_sgf/simple.sgf
|
96
|
+
- sample_sgf/simple_saved.sgf
|
59
97
|
- sample_usage/parsing_files.rb
|
60
98
|
- spec/node_spec.rb
|
61
|
-
- spec/spec.opts
|
62
99
|
- spec/spec_helper.rb
|
100
|
+
- spec/tree_parser_spec.rb
|
63
101
|
- spec/tree_spec.rb
|
64
|
-
- tryme.rb
|
65
102
|
has_rdoc: true
|
66
103
|
homepage: http://github.com/Trevoke/SGFParser
|
67
104
|
licenses: []
|
68
105
|
|
69
106
|
post_install_message:
|
70
|
-
rdoc_options:
|
71
|
-
|
107
|
+
rdoc_options: []
|
108
|
+
|
72
109
|
require_paths:
|
73
110
|
- lib
|
74
111
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
75
113
|
requirements:
|
76
114
|
- - ">="
|
77
115
|
- !ruby/object:Gem::Version
|
116
|
+
hash: 3
|
78
117
|
segments:
|
79
118
|
- 0
|
80
119
|
version: "0"
|
81
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
82
122
|
requirements:
|
83
123
|
- - ">="
|
84
124
|
- !ruby/object:Gem::Version
|
125
|
+
hash: 3
|
85
126
|
segments:
|
86
127
|
- 0
|
87
128
|
version: "0"
|
88
129
|
requirements: []
|
89
130
|
|
90
131
|
rubyforge_project:
|
91
|
-
rubygems_version: 1.
|
132
|
+
rubygems_version: 1.6.2
|
92
133
|
signing_key:
|
93
134
|
specification_version: 3
|
94
135
|
summary: A library for working with SGF files.
|
95
|
-
test_files:
|
96
|
-
|
97
|
-
- spec/spec_helper.rb
|
98
|
-
- spec/tree_parser_spec.rb
|
99
|
-
- spec/tree_spec.rb
|
136
|
+
test_files: []
|
137
|
+
|
data/.gitignore
DELETED
data/spec/spec.opts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--color
|
data/tryme.rb
DELETED