theoros-rubysgf 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+