chess_openings 0.0.3 → 1.0.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.yardoc/checksums +4 -0
- data/.yardoc/complete +0 -0
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/.yardoc/proxy_types +0 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +13 -2
- data/README.md +124 -10
- data/{openings_parser.rb → bin/openings_parser} +24 -23
- data/chess_openings.gemspec +16 -14
- data/doc/ChessOpenings.html +1163 -0
- data/doc/ChessOpeningsHelper.html +355 -0
- data/doc/InvalidPGNError.html +135 -0
- data/doc/Opening.html +909 -0
- data/doc/SearchTree/Node.html +770 -0
- data/doc/SearchTree.html +1081 -0
- data/doc/_index.html +166 -0
- data/doc/class_list.html +51 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +58 -0
- data/doc/css/style.css +492 -0
- data/doc/file.README.html +241 -0
- data/doc/file_list.html +56 -0
- data/doc/frames.html +17 -0
- data/doc/index.html +241 -0
- data/doc/js/app.js +243 -0
- data/doc/js/full_list.js +216 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +371 -0
- data/doc/top-level-namespace.html +110 -0
- data/lib/chess_openings/chess_openings_helper.rb +18 -0
- data/lib/chess_openings/json_openings/openings.json +3 -3
- data/lib/chess_openings/opening.rb +31 -15
- data/lib/chess_openings/search_tree.rb +97 -81
- data/lib/chess_openings.rb +51 -20
- metadata +61 -7
- data/chess_openings-0.0.1.gem +0 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>
|
7
|
+
Top Level Namespace
|
8
|
+
|
9
|
+
— Documentation by YARD 0.9.7
|
10
|
+
|
11
|
+
</title>
|
12
|
+
|
13
|
+
<link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
|
14
|
+
|
15
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
|
16
|
+
|
17
|
+
<script type="text/javascript" charset="utf-8">
|
18
|
+
pathId = "";
|
19
|
+
relpath = '';
|
20
|
+
</script>
|
21
|
+
|
22
|
+
|
23
|
+
<script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
|
24
|
+
|
25
|
+
<script type="text/javascript" charset="utf-8" src="js/app.js"></script>
|
26
|
+
|
27
|
+
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<div class="nav_wrap">
|
31
|
+
<iframe id="nav" src="class_list.html?1"></iframe>
|
32
|
+
<div id="resizer"></div>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="main" tabindex="-1">
|
36
|
+
<div id="header">
|
37
|
+
<div id="menu">
|
38
|
+
|
39
|
+
<a href="_index.html">Index</a> »
|
40
|
+
|
41
|
+
|
42
|
+
<span class="title">Top Level Namespace</span>
|
43
|
+
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<div id="search">
|
47
|
+
|
48
|
+
<a class="full_list_link" id="class_list_link"
|
49
|
+
href="class_list.html">
|
50
|
+
|
51
|
+
<svg width="24" height="24">
|
52
|
+
<rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
|
53
|
+
<rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
|
54
|
+
<rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
|
55
|
+
</svg>
|
56
|
+
</a>
|
57
|
+
|
58
|
+
</div>
|
59
|
+
<div class="clear"></div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<div id="content"><h1>Top Level Namespace
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
</h1>
|
67
|
+
<div class="box_info">
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<h2>Defined Under Namespace</h2>
|
82
|
+
<p class="children">
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
<strong class="classes">Classes:</strong> <span class='object_link'><a href="ChessOpenings.html" title="ChessOpenings (class)">ChessOpenings</a></span>, <span class='object_link'><a href="ChessOpeningsHelper.html" title="ChessOpeningsHelper (class)">ChessOpeningsHelper</a></span>, <span class='object_link'><a href="InvalidPGNError.html" title="InvalidPGNError (class)">InvalidPGNError</a></span>, <span class='object_link'><a href="Opening.html" title="Opening (class)">Opening</a></span>, <span class='object_link'><a href="SearchTree.html" title="SearchTree (class)">SearchTree</a></span>
|
88
|
+
|
89
|
+
|
90
|
+
</p>
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<div id="footer">
|
103
|
+
Generated on Thu Jan 12 10:12:16 2017 by
|
104
|
+
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
+
0.9.7 (ruby-2.2.0).
|
106
|
+
</div>
|
107
|
+
|
108
|
+
</div>
|
109
|
+
</body>
|
110
|
+
</html>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Helper class with utility functions
|
2
|
+
class ChessOpeningsHelper
|
3
|
+
# Transform contents of array to symbols
|
4
|
+
#
|
5
|
+
# @param [Array] moves Moves as strings or symbols
|
6
|
+
# @return [Array] Array with all moves as symbols
|
7
|
+
def self.moves_as_symbols(moves)
|
8
|
+
moves.map { |move| move.is_a?(String) ? move.to_sym : move }
|
9
|
+
end
|
10
|
+
|
11
|
+
# Transform contents of array to strings
|
12
|
+
#
|
13
|
+
# @param [Array] moves Moves as strings or symbols
|
14
|
+
# @return [Array] Array with all moves as strings
|
15
|
+
def self.moves_as_strings(moves)
|
16
|
+
moves.map { |move| move.is_a?(Symbol) ? move.to_s : move }
|
17
|
+
end
|
18
|
+
end
|
@@ -636,8 +636,8 @@
|
|
636
636
|
{"name":"Sicilian, Pin, Koch variation","eco_code":"B40","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nf6","Nc3","Bb4","e5"]},
|
637
637
|
{"name":"Sicilian defence","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6"]},
|
638
638
|
{"name":"Sicilian, Szen (`anti-Taimanov') variation","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6","Nb5"]},
|
639
|
-
{"name":"Sicilian, Szen, hedgehog variation","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6","Nb5","d6","c4","Nf6","
|
640
|
-
{"name":"Sicilian, Szen variation, Dely-Kasparov gambit","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6","Nb5","d6","c4","Nf6","
|
639
|
+
{"name":"Sicilian, Szen, hedgehog variation","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6","Nb5","d6","c4","Nf6","N1c3","a6","Na3","Be7","Be2","O-O","O-O","b6"]},
|
640
|
+
{"name":"Sicilian, Szen variation, Dely-Kasparov gambit","eco_code":"B44","moves":["e4","c5","Nf3","e6","d4","cxd4","Nxd4","Nc6","Nb5","d6","c4","Nf6","N1c3","a6","Na3","d5"]},
|
641
641
|
{"name":"Sicilian","eco_code":"B50","moves":["e4","c5","Nf3","d6"]},
|
642
642
|
{"name":"Sicilian, wing gambit deferred","eco_code":"B50","moves":["e4","c5","Nf3","d6","b4"]},
|
643
643
|
{"name":"Sicilian, Chekhover variation","eco_code":"B53","moves":["e4","c5","Nf3","d6","d4","cxd4","Qxd4"]},
|
@@ -1678,7 +1678,7 @@
|
|
1678
1678
|
{"name":"French, Winawer, Kondratiyev variation","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Bd3","c5","exd5","Qxd5","Bd2"]},
|
1679
1679
|
{"name":"French, Winawer, fingerslip variation","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Bd2"]},
|
1680
1680
|
{"name":"French, Winawer, Alekhine (Maroczy) gambit","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Nge2"]},
|
1681
|
-
{"name":"French, Winawer, Alekhine gambit, Alatortsev variation","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Nge2","dxe4","a3","Be7","Nxe4","Nf6","
|
1681
|
+
{"name":"French, Winawer, Alekhine gambit, Alatortsev variation","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Nge2","dxe4","a3","Be7","Nxe4","Nf6","N2g3","O-O","Be2","Nc6"]},
|
1682
1682
|
{"name":"French, Winawer, Alekhine gambit","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Nge2","dxe4","a3","Bxc3+"]},
|
1683
1683
|
{"name":"French, Winawer, Alekhine gambit, Kan variation","eco_code":"C15","moves":["e4","e6","d4","d5","Nc3","Bb4","Nge2","dxe4","a3","Bxc3+","Nxc3","Nc6"]},
|
1684
1684
|
{"name":"French, Winawer, advance variation","eco_code":"C16","moves":["e4","e6","d4","d5","Nc3","Bb4","e5"]},
|
@@ -1,40 +1,56 @@
|
|
1
|
-
|
1
|
+
require 'pgn'
|
2
|
+
require_relative 'chess_openings_helper.rb'
|
2
3
|
|
4
|
+
# Class that represents a chess Opening
|
5
|
+
class Opening
|
3
6
|
attr_accessor :name, :eco_code, :moves
|
4
7
|
|
5
8
|
def initialize(name, eco_code, moves)
|
6
9
|
@name = name
|
7
10
|
@eco_code = eco_code
|
8
|
-
@moves = moves.map! { |move| move.is_a?(String)? move.to_sym : move }
|
11
|
+
@moves = moves.map! { |move| move.is_a?(String) ? move.to_sym : move }
|
9
12
|
end
|
10
13
|
|
14
|
+
# String representation of Opening
|
15
|
+
#
|
16
|
+
# @return [String] String that represents an Opening
|
11
17
|
def to_s
|
12
18
|
"#{@eco_code}: #{@name}\n#{@moves}"
|
13
19
|
end
|
14
20
|
|
21
|
+
# Hash representation of Opening
|
22
|
+
#
|
23
|
+
# @return [Hash] Hash that represents an Opening
|
15
24
|
def to_h
|
16
|
-
{
|
25
|
+
{ 'name' => @name, 'eco_code' => @eco_code, 'moves' => @moves }
|
17
26
|
end
|
18
27
|
|
28
|
+
# Compares two Openings
|
29
|
+
#
|
30
|
+
# @param [Opening] other Opening to be compared
|
31
|
+
# @return [Boolean] True if both Openings have same values, false otherwise
|
19
32
|
def ==(other)
|
20
33
|
@name == other.name && @eco_code == other.eco_code && @moves == other.moves
|
21
34
|
end
|
22
35
|
|
36
|
+
# Returns PGN formatted String of the Opening
|
37
|
+
#
|
38
|
+
# @return [String] String that represents a game where Opening was used
|
23
39
|
def to_pgn
|
24
|
-
result =
|
40
|
+
result = ''
|
25
41
|
index = 1
|
26
42
|
@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
|
-
|
43
|
+
result += index.odd? ? "#{(index / 2.0).ceil}.#{move}" : " #{move} "
|
36
44
|
index += 1
|
37
45
|
end
|
38
|
-
|
46
|
+
result.chop!
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns FEN formatted String representation of the Opening
|
50
|
+
#
|
51
|
+
# @return [String] String that represents a game where Opening was used
|
52
|
+
def to_fen
|
53
|
+
moves = ChessOpeningsHelper.moves_as_strings(@moves)
|
54
|
+
PGN::Game.new(moves).fen_list.last
|
39
55
|
end
|
40
|
-
end
|
56
|
+
end
|
@@ -1,128 +1,145 @@
|
|
1
|
-
|
1
|
+
require_relative 'chess_openings_helper.rb'
|
2
2
|
|
3
|
+
# Class that is a Tree-like data structure to hold all Openings
|
4
|
+
class SearchTree
|
3
5
|
attr_accessor :root
|
4
6
|
|
5
7
|
def initialize
|
6
8
|
@root = Node.new(nil)
|
7
9
|
end
|
8
10
|
|
11
|
+
# Check if tree doesnt have child Nodes and root is empty
|
12
|
+
#
|
13
|
+
# @return [Boolean] True if the tree doesnt have childs and root value is nil, false otherwise
|
9
14
|
def empty?
|
10
|
-
@root.
|
15
|
+
@root.empty? && @root.leaf?
|
11
16
|
end
|
12
17
|
|
18
|
+
# Number of not empty Nodes
|
19
|
+
#
|
20
|
+
# @return [int] Total of not empty Nodes in the tree
|
13
21
|
def size
|
14
22
|
size_helper(@root)
|
15
23
|
end
|
16
24
|
|
25
|
+
# String representation of the tree
|
26
|
+
#
|
27
|
+
# @return [String] Representation of the Nodes in the tree
|
17
28
|
def to_s
|
18
29
|
@root.to_s
|
19
30
|
end
|
20
31
|
|
32
|
+
# Compares two trees
|
33
|
+
#
|
34
|
+
# @param [SearchTree] other SearchTree to be compared with
|
35
|
+
# @return [Boolean] True if both trees have the same children with the same values
|
21
36
|
def ==(other)
|
22
|
-
|
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
|
37
|
+
@root == other.root
|
33
38
|
end
|
34
39
|
|
40
|
+
# Insert new value in SearchTree at the depth of the moves
|
41
|
+
#
|
42
|
+
# @param [Array] moves Path to the place to insert the value
|
43
|
+
# @param [Opening] value Value to be inserted in the tree
|
35
44
|
def insert(moves, value)
|
36
|
-
moves =
|
45
|
+
moves = ChessOpeningsHelper.moves_as_symbols(moves)
|
37
46
|
insert_helper(moves, value, @root)
|
38
47
|
end
|
39
48
|
|
49
|
+
# Search in the tree with the path moves
|
50
|
+
#
|
51
|
+
# @param [Array] moves Array with Strings or symbols that represent the path
|
52
|
+
# @return [Opening] Opening that was found in the tree, Nil otherwise
|
40
53
|
def search(moves)
|
41
|
-
moves =
|
54
|
+
moves = ChessOpeningsHelper.moves_as_symbols(moves)
|
42
55
|
search_helper(moves, @root)
|
43
56
|
end
|
44
57
|
|
58
|
+
# Search the tree for all the values from the path and values of its children
|
59
|
+
#
|
60
|
+
# @param [Array] moves Array with Strings or symbols that represent the path
|
61
|
+
# @return [Array] Array with values found
|
45
62
|
def search_all_with_moves(moves)
|
46
|
-
moves =
|
63
|
+
moves = ChessOpeningsHelper.moves_as_symbols(moves)
|
47
64
|
node = find_node(moves, @root)
|
48
65
|
get_all_from_node(node).flatten
|
49
66
|
end
|
50
67
|
|
68
|
+
# Get all values at a certain depth
|
69
|
+
#
|
70
|
+
# @param [int] num Depth to be used to find values
|
71
|
+
# @return [Array] Array with all the values found at depth num
|
72
|
+
def get_moves_in_depth(num)
|
73
|
+
get_moves_in_depth_helper(num, @root, 0).flatten
|
74
|
+
end
|
51
75
|
|
52
76
|
private
|
53
77
|
|
54
|
-
|
55
|
-
|
78
|
+
def get_moves_in_depth_helper(num_moves, curr_node, depth)
|
79
|
+
return [] if depth == num_moves && curr_node.value.nil?
|
80
|
+
return [curr_node.value] if depth == num_moves
|
81
|
+
result = []
|
82
|
+
curr_node.nodes.values.each do |node|
|
83
|
+
result << get_moves_in_depth_helper(num_moves, node, depth + 1)
|
56
84
|
end
|
85
|
+
result
|
86
|
+
end
|
57
87
|
|
58
|
-
|
59
|
-
|
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
|
88
|
+
def find_node(moves, curr_node)
|
89
|
+
return curr_node if moves.empty?
|
68
90
|
|
69
|
-
|
91
|
+
curr_hash = curr_node.nodes
|
92
|
+
move = moves.first
|
93
|
+
return nil if curr_hash[move].nil?
|
70
94
|
|
71
|
-
|
72
|
-
|
95
|
+
next_node = curr_hash[move]
|
96
|
+
find_node(moves.drop(1), next_node)
|
97
|
+
end
|
73
98
|
|
74
|
-
|
99
|
+
def get_all_from_node(curr_node)
|
100
|
+
result = curr_node.value.nil? ? [] : [curr_node.value]
|
101
|
+
return result if curr_node.leaf?
|
75
102
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
result
|
103
|
+
curr_hash = curr_node.nodes
|
104
|
+
curr_hash.values.each do |next_node|
|
105
|
+
result << get_all_from_node(next_node)
|
82
106
|
end
|
83
107
|
|
84
|
-
|
85
|
-
|
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
|
108
|
+
result
|
109
|
+
end
|
100
110
|
|
101
|
-
|
102
|
-
|
111
|
+
def insert_helper(moves, value, curr_node)
|
112
|
+
return if moves.empty?
|
113
|
+
curr_hash = curr_node.nodes
|
114
|
+
move = moves.first
|
115
|
+
last_move = moves.size == 1
|
116
|
+
if curr_hash[move].nil?
|
117
|
+
curr_hash[move] = last_move ? Node.new(value) : Node.new(nil)
|
118
|
+
elsif last_move && curr_hash[move].value.nil?
|
119
|
+
curr_hash[move].value = value
|
103
120
|
end
|
121
|
+
insert_helper(moves.drop(1), value, curr_hash[move])
|
122
|
+
end
|
104
123
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
return search_helper(moves.drop(1), next_node) || curr_hash[move].value
|
113
|
-
end
|
124
|
+
def search_helper(moves, curr_node)
|
125
|
+
move = moves.first
|
126
|
+
curr_hash = curr_node.nodes
|
127
|
+
return nil if curr_hash[move].nil?
|
128
|
+
next_node = curr_hash[move]
|
129
|
+
search_helper(moves.drop(1), next_node) || curr_hash[move].value
|
130
|
+
end
|
114
131
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
return sum
|
132
|
+
def size_helper(node)
|
133
|
+
sum = node.value.nil? ? 0 : 1
|
134
|
+
return sum if node.leaf?
|
135
|
+
node.keys.each do |key|
|
136
|
+
sum += size_helper(node.nodes[key])
|
122
137
|
end
|
138
|
+
sum
|
139
|
+
end
|
123
140
|
|
141
|
+
# Class that represents a node of a tree data structure
|
124
142
|
class Node
|
125
|
-
|
126
143
|
attr_accessor :value, :nodes
|
127
144
|
|
128
145
|
def initialize(value)
|
@@ -130,7 +147,11 @@ class SearchTree
|
|
130
147
|
@nodes = {}
|
131
148
|
end
|
132
149
|
|
133
|
-
def
|
150
|
+
def empty?
|
151
|
+
@value.nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
def leaf?
|
134
155
|
@nodes.empty?
|
135
156
|
end
|
136
157
|
|
@@ -147,17 +168,12 @@ class SearchTree
|
|
147
168
|
end
|
148
169
|
|
149
170
|
def ==(other)
|
150
|
-
return false if
|
171
|
+
return false if size != other.size || @value != other.value
|
151
172
|
@nodes.keys.each do |key|
|
152
173
|
return false unless other.keys.include?(key)
|
153
|
-
end
|
154
|
-
|
155
|
-
@nodes.keys.each do |key|
|
156
174
|
return false if @nodes[key] != other.nodes[key]
|
157
175
|
end
|
158
|
-
|
159
176
|
true
|
160
177
|
end
|
161
178
|
end
|
162
|
-
|
163
|
-
end
|
179
|
+
end
|
data/lib/chess_openings.rb
CHANGED
@@ -4,74 +4,105 @@ require 'json'
|
|
4
4
|
require 'chess_openings/opening.rb'
|
5
5
|
require 'chess_openings/search_tree.rb'
|
6
6
|
|
7
|
+
# Class responsible for searching for chess Openings
|
7
8
|
class ChessOpenings
|
8
|
-
|
9
9
|
attr_accessor :tree, :list
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@tree = SearchTree.new
|
13
13
|
@list = []
|
14
|
-
|
15
|
-
file = "/chess_openings/json_openings/openings.json"
|
14
|
+
file = '/chess_openings/json_openings/openings.json'
|
16
15
|
openings_file = File.join(File.dirname(__FILE__), file)
|
17
|
-
|
18
|
-
openings = JSON.load(File.open(openings_file))["openings"]
|
16
|
+
openings = JSON.load(File.open(openings_file))['openings']
|
19
17
|
openings.each do |op|
|
20
|
-
opening = Opening.new(op[
|
18
|
+
opening = Opening.new(op['name'], op['eco_code'], op['moves'])
|
21
19
|
@list << opening
|
22
20
|
@tree.insert opening.moves, opening
|
23
21
|
end
|
24
|
-
|
25
22
|
end
|
26
23
|
|
27
|
-
|
24
|
+
# Get all openings available
|
25
|
+
#
|
26
|
+
# @return [Array] Array with all the Openings possible
|
27
|
+
def all
|
28
28
|
@list.dup
|
29
29
|
end
|
30
30
|
|
31
|
+
# Get Opening from moves (as symbols or strings)
|
32
|
+
#
|
33
|
+
# @param [Array] moves Array with strings or symbols
|
34
|
+
# @return [Opening] Opening that has exactly the moves in the argument
|
31
35
|
def from_moves(moves)
|
32
|
-
# moves.map! { |move| move.to_s if move.is_a? Symbol }
|
33
36
|
@tree.search moves
|
34
37
|
end
|
35
38
|
|
39
|
+
# Get Opening from a PGN file
|
40
|
+
#
|
41
|
+
# @param [String] file_name String with the path to the PGN file
|
42
|
+
# @return [Opening] Opening that was used in the PGN game
|
43
|
+
# @raise [InvalidPGNError] Path points to invalid PGN file
|
44
|
+
# @raise [LoadError] Path points to non existent file
|
36
45
|
def from_pgn(file_name)
|
37
|
-
raise LoadError,
|
46
|
+
raise LoadError, 'File does not exist' unless File.exist?(file_name)
|
38
47
|
begin
|
39
48
|
game = PGN.parse(File.read(file_name)).first
|
40
|
-
@tree.search game.moves
|
49
|
+
@tree.search game.moves.map(&:notation)
|
41
50
|
rescue
|
42
|
-
raise InvalidPGNError,
|
51
|
+
raise InvalidPGNError, 'Invalid PGN file'
|
43
52
|
end
|
44
53
|
end
|
45
54
|
|
55
|
+
# Search Openings by name
|
56
|
+
#
|
57
|
+
# @param [String] name String with the name to search for
|
58
|
+
# @return [Array] Array with all the Openings found
|
46
59
|
def with_name(name)
|
47
60
|
@list.select { |op| op.name.downcase.include?(name.downcase) }
|
48
61
|
end
|
49
62
|
|
63
|
+
# Search all Openings that start with the moves in the argument
|
64
|
+
#
|
65
|
+
# @param [Array] moves Moves to be searched for
|
66
|
+
# @return [Array] Array with all Openings that start with the moves provided
|
50
67
|
def that_start_with(moves)
|
51
68
|
@tree.search_all_with_moves moves
|
52
69
|
end
|
53
70
|
|
71
|
+
# Get Opening from a PGN formatted String
|
72
|
+
#
|
73
|
+
# @param [String] string String in PGN format
|
74
|
+
# @return [Opening] Opening that was used in PGN formatted String
|
54
75
|
def from_string(string)
|
55
76
|
array = string.split
|
56
77
|
moves = []
|
57
78
|
array.each do |move|
|
58
|
-
if move.include?(
|
59
|
-
next if move.end_with?(
|
60
|
-
move = move.split(
|
79
|
+
if move.include?('.')
|
80
|
+
next if move.end_with?('.')
|
81
|
+
move = move.split('.').last
|
61
82
|
end
|
62
83
|
moves << move
|
63
84
|
end
|
64
85
|
@tree.search moves
|
65
86
|
end
|
66
87
|
|
67
|
-
|
88
|
+
# Get Opening from FEN string
|
89
|
+
#
|
90
|
+
# @param [String] fen_string FEN formatted String
|
91
|
+
# @return [Opening] Opening that was used in the game of the FEN String
|
92
|
+
def from_fen(fen_string)
|
93
|
+
fen = PGN::FEN.new(fen_string)
|
94
|
+
move_number = (fen.fullmove.to_i - 1) * 2
|
95
|
+
final_list = @tree.get_moves_in_depth(move_number)
|
96
|
+
final_list.select { |op| op.to_fen == fen_string }.first
|
97
|
+
end
|
68
98
|
|
69
|
-
|
70
|
-
@tree.to_s
|
71
|
-
end
|
99
|
+
private
|
72
100
|
|
101
|
+
def to_s
|
102
|
+
@tree.to_s
|
103
|
+
end
|
73
104
|
end
|
74
105
|
|
106
|
+
# Exception raised when there is a error reading a PGN file
|
75
107
|
class InvalidPGNError < RuntimeError
|
76
|
-
|
77
108
|
end
|