sgf_parser 0.1.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.
Files changed (53) hide show
  1. data/.irbrc +3 -0
  2. data/.rvmrc +4 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +17 -0
  5. data/LICENSE +20 -0
  6. data/README.textile +27 -0
  7. data/Rakefile +20 -0
  8. data/TODO +0 -0
  9. data/VERSION +1 -0
  10. data/bin/sgf +28 -0
  11. data/bin/stm2dot +18 -0
  12. data/doc/sgf_state_machine.dot +58 -0
  13. data/doc/sgf_state_machine.svg +269 -0
  14. data/lib/sgf.rb +15 -0
  15. data/lib/sgf/binary_file_error.rb +4 -0
  16. data/lib/sgf/debugger.rb +15 -0
  17. data/lib/sgf/default_event_listener.rb +37 -0
  18. data/lib/sgf/model/constants.rb +19 -0
  19. data/lib/sgf/model/event_listener.rb +72 -0
  20. data/lib/sgf/model/game.rb +52 -0
  21. data/lib/sgf/model/label.rb +12 -0
  22. data/lib/sgf/model/node.rb +115 -0
  23. data/lib/sgf/model/property_handler.rb +51 -0
  24. data/lib/sgf/more/state_machine_presenter.rb +43 -0
  25. data/lib/sgf/more/stm_dot_converter.rb +139 -0
  26. data/lib/sgf/parse_error.rb +25 -0
  27. data/lib/sgf/parser.rb +56 -0
  28. data/lib/sgf/renderer.rb +25 -0
  29. data/lib/sgf/sgf_helper.rb +55 -0
  30. data/lib/sgf/sgf_state_machine.rb +174 -0
  31. data/lib/sgf/state_machine.rb +76 -0
  32. data/sgf_parser.gemspec +95 -0
  33. data/spec/fixtures/2009-11-01-1.sgf +24 -0
  34. data/spec/fixtures/2009-11-01-2.sgf +23 -0
  35. data/spec/fixtures/chinese_gb.sgf +9 -0
  36. data/spec/fixtures/chinese_utf.sgf +9 -0
  37. data/spec/fixtures/example.sgf +18 -0
  38. data/spec/fixtures/good.sgf +55 -0
  39. data/spec/fixtures/good1.sgf +167 -0
  40. data/spec/fixtures/kgs.sgf +723 -0
  41. data/spec/fixtures/test.png +0 -0
  42. data/spec/sgf/model/event_listener_spec.rb +97 -0
  43. data/spec/sgf/model/game_spec.rb +29 -0
  44. data/spec/sgf/model/node_spec.rb +84 -0
  45. data/spec/sgf/more/state_machine_presenter_spec.rb +29 -0
  46. data/spec/sgf/parse_error_spec.rb +10 -0
  47. data/spec/sgf/parser_spec.rb +210 -0
  48. data/spec/sgf/sgf_helper_spec.rb +68 -0
  49. data/spec/sgf/sgf_state_machine_spec.rb +166 -0
  50. data/spec/sgf/state_machine_spec.rb +137 -0
  51. data/spec/spec.opts +4 -0
  52. data/spec/spec_helper.rb +47 -0
  53. metadata +150 -0
data/lib/sgf.rb ADDED
@@ -0,0 +1,15 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/debugger')
2
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/state_machine')
3
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/sgf_state_machine')
4
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/default_event_listener')
5
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/parser')
6
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/parse_error')
7
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/binary_file_error')
8
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/sgf_helper')
9
+
10
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/constants')
11
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/property_handler')
12
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/event_listener')
13
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/game')
14
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/node')
15
+ require File.expand_path(File.dirname(__FILE__) + '/sgf/model/label')
@@ -0,0 +1,4 @@
1
+ module SGF
2
+ class BinaryFileError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,15 @@
1
+ module SGF
2
+ module Debugger
3
+ def enable_debug_mode
4
+ @debug_mode = true
5
+ end
6
+
7
+ def disable_debug_mode
8
+ @debug_mode = false
9
+ end
10
+
11
+ def debug message
12
+ puts message if @debug_mode
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ module SGF
2
+ class DefaultEventListener
3
+ include Debugger
4
+
5
+ def initialize debug_mode = false
6
+ enable_debug_mode if debug_mode
7
+ end
8
+
9
+ def start_game
10
+ debug 'start_game'
11
+ end
12
+
13
+ def start_node
14
+ debug 'start_node'
15
+ end
16
+
17
+ def property_name= name
18
+ debug "property_name = '#{name}'"
19
+ end
20
+
21
+ def property_value= value
22
+ debug "property_value = '#{value}'"
23
+ end
24
+
25
+ def start_variation
26
+ debug "start_variation"
27
+ end
28
+
29
+ def end_variation
30
+ debug "end_variation"
31
+ end
32
+
33
+ def end_game
34
+ debug "end_game"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ module SGF
2
+ module Model
3
+ module Constants
4
+ WEIQI = 1
5
+
6
+ DEFAULT_BOARD_SIZE = 19
7
+ DEFAULT_KOMI = 7.5
8
+
9
+ BLACK = 1
10
+ WHITE = 2
11
+
12
+ NODE_SETUP = 0
13
+ NODE_MOVE = 1
14
+ NODE_PASS = 2
15
+
16
+ POSITIONS = "abcdefghijklmnopqrs"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ module SGF
2
+ module Model
3
+ class EventListener < SGF::DefaultEventListener
4
+ include SGF::SGFHelper
5
+
6
+ attr_reader :game, :node
7
+
8
+ def initialize debug_mode = false
9
+ super(debug_mode)
10
+ end
11
+
12
+ def start_game
13
+ super
14
+
15
+ @game = Game.new
16
+ end
17
+
18
+ def start_variation
19
+ super
20
+
21
+ @node = Node.new(@node)
22
+ @node.variation_root = true
23
+ end
24
+
25
+ def end_variation
26
+ super
27
+
28
+ @node = find_variation_root(@node).parent
29
+ end
30
+
31
+ def start_node
32
+ super
33
+
34
+ @node = @node.nil? ? game.root_node : Node.new(@node)
35
+ end
36
+
37
+ def property_name= name
38
+ super name
39
+
40
+ @property_name = name
41
+ end
42
+
43
+ def property_value= value
44
+ super value
45
+
46
+ set_property @property_name, value
47
+ end
48
+
49
+ private
50
+
51
+ def find_variation_root node
52
+ while not node.variation_root?
53
+ return node if node.parent.nil?
54
+
55
+ node = node.parent
56
+ end
57
+ node
58
+ end
59
+
60
+ def set_property name, value
61
+ return unless name
62
+ name = name.strip.upcase
63
+ value.strip! unless value.nil?
64
+
65
+ return if GAME_PROPERTY_HANDLER.handle(game, name, value)
66
+ return if NODE_PROPERTY_HANDLER.handle(node, name, value)
67
+
68
+ puts "WARNING: SGF property is not recognized(name=#{name}, value=#{value})"
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,52 @@
1
+ module SGF
2
+ module Model
3
+ class Game
4
+ include Constants
5
+ include SGF::SGFHelper
6
+
7
+ attr_accessor :game_type, :name, :rule, :board_size, :handicap, :komi,
8
+ :black_player, :black_rank, :white_player, :white_rank,
9
+ :black_team, :white_team,
10
+ :time_rule, :overtime_rule, :result,
11
+ :played_on, :program, :place, :event, :round, :source,
12
+ :annotation, :comment
13
+
14
+ def initialize
15
+ @game_type = WEIQI
16
+ @board_size = DEFAULT_BOARD_SIZE
17
+ @handicap = 0
18
+ @komi = DEFAULT_KOMI
19
+ end
20
+
21
+ def misc_properties
22
+ @misc_properties ||= {}
23
+ end
24
+
25
+ def game_type=(value); @game_type = value.to_i; end
26
+ def board_size=(value); @board_size = value.to_i; end
27
+ def handicap=(value); @handicap = value.to_i; end
28
+ def komi=(value); @komi = value.to_f; end
29
+
30
+ def root_node
31
+ @root_node ||= Node.new(nil)
32
+ end
33
+
34
+ def time_rule
35
+ @time_rule.to_s + (overtime_rule ? " (#{overtime_rule})" : "")
36
+ end
37
+
38
+ def moves
39
+ moves = 0
40
+
41
+ node = root_node
42
+ while node
43
+ moves = node.move_no
44
+ node = node.children[0]
45
+ end
46
+
47
+ moves
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ class SGF::Model::Label
2
+ attr_accessor :position, :text
3
+
4
+ def initialize position, text
5
+ self.position = position
6
+ self.text = text
7
+ end
8
+
9
+ def == other
10
+ self.position == other.position and self.text == other.text
11
+ end
12
+ end
@@ -0,0 +1,115 @@
1
+ module SGF
2
+ module Model
3
+ class Node
4
+ include Constants
5
+ include SGF::SGFHelper
6
+
7
+ attr_reader :node_type, :color, :move, :black_moves, :white_moves, :labels
8
+ attr_reader :parent, :children
9
+ attr_accessor :comment, :whose_turn
10
+
11
+ def initialize parent = nil
12
+ @parent = parent
13
+ @node_type = NODE_SETUP
14
+ @labels = []
15
+ @whose_turn = BLACK
16
+ @trunk = true
17
+ if parent
18
+ @trunk = parent.trunk?
19
+ @parent.children << self
20
+ end
21
+ end
22
+
23
+ def move_no
24
+ parent_move_no = parent.nil? ? 0 : parent.move_no
25
+ self.node_type == NODE_MOVE ? parent_move_no + 1 : parent_move_no
26
+ end
27
+
28
+ def misc_properties
29
+ @misc_properties ||= {}
30
+ end
31
+
32
+ def whose_turn= input
33
+ @whose_turn = input.to_i
34
+ end
35
+
36
+ def black_moves
37
+ @black_moves ||= []
38
+ end
39
+
40
+ def white_moves
41
+ @white_moves ||= []
42
+ end
43
+
44
+ def clear_moves
45
+ @white_moves ||= []
46
+ end
47
+
48
+ def children
49
+ @children ||= []
50
+ end
51
+
52
+ def child
53
+ @children.first
54
+ end
55
+
56
+ def root?
57
+ @parent.nil?
58
+ end
59
+
60
+ def trunk?
61
+ @trunk
62
+ end
63
+
64
+ def last?
65
+ @children.nil? or @children.empty?
66
+ end
67
+
68
+ def variation_root= value
69
+ @variation_root = value
70
+ @trunk = false if value
71
+ end
72
+
73
+ def variation_root?
74
+ @variation_root
75
+ end
76
+
77
+ def sgf_setup_black input
78
+ to_position_array(input).each {|position| self.black_moves << position}
79
+ end
80
+
81
+ def sgf_setup_white input
82
+ to_position_array(input).each {|position| self.white_moves << position}
83
+ end
84
+
85
+ def sgf_setup_clear input
86
+ to_position_array(input).each {|position| self.clear_moves << position}
87
+ end
88
+
89
+ def sgf_play_black input
90
+ @color = BLACK
91
+ set_move input
92
+ end
93
+
94
+ def sgf_play_white input
95
+ @color = WHITE
96
+ set_move input
97
+ end
98
+
99
+ def sgf_label input
100
+ @labels << to_label(input)
101
+ end
102
+
103
+ private
104
+
105
+ def set_move input
106
+ if input.nil? or input.strip.size == 0
107
+ @node_type = NODE_PASS
108
+ else
109
+ @node_type = NODE_MOVE
110
+ @move = to_position(input)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,51 @@
1
+ module SGF
2
+ module Model
3
+ class PropertyHandler
4
+ def initialize method_mappings, misc_properties
5
+ @method_mappings = method_mappings
6
+ @misc_properties = misc_properties
7
+ end
8
+
9
+ def handle model, name, value
10
+ if @method_mappings.include?(name)
11
+ model.send(@method_mappings[name], value)
12
+
13
+ true
14
+ elsif @misc_properties.include?(name)
15
+ if model.misc_properties[name]
16
+ model.misc_properties[name] = [model.misc_properties[name], value].flatten
17
+ else
18
+ model.misc_properties[name] = value
19
+ end
20
+
21
+ true
22
+ end
23
+ end
24
+ end
25
+
26
+ # See http://www.red-bean.com/sgf/properties.html for property definitions
27
+ GAME_PROPERTY_MAPPINGS = {
28
+ 'GM' => :game_type=, 'GN' => :name=, 'RU' => :rule=, 'SZ' => :board_size=, 'HA' => :handicap=, 'KM' => :komi=,
29
+ 'PW' => :white_player=, 'BR' => :black_rank=, 'PB' => :black_player=, 'WR' => :white_rank=,
30
+ 'BT' => :black_team=, 'WT' => :white_team=,
31
+ 'DT' => :played_on=, 'TM' => :time_rule=, 'OT' => :overtime_rule=,
32
+ 'SY' => :program=, 'RE' => :result=, 'AP' => :program=, 'PC' => :place=, 'EV' => :event=, 'RO' => :round=,
33
+ 'SO' => :source=, 'AN' => :annotation=, 'GC' => :comment=
34
+ }
35
+
36
+ GAME_MISC_PROPERTIES = %w(FF US CA ST)
37
+
38
+ NODE_PROPERTY_MAPPINGS = {
39
+ "B" => :sgf_play_black, "W" => :sgf_play_white, "C" => :comment=,
40
+ "AB" => :sgf_setup_black, "AW" => :sgf_setup_white, "AE" => :sgf_setup_clear, "LB" => :sgf_label,
41
+ "PL" => :whose_turn=
42
+ }
43
+
44
+ NODE_MARK_PROPERTIES = %w(AR CR DD LB LN MA SL SQ TR)
45
+ NODE_TIME_PROPERTIES = %w(BL WL OB OW TB TW)
46
+ NODE_MISC_PROPERTIES = %w(N GW GB DM UC TE BM DO IT MN) + NODE_MARK_PROPERTIES + NODE_TIME_PROPERTIES
47
+
48
+ GAME_PROPERTY_HANDLER = PropertyHandler.new(GAME_PROPERTY_MAPPINGS, GAME_MISC_PROPERTIES)
49
+ NODE_PROPERTY_HANDLER = PropertyHandler.new(NODE_PROPERTY_MAPPINGS, NODE_MISC_PROPERTIES)
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ # This class is created for illustrative purpose only. It should not be used as
2
+ # the base class for state machine visualizer
3
+
4
+ module SGF
5
+ module More
6
+ class StateMachinePresenter
7
+ def nodes
8
+ nodes_hash.values
9
+ end
10
+
11
+ def edges
12
+ @edges ||= []
13
+ end
14
+
15
+ def process state_machine
16
+ nodes_hash[state_machine.start_state.to_s] = create_node(state_machine.start_state.to_s)
17
+ state_machine.transitions.each do |from_state, transitions|
18
+ transitions.each do |transition|
19
+ from_node = nodes_hash[from_state.to_s] ||= create_node(from_state.to_s)
20
+ to_state = transition.after_state || from_state
21
+ to_node = nodes_hash[to_state.to_s] ||= create_node(to_state.to_s)
22
+ transition_desc = transition.description || transition.event_pattern.inspect
23
+ edges << create_edge(from_node, to_node, transition_desc)
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def nodes_hash
31
+ @nodes ||= {}
32
+ end
33
+
34
+ def create_node name
35
+ name
36
+ end
37
+
38
+ def create_edge from_node, to_node, description
39
+ [from_node, to_node, description]
40
+ end
41
+ end
42
+ end
43
+ end