theoros-rubysgf 0.0.2

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/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Adam Prescott <disaffect@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/lib/rubysgf.rb ADDED
@@ -0,0 +1,76 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ $:.unshift(File.dirname(__FILE__)) unless
7
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
8
+
9
+ require "strscan"
10
+ require "sgf/errors"
11
+ require "sgf/coordinates"
12
+ require "sgf/compressed_list"
13
+ require "sgf/property"
14
+ require "sgf/node"
15
+ require "sgf/record"
16
+
17
+ module RubySGF
18
+ VERSION = "0.0.2"
19
+
20
+ module SGF # Go-specific under FF[4]
21
+
22
+ ALL_PROPERTY_NAMES = %w{ B KO MN W AB AE AW PL C DM GB GW HO N UC V BM DO IT TE AR CR DD LB LN MA SL SQ TR AP CA FF GM ST SZ AN BR BT CP DT EV GN GC ON OT PB PC PW RE RO RU SO TM US WR WT BL OB OW WL FG PM VW HA KM TB TW }
23
+
24
+ # properties which have values which are coordinates and implemented by this program
25
+ COORDINATE_PROPERTIES = %w{ B W AB AW AE CR MA SQ TR TW TB }
26
+
27
+ # the properties going to be implemented by this program
28
+ PROPERTY_NAMES_HASH = {
29
+ :black_move => "B",
30
+ :white_move => "W",
31
+ :black_stones => "AB",
32
+ :white_stones => "AW",
33
+ :neutral_points => "AE",
34
+ :comment => "C",
35
+ :node_name => "N",
36
+ :circled_points => "CR",
37
+ :label => "LB",
38
+ :marked_x => "MA",
39
+ :squared_points => "SQ",
40
+ :triangled_points => "TR",
41
+ :application => "AP",
42
+ :character_set => "CA",
43
+ :file_format => "FF",
44
+ :game_type => "GM",
45
+ :board_size => "SZ",
46
+ :annotator => "AN",
47
+ :black_rank => "BR",
48
+ :copyright_info => "CP",
49
+ :game_date => "DT",
50
+ :game_name => "GN",
51
+ :overtime => "OT",
52
+ :black_player => "PB",
53
+ :game_location => "PC",
54
+ :white_player => "PW",
55
+ :game_result => "RE",
56
+ :ruleset => "RU",
57
+ :source => "SO",
58
+ :time_limits => "TM",
59
+ :white_rank => "WR",
60
+ :black_time_left => "BL",
61
+ :black_overtime => "OB",
62
+ :white_time_left => "WL",
63
+ :white_overtime => "OW",
64
+ :handicap => "HA",
65
+ :komi => "KM",
66
+ :white_territory => "TW",
67
+ :black_territory => "TB",
68
+ :markup_style => "ST"
69
+ }
70
+
71
+ # implemented by this program
72
+ PROPERTY_NAMES = PROPERTY_NAMES_HASH.values
73
+
74
+ NOT_IMPLEMENTED_PROPERTY_NAMES = ALL_PROPERTY_NAMES - PROPERTY_NAMES
75
+ end # SGF
76
+ end # RSGF
@@ -0,0 +1,30 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ class CompressedList
9
+ attr_reader :ulx, :uly, :lrx, :lry
10
+ def initialize(upper_left_x, upper_left_y, lower_right_x, lower_right_y)
11
+ @ulx, @uly, @lrx, @lry = upper_left_x, upper_left_y, lower_right_x, lower_right_y
12
+ end
13
+
14
+ # expand compressed list to array
15
+ def to_a
16
+ smallx, bigx = [ulx, lrx].sort.map { |i| Coordinates::HORIZONTAL.index(i) }
17
+ xgap = bigx - smallx
18
+ smally, bigy = [uly, lry].sort.map { |i| Coordinates::VERTICAL.index(i) }
19
+ ygap = bigy - smally
20
+ coord_list = []
21
+ Coordinates::VERTICAL[smally..bigy].each do |y|
22
+ Coordinates::HORIZONTAL[smallx..bigx].each do |x|
23
+ coord_list << [x,y]
24
+ end
25
+ end
26
+ coord_list.sort
27
+ end
28
+ end # compressedlist
29
+ end # sgf
30
+ end # rsgf
@@ -0,0 +1,74 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ class Coordinates
9
+ VERTICAL = HORIZONTAL = [*"a".."z"] + [*"A".."Z"]
10
+
11
+ attr_reader :x, :y
12
+ def initialize(x, y)
13
+ @x = x
14
+ @y = y
15
+ end
16
+
17
+ def ==(second_coordinates)
18
+ @x == second_coordinates.x && @y == second_coordinates.y
19
+ end
20
+
21
+ def neighbours(size)
22
+ [up, right, down, left].compact.map do |c|
23
+ c.inside?(size) ? c : nil
24
+ end
25
+ end
26
+
27
+ def inside?(size)
28
+ vert = VERTICAL[0,size]
29
+ horiz = HORIZONTAL[0,size]
30
+ horiz.include?(@x) && vert.include?(@y)
31
+ end
32
+
33
+ def outside?(size)
34
+ !inside?(size)
35
+ end
36
+
37
+ def up
38
+ idx = VERTICAL.index(y) - 1
39
+ return nil unless idx >= 0
40
+ new_y = VERTICAL[idx]
41
+ return nil unless new_y
42
+ Coordinates.new(x, new_y)
43
+ end
44
+
45
+ def right
46
+ idx = HORIZONTAL.index(x) + 1
47
+ return nil unless idx >= 0
48
+ new_x = HORIZONTAL[idx]
49
+ return nil unless new_x
50
+ Coordinates.new(new_x, y)
51
+ end
52
+
53
+ def down
54
+ idx = VERTICAL.index(y) + 1
55
+ return nil unless idx >= 0
56
+ new_y = VERTICAL[idx]
57
+ return nil unless new_y
58
+ Coordinates.new(x, new_y)
59
+ end
60
+
61
+ def left
62
+ idx = HORIZONTAL.index(x) - 1
63
+ return nil unless idx >= 0
64
+ new_x = HORIZONTAL[idx]
65
+ return nil unless new_x
66
+ Coordinates.new(new_x, y)
67
+ end
68
+
69
+ def to_s
70
+ @x + @y
71
+ end
72
+ end # coordinates
73
+ end # SGF
74
+ end # RSGF
data/lib/sgf/errors.rb ADDED
@@ -0,0 +1,14 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ class SGFError < StandardError
9
+ end
10
+
11
+ class PropertyError < SGFError
12
+ end
13
+ end # SGF
14
+ end # RSGF
data/lib/sgf/node.rb ADDED
@@ -0,0 +1,68 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ #--
9
+ # this class is partially inspired by code written by
10
+ # Stefan Rusterholz, stefan.rusterholz <AT> gmail <DOT> com
11
+ #++
12
+ class Node
13
+
14
+ attr_reader :parent, :children, :number
15
+ attr_accessor :properties
16
+
17
+ def initialize(parent, properties = [])
18
+ @parent = parent
19
+ @properties = properties
20
+ @children = []
21
+
22
+ @parent << self if parent
23
+
24
+ if parent
25
+ @number = parent.number + 1
26
+ else
27
+ @number = 0
28
+ end
29
+ end
30
+
31
+ def prev
32
+ @parent
33
+ end
34
+
35
+ def next
36
+ @children[0]
37
+ end
38
+
39
+ def [](index)
40
+ @children[index]
41
+ end
42
+
43
+ def <<(node)
44
+ @children << node
45
+ end
46
+
47
+ def leaf?
48
+ @children.empty?
49
+ end
50
+
51
+ def root?
52
+ @parent.nil?
53
+ end
54
+
55
+ def to_s
56
+ " "*@number << @number.to_s
57
+ end
58
+
59
+ def write_tree(string="")
60
+ string << self.to_s << "\n"
61
+ @children.each do |child|
62
+ child.write_tree(string)
63
+ end
64
+ string
65
+ end # write_tree
66
+ end # class
67
+ end # sgf
68
+ end # rsgf
@@ -0,0 +1,102 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ class Property
9
+
10
+ DEFAULTS = {"AP" => "rGo:0.1a", "GM" => "1", "FF" => "4", "SZ" => "19", "PW" => "White", "PB" => "Black"}
11
+
12
+ attr_reader :name, :value
13
+
14
+ # /(PN\[((?:[^\]\\]+|\\[\]\[]?)*?)\])/ --> /(PN\[((?:[^\]\\]+|\\[\]\[]?)*?)\])+/
15
+ # allows lists to be taken into account [aa][ev][gg]
16
+ # returns PN[everything here including escaped \] like this]
17
+ MATCH = /(\[((?:[^\]\\]+|\\[\]\[]?)*?)\])/
18
+ MATCH_MANY = /(\[((?:[^\]\\]+|\\[\]\[]?)*?)\])+/
19
+
20
+
21
+ def initialize(name, value)
22
+ @name = name
23
+ @value = value
24
+ end
25
+
26
+ def human_name
27
+ Property.human_name_of(@name)
28
+ end
29
+
30
+ class << self
31
+
32
+ def create_many_from_raw(text, properties)
33
+ properties.map! do |pn|
34
+ Property.create_from_raw(text,pn)
35
+ end
36
+ end
37
+
38
+ def create_from_raw(text, property_name)
39
+ prop_value = Property.parse_sgf(text, property_name) # Array, may be size 1 or more
40
+ prop_value.map! do |v|
41
+ if COORDINATE_PROPERTIES.include? property_name
42
+ Coordinates.new(v[0,1], v[1,1])
43
+ elsif property_name == "LB"
44
+ v = v.split(":")
45
+ v[0] = Coordinates.new(v[0][0,1], v[0][1,1])
46
+ v
47
+ else
48
+ v
49
+ end
50
+ end
51
+ new(property_name, prop_value)
52
+ end
53
+
54
+ # take a text of properties (NAME[VALUE], or otherwise) and return the value(s)
55
+ def parse_sgf(input, property_name)
56
+ $stderr << "invalid property detected (will be ignored): #{property_name} -- \"#{input}\"\n" unless Property.valid?(property_name)
57
+ match_many = MATCH_MANY.source
58
+ match = MATCH.source
59
+ output = ""
60
+ case property_name
61
+ when "C"
62
+ output = input[/#{property_name}#{match}/m][2..-2]
63
+ when "GN"
64
+ output = input[/#{property_name}#{match}/m][3..-2]
65
+ else
66
+ output = input.gsub("\n","")[/#{property_name}#{match_many}/][2..-1].scan(/[^\[\]]+/)
67
+ end
68
+ [*output]
69
+ end
70
+
71
+ def valid?(name)
72
+ ALL_PROPERTY_NAMES.include?(name)
73
+ end
74
+
75
+ def compressed_list?(val)
76
+ val =~ /^[a-z][a-z]\:[a-z][a-z]$/i
77
+ end
78
+
79
+ def expand_list(val)
80
+ ulx, uly, lrx, lry = val.scan(/^([a-z])([a-z])\:([a-z])([a-z])$/i).flatten
81
+ CompressedList.new(ulx, uly, lrx, lry).to_a
82
+ end
83
+
84
+ def can_be_nil?(property_name)
85
+ ["W","B"].include?(property_name)
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ class << self
92
+ def human_name_of(name)
93
+ PROPERTY_NAMES_HASH.invert[name]
94
+ end
95
+
96
+ def sgf_name_of(sym)
97
+ PROPERTY_NAMES_HASH[sym]
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
data/lib/sgf/record.rb ADDED
@@ -0,0 +1,66 @@
1
+ #--
2
+ # Copyright (c) 2008 Adam Prescott
3
+ # licensed as specified under LICENSE.txt
4
+ #++
5
+
6
+ module RubySGF
7
+ module SGF
8
+ class Record
9
+ attr_reader :data, :root, :info
10
+
11
+ def initialize(data = nil)
12
+ @data = data
13
+ if data
14
+ @root = Record.create_tree(@data)
15
+ else
16
+ n = Node.new(nil)
17
+ n.properties = Property::DEFAULTS.map do |name, value|
18
+ Property.new(name, [value])
19
+ end
20
+ @root = n
21
+ end
22
+ end
23
+
24
+ def to_s
25
+ @root.write_tree
26
+ end
27
+
28
+ private
29
+
30
+ class << self
31
+ def create_tree(str)
32
+ str = StringScanner.new(str)
33
+ root_node = node = nil
34
+ old_nodes = []
35
+ until str.eos?
36
+ case s = str.getch
37
+ when "("
38
+ old_nodes << node
39
+ node = Node.new(node)
40
+ root_node = node if node.root?
41
+ when ";"
42
+ # pass along all of the properties of the current node to Node for creation
43
+ # we ignore ; ( ) chars inside property values
44
+ node_text = str.scan(/[A-Z]+\[.*?\]\s*[;\(\)]/m).strip[0..-2]
45
+ # look for all property names
46
+ property_names = node_text.scan(/((?:[A-Z]+)(?:[^\\]|))\[/).flatten
47
+ $stderr << "Error around \"#{str.matched}\": multiple properties of the same type -- all but the first instance will be ignored\n" if property_names.uniq!
48
+ properties = Property.create_many_from_raw(node_text, property_names)
49
+ if node.properties.empty? # every node must have a property
50
+ node.properties = properties
51
+ else
52
+ node = Node.new(node, properties)
53
+ end
54
+ str.pos -= 1 # move back before the ; or ( or )
55
+ when ")"
56
+ node = old_nodes.pop
57
+ else
58
+ next
59
+ end # case
60
+ end # until
61
+ root_node
62
+ end # create_tree
63
+ end # singleton
64
+ end # record
65
+ end # SGF
66
+ end # RSGF
data/rubysgf.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rubysgf"
3
+ s.version = "0.0.2"
4
+ s.date = "2008-08-22"
5
+ s.summary = "SGF file reader for Ruby"
6
+ s.email = "disaffect@gmail.com"
7
+ s.homepage = "http://github.com/theoros/rubysgf"
8
+ s.description = "A Ruby library to read SGF files and create objects out of them."
9
+ s.required_ruby_version = "~> 1.8.6"
10
+ s.has_rdoc = false
11
+ s.authors = ["Adam Prescott"]
12
+ s.files = ["LICENSE.txt",
13
+ "rubysgf.gemspec",
14
+ "lib/rubysgf.rb",
15
+ "lib/sgf/compressed_list.rb",
16
+ "lib/sgf/coordinates.rb",
17
+ "lib/sgf/errors.rb",
18
+ "lib/sgf/node.rb",
19
+ "lib/sgf/property.rb",
20
+ "lib/sgf/record.rb",
21
+ ]
22
+ s.rdoc_options = []#["--main", "README.txt"]
23
+ s.extra_rdoc_files = []#["History.txt", "Manifest.txt", "README.txt"]
24
+ #s.add_dependency("mime-types", ["> 0.0.0"])
25
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: theoros-rubysgf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Adam Prescott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A Ruby library to read SGF files and create objects out of them.
17
+ email: disaffect@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - LICENSE.txt
26
+ - rubysgf.gemspec
27
+ - lib/rubysgf.rb
28
+ - lib/sgf/compressed_list.rb
29
+ - lib/sgf/coordinates.rb
30
+ - lib/sgf/errors.rb
31
+ - lib/sgf/node.rb
32
+ - lib/sgf/property.rb
33
+ - lib/sgf/record.rb
34
+ has_rdoc: false
35
+ homepage: http://github.com/theoros/rubysgf
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.8.6
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.2.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: SGF file reader for Ruby
60
+ test_files: []
61
+