chess_openings 0.0.3

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,40 @@
1
+ class Opening
2
+
3
+ attr_accessor :name, :eco_code, :moves
4
+
5
+ def initialize(name, eco_code, moves)
6
+ @name = name
7
+ @eco_code = eco_code
8
+ @moves = moves.map! { |move| move.is_a?(String)? move.to_sym : move }
9
+ end
10
+
11
+ def to_s
12
+ "#{@eco_code}: #{@name}\n#{@moves}"
13
+ end
14
+
15
+ def to_h
16
+ { "name" => @name, "eco_code" => @eco_code, "moves" => @moves }
17
+ end
18
+
19
+ def ==(other)
20
+ @name == other.name && @eco_code == other.eco_code && @moves == other.moves
21
+ end
22
+
23
+ def to_pgn
24
+ result = ""
25
+ index = 1
26
+ @moves.each do |move|
27
+
28
+ if index.odd?
29
+ result += "#{(index / 2.0).ceil}. #{move}"
30
+ elsif index.even? && @moves.size != index
31
+ result += " #{move} "
32
+ else
33
+ result += " #{move}"
34
+ end
35
+
36
+ index += 1
37
+ end
38
+ return result
39
+ end
40
+ end
@@ -0,0 +1,163 @@
1
+ class SearchTree
2
+
3
+ attr_accessor :root
4
+
5
+ def initialize
6
+ @root = Node.new(nil)
7
+ end
8
+
9
+ def empty?
10
+ @root.is_leaf?
11
+ end
12
+
13
+ def size
14
+ size_helper(@root)
15
+ end
16
+
17
+ def to_s
18
+ @root.to_s
19
+ end
20
+
21
+ def ==(other)
22
+ return false unless @root.size == other.root.size
23
+
24
+ @root.keys.each do |key|
25
+ return false unless other.root.keys.include?(key)
26
+ end
27
+
28
+ @root.keys.each do |key|
29
+ return false unless @root.nodes[key] == other.root.nodes[key]
30
+ end
31
+
32
+ true
33
+ end
34
+
35
+ def insert(moves, value)
36
+ moves = moves_in_symbols(moves)
37
+ insert_helper(moves, value, @root)
38
+ end
39
+
40
+ def search(moves)
41
+ moves = moves_in_symbols(moves)
42
+ search_helper(moves, @root)
43
+ end
44
+
45
+ def search_all_with_moves(moves)
46
+ moves = moves_in_symbols(moves)
47
+ node = find_node(moves, @root)
48
+ get_all_from_node(node).flatten
49
+ end
50
+
51
+
52
+ private
53
+
54
+ def moves_in_symbols(moves)
55
+ moves.map! { |move| move.is_a?(String)? move.to_sym : move }
56
+ end
57
+
58
+ def find_node(moves, curr_node)
59
+ return curr_node if moves.empty?
60
+
61
+ curr_hash = curr_node.nodes
62
+ move = moves.first
63
+ return nil if curr_hash[move].nil?
64
+
65
+ next_node = curr_hash[move]
66
+ find_node(moves.drop(1), next_node)
67
+ end
68
+
69
+ def get_all_from_node(curr_node)
70
+
71
+ result = curr_node.value.nil? ? [] : [curr_node.value]
72
+ return result if curr_node.is_leaf?
73
+
74
+ curr_hash = curr_node.nodes
75
+
76
+ curr_hash.each do |key, value|
77
+ next_node = value
78
+ result << get_all_from_node(next_node)
79
+ end
80
+
81
+ result
82
+ end
83
+
84
+ def insert_helper(moves, value, curr_node)
85
+ return if moves.empty?
86
+
87
+ curr_hash = curr_node.nodes
88
+ move = moves.first
89
+ last_move = moves.size == 1
90
+
91
+ if curr_hash[move].nil?
92
+ if last_move
93
+ curr_hash[move] = Node.new(value)
94
+ else
95
+ curr_hash[move] = Node.new(nil)
96
+ end
97
+ else
98
+ curr_hash[move].value = value if last_move && curr_hash[move].value.nil?
99
+ end
100
+
101
+ next_node = curr_hash[move]
102
+ insert_helper(moves.drop(1), value, next_node)
103
+ end
104
+
105
+ def search_helper(moves, curr_node)
106
+ move = moves.first
107
+ curr_hash = curr_node.nodes
108
+
109
+ return nil if curr_hash[move].nil?
110
+
111
+ next_node = curr_hash[move]
112
+ return search_helper(moves.drop(1), next_node) || curr_hash[move].value
113
+ end
114
+
115
+ def size_helper(node)
116
+ sum = node.value.nil? ? 0 : 1
117
+ return sum if node.is_leaf?
118
+ node.keys.each do |key|
119
+ sum += size_helper(node.nodes[key])
120
+ end
121
+ return sum
122
+ end
123
+
124
+ class Node
125
+
126
+ attr_accessor :value, :nodes
127
+
128
+ def initialize(value)
129
+ @value = value
130
+ @nodes = {}
131
+ end
132
+
133
+ def is_leaf?
134
+ @nodes.empty?
135
+ end
136
+
137
+ def size
138
+ @nodes.size
139
+ end
140
+
141
+ def keys
142
+ @nodes.keys
143
+ end
144
+
145
+ def include?(key)
146
+ @nodes.keys.include?(key)
147
+ end
148
+
149
+ def ==(other)
150
+ return false if self.size != other.size || @value != other.value
151
+ @nodes.keys.each do |key|
152
+ return false unless other.keys.include?(key)
153
+ end
154
+
155
+ @nodes.keys.each do |key|
156
+ return false if @nodes[key] != other.nodes[key]
157
+ end
158
+
159
+ true
160
+ end
161
+ end
162
+
163
+ end
@@ -0,0 +1,85 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+ require 'json'
4
+ load 'opening.rb'
5
+
6
+ def get_eco_code(name)
7
+ name.split(' ').first
8
+ end
9
+
10
+ def remove_eco_code(name)
11
+ name.split(' ').drop(1).join(' ')
12
+ end
13
+
14
+ def has_more_than_1_code?(name)
15
+ get_eco_code(name).size > 3
16
+ end
17
+
18
+ def convert_line_to_array(moves)
19
+ moves.split(' ').select{ |move| !move.include?('.') }
20
+ end
21
+
22
+ initial_page = "http://www.365chess.com/eco.php"
23
+
24
+ openings = []
25
+ pages_to_scrap = [initial_page]
26
+
27
+ # Scrap pages in pages_to_scrap array and dump Opening objects to openings array
28
+ pages_to_scrap.each do |link|
29
+
30
+ page = Nokogiri::HTML(open(link))
31
+ page.css('ul#tree li.closed div.line').each do |line|
32
+
33
+ if has_more_than_1_code?(line.css('div.opname a').text)
34
+
35
+ pages_to_scrap << line.css('div.opname a').first["href"]
36
+ else
37
+ name = remove_eco_code(line.css('div.opname a').text)
38
+ eco = get_eco_code(line.css('div.opname a').text)
39
+ moves = convert_line_to_array(line.css('div.fright').text.empty? ? line.css('div.opmoves').text : line.css('div.fright').text)
40
+
41
+ openings << Opening.new(name, eco, moves)
42
+ end
43
+ end
44
+ end
45
+
46
+ puts "Number of openings aquired: #{openings.size}"
47
+ puts "Number of pages scrapped: #{pages_to_scrap.size}"
48
+
49
+ # openings.each do |op|
50
+ # puts op.to_s
51
+ # end
52
+
53
+ # Check if there are some invalid openings
54
+ invalid_openings = openings.select do |op|
55
+ op.name.empty? || op.moves.empty? || op.eco_code.empty?
56
+ end
57
+
58
+ # If there are no invalid openings, go ahead and create a JSON
59
+ if invalid_openings.empty?
60
+
61
+ # Convert array elements to hashes
62
+ openings.map! { |op| op.to_h }
63
+
64
+ # Write to JSON
65
+ File.delete("openings.json") if File.exists?("openings.json")
66
+ File.open("openings.json", "a+") do |file|
67
+ file.write("{\n")
68
+ file.write("\"openings\": [\n")
69
+
70
+ openings.each_with_index do |op, index|
71
+ result = openings.size == index + 1 ? "" : ", "
72
+ file.write("#{JSON.generate(op)}#{result}\n")
73
+ end
74
+
75
+ file.write("]")
76
+ file.write("}")
77
+
78
+ puts "openings.json was successfully created!"
79
+ end
80
+
81
+ else
82
+ puts "There were some invalid openings detected:"
83
+ puts invalid_openings
84
+ end
85
+
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chess_openings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Simão Neves
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pgn
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.6.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.6.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.3.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.3.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: Gem that allows you to know what chess opening was used in a chess game,
84
+ find chess openings by name or get all openings that start with certain moves
85
+ email:
86
+ - simaocostaneves@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE
96
+ - README.md
97
+ - chess_openings-0.0.1.gem
98
+ - chess_openings.gemspec
99
+ - lib/chess_openings.rb
100
+ - lib/chess_openings/json_openings/openings.json
101
+ - lib/chess_openings/opening.rb
102
+ - lib/chess_openings/search_tree.rb
103
+ - openings_parser.rb
104
+ homepage: http://www.github.com/simaoneves/chess_openings
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.4.5
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Gem that allows you to calculate which opening was used in a chess game
128
+ test_files: []