gotasku 0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +116 -0
- data/bin/gotasku +101 -0
- data/db/tasks/db-tasks.rb +19 -0
- data/db/tasks/populate_database.rb +46 -0
- data/lib/gotasku.rb +17 -0
- data/lib/gotasku/difficulty_string.rb +22 -0
- data/lib/gotasku/display.rb +26 -0
- data/lib/gotasku/exceptions.rb +5 -0
- data/lib/gotasku/gotasku.rb +4 -0
- data/lib/gotasku/parser.rb +28 -0
- data/lib/gotasku/problem.rb +151 -0
- data/lib/gotasku/query.rb +106 -0
- data/lib/gotasku/rating_string.rb +11 -0
- data/lib/gotasku/session.rb +35 -0
- data/lib/gotasku/user.rb +11 -0
- data/rakefile +11 -0
- data/test/cli.rb +77 -0
- data/test/parser.rb +81 -0
- data/test/problem_features.rb +27 -0
- data/test/query.rb +52 -0
- data/test/sessions.rb +16 -0
- data/test/sgf.rb +79 -0
- data/test/user.rb +15 -0
- data/variables.rb +2 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a96fbb30bae291ebd35b1b6b7afb2ef18b1588a
|
4
|
+
data.tar.gz: 0ac6145dd789670da679de4e8e3b8f8d4ef6ac14
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87b2eab023c391a130c2839167b7380e3e0a9371220f8a0e4e454c1d1b0d6b9e54b2e2330b9bf34b89736db79542041f1cc6480377bc5ec297bc40b3c7869fe5
|
7
|
+
data.tar.gz: 7c10a24044927d1dd28f6b70e7ed43a6a10160568e14c965eff37af71f783b252256250a945690ebf615b98026d50b7e29cdb55981d65846574ad3874ebe05e6
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 John Hager
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
gotasku
|
2
|
+
=======
|
3
|
+
|
4
|
+
Saving and opening tsumego
|
5
|
+
|
6
|
+
_Use_
|
7
|
+
|
8
|
+
*Problem*
|
9
|
+
|
10
|
+
New problem. You can provide "id", "sgf", or "data" options.
|
11
|
+
"id" => Integer (id of goproblem.com problem)
|
12
|
+
"sgf" => String or File of SGF
|
13
|
+
"data" => Hash with any of the following keys
|
14
|
+
("id", "sgf", "difficulty",
|
15
|
+
"diff_num", "type", "rating")
|
16
|
+
|
17
|
+
Gotasku::Problem.new(options)
|
18
|
+
|
19
|
+
Gotasku::Problem.new("id" => 1000)
|
20
|
+
|
21
|
+
Gotasku::Problem.new("sgf" => "problem.sgf")
|
22
|
+
Gotasku::Problem.new("sgf" => "(;AB[aa]AW[ab]")
|
23
|
+
|
24
|
+
Gotasku::Problem.new("data" => {"id" => 1000,
|
25
|
+
"sgf" => "(;AB[aa]AW[ab])",
|
26
|
+
"difficulty" => "6 dan",
|
27
|
+
"diff_num" => -6,
|
28
|
+
"type" => "life and death",
|
29
|
+
"rating" => 3})
|
30
|
+
|
31
|
+
You can access Problem data with #sgf, #id, #difficulty, #type,
|
32
|
+
#rating, and #data instance methods
|
33
|
+
|
34
|
+
prob = Gotasku::Problem.new("id" => 10000)
|
35
|
+
prob.data
|
36
|
+
prob.difficulty
|
37
|
+
|
38
|
+
Gotasku use SgfParser to parse the SGF and save it.
|
39
|
+
|
40
|
+
You can get the tree from SGF with the #tree instance method
|
41
|
+
|
42
|
+
prob.tree
|
43
|
+
|
44
|
+
And you can save the file with the #save instance method
|
45
|
+
|
46
|
+
prob.save
|
47
|
+
|
48
|
+
The file will be saved as "#{id}.sgf" or using a time stamp if no id.
|
49
|
+
|
50
|
+
\*see MongoDB for more
|
51
|
+
|
52
|
+
|
53
|
+
*User*
|
54
|
+
|
55
|
+
You can create a user object.
|
56
|
+
|
57
|
+
user = Gotasku::User.new('username', 'password')
|
58
|
+
|
59
|
+
*Session*
|
60
|
+
|
61
|
+
You can login to goproblems.com by creating a new session object.
|
62
|
+
The current session is saved in the @@current\_session class variable
|
63
|
+
|
64
|
+
Gotasku::Session.new(user)
|
65
|
+
|
66
|
+
|
67
|
+
_*For MongoDB*_
|
68
|
+
|
69
|
+
If you have MongoDB installed, you can run the rake tasks from the
|
70
|
+
root directory of the gem:
|
71
|
+
|
72
|
+
rake db:create
|
73
|
+
rake problems
|
74
|
+
|
75
|
+
The first will create the database, the second will populate a
|
76
|
+
collection with all the go problems from goproblems.com.
|
77
|
+
|
78
|
+
_Querying_
|
79
|
+
|
80
|
+
You can build a query with Gotasku::Query.create (you will be
|
81
|
+
prompted on how you want to filter the problems collection.
|
82
|
+
|
83
|
+
Gotasku::Query.create
|
84
|
+
|
85
|
+
Or you can query the collection directly
|
86
|
+
Gotasku::Query.new(query\_hash)
|
87
|
+
|
88
|
+
Gotasku::Query.new({"id" => 1000})
|
89
|
+
|
90
|
+
This returns a Gotasku::Query object, with an instance variable
|
91
|
+
(@found) that holds the Mongo::Cursor object from the query.
|
92
|
+
|
93
|
+
query = Gotasku::Query.new({"type" => "life and death"})
|
94
|
+
query.found
|
95
|
+
|
96
|
+
Gotasku::Query has a #to\_a instance method to get an array from the
|
97
|
+
Mongo::Cursor object.
|
98
|
+
|
99
|
+
query.to\_a
|
100
|
+
|
101
|
+
*Problem*
|
102
|
+
|
103
|
+
index = 0
|
104
|
+
Gotasku::Problem.new("data", query.to\_a[index])
|
105
|
+
|
106
|
+
_Executable_
|
107
|
+
|
108
|
+
There is an executable in the bin folder, which you can add to your
|
109
|
+
PATH. It provides commands for getting data from or saving single
|
110
|
+
problems.
|
111
|
+
|
112
|
+
Use the help option to get more info:
|
113
|
+
|
114
|
+
$ path/to/gem/bin/gotasku --help
|
115
|
+
|
116
|
+
|
data/bin/gotasku
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
require_relative "../lib/gotasku"
|
6
|
+
|
7
|
+
def get_help
|
8
|
+
puts "\"gotasku #{ARGV.join(" ")}\" isn't a command I know."
|
9
|
+
puts "Try `gotasku --help`"
|
10
|
+
end
|
11
|
+
|
12
|
+
def problem(options)
|
13
|
+
Gotasku::Problem.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def prompt(text)
|
17
|
+
print(text)
|
18
|
+
$stdin.gets.chomp.strip
|
19
|
+
end
|
20
|
+
|
21
|
+
problem_commands = %w[ display sgf id save data difficulty
|
22
|
+
rating type ]
|
23
|
+
session_commands = %w[ login ]
|
24
|
+
rake_commands = %w[ test ]
|
25
|
+
|
26
|
+
options = {}
|
27
|
+
optparse = OptionParser.new do |opts|
|
28
|
+
|
29
|
+
opts.banner = "Gotasku"
|
30
|
+
opts.separator ""
|
31
|
+
opts.separator "Problem Commands"
|
32
|
+
problem_commands.each do |c|
|
33
|
+
opts.separator " #{c}"
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.separator ""
|
37
|
+
opts.separator "Options"
|
38
|
+
|
39
|
+
options["id"] = nil
|
40
|
+
opts.on( '-i', '--id Num', Integer, "Provide the problem id" ) do |id|
|
41
|
+
options["id"] = id
|
42
|
+
end
|
43
|
+
|
44
|
+
options["sgf"] = nil
|
45
|
+
opts.on( '-s', '--sgf String|FILE',
|
46
|
+
"Provide the problem SGF" ) do |sgf|
|
47
|
+
options["sgf"] = sgf
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.separator "Session Commands"
|
51
|
+
session_commands.each do |c|
|
52
|
+
opts.separator " #{c}"
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.separator "Options"
|
56
|
+
options[:username] = nil
|
57
|
+
opts.on( '-u', '--username Username',
|
58
|
+
"Provide your Username" ) do |username|
|
59
|
+
options[:username] = username
|
60
|
+
end
|
61
|
+
|
62
|
+
options[:password] = nil
|
63
|
+
opts.on( '-p', '--password Password',
|
64
|
+
"Provide your Password" ) do |password|
|
65
|
+
options[:password] = password
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.separator "Other Commands"
|
69
|
+
rake_commands.each do |c|
|
70
|
+
opts.separator " #{c}"
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.separator "Help"
|
74
|
+
opts.on("-h","--help","help") do
|
75
|
+
puts optparse
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
optparse.parse!
|
80
|
+
|
81
|
+
# if multiple commands are given
|
82
|
+
if ARGV.length > 1
|
83
|
+
get_help
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
if session_commands.include?(ARGV[0])
|
88
|
+
options[:username] ||= prompt("Username: ")
|
89
|
+
options[:password] ||= prompt("Password: ")
|
90
|
+
|
91
|
+
user = Gotasku::User.new(options[:username], options[:password])
|
92
|
+
p Gotasku::Session.new(user)
|
93
|
+
elsif problem_commands.include?(ARGV[0])
|
94
|
+
p problem(options).send(ARGV[0].to_sym)
|
95
|
+
elsif rake_commands.include?(ARGV[0])
|
96
|
+
puts `rake`
|
97
|
+
elsif ARGV[0].nil?
|
98
|
+
p problem(options)
|
99
|
+
else
|
100
|
+
get_help
|
101
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'mongo'
|
3
|
+
|
4
|
+
require_relative 'populate_database'
|
5
|
+
|
6
|
+
namespace :db do
|
7
|
+
desc "Create the database"
|
8
|
+
task :create do
|
9
|
+
client = Mongo::MongoClient.new
|
10
|
+
db = client.db('sgf')
|
11
|
+
collection = db.collection('owner')
|
12
|
+
collection.insert({name: "John"})
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Drop the database"
|
16
|
+
task :drop do
|
17
|
+
Mongo::MongoClient.new.drop_database('sgf')
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'mongo'
|
3
|
+
require_relative '../../lib/gotasku'
|
4
|
+
|
5
|
+
namespace :problems do
|
6
|
+
desc "Populate the problems collection"
|
7
|
+
task :populate do
|
8
|
+
client = Mongo::MongoClient.new
|
9
|
+
db = client.db('sgf')
|
10
|
+
collection = db.collection('problems')
|
11
|
+
|
12
|
+
(0..Gotasku::Problem.last_problem_id).each do |id|
|
13
|
+
if collection.find(id: id).first
|
14
|
+
print '_'
|
15
|
+
next
|
16
|
+
end
|
17
|
+
|
18
|
+
prob = Gotasku::Problem.new(id: id)
|
19
|
+
|
20
|
+
begin
|
21
|
+
if prob.blank? == false
|
22
|
+
collection.insert(prob.data)
|
23
|
+
print '.'
|
24
|
+
else
|
25
|
+
print '*'
|
26
|
+
end
|
27
|
+
rescue
|
28
|
+
print 'E'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Structure the problems collection"
|
34
|
+
task :structure do
|
35
|
+
collection = Mongo::MongoClient.new.db('sgf').collection('problems')
|
36
|
+
collection.ensure_index(
|
37
|
+
{id: Mongo::ASCENDING},
|
38
|
+
{unique: true, dropDups: true})
|
39
|
+
collection.create_index(diff_num: Mongo::DESCENDING)
|
40
|
+
collection.create_index(rating: Mongo::DESCENDING)
|
41
|
+
collection.create_index("type")
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Populate and structure problems collection"
|
45
|
+
task default: [:populate, :structure]
|
46
|
+
end
|
data/lib/gotasku.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'sgf'
|
4
|
+
require 'mechanize'
|
5
|
+
require 'mongo'
|
6
|
+
|
7
|
+
require_relative 'gotasku/gotasku'
|
8
|
+
|
9
|
+
require_relative 'gotasku/exceptions'
|
10
|
+
require_relative 'gotasku/problem'
|
11
|
+
require_relative 'gotasku/difficulty_string'
|
12
|
+
require_relative 'gotasku/rating_string'
|
13
|
+
require_relative 'gotasku/display'
|
14
|
+
require_relative 'gotasku/parser'
|
15
|
+
require_relative 'gotasku/user'
|
16
|
+
require_relative 'gotasku/session'
|
17
|
+
require_relative 'gotasku/query'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Gotasku::DifficultyString < String
|
2
|
+
|
3
|
+
# convert difficulty string to number
|
4
|
+
def convert
|
5
|
+
case self
|
6
|
+
when /p/ #pro
|
7
|
+
# grab number, mulitply by -1 and subtract 10
|
8
|
+
(self.slice(/\d+/).to_i * -1) - 10
|
9
|
+
when /d/ #dan
|
10
|
+
# grab number, multiply by -1
|
11
|
+
(self.slice(/\d+/).to_i * -1)
|
12
|
+
when /k/ #kyu
|
13
|
+
# grab number
|
14
|
+
self.slice(/\d+/).to_i
|
15
|
+
else
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Gotasku::Display
|
2
|
+
def self.show_problem(problem)
|
3
|
+
puts "=" * 40
|
4
|
+
puts "id: #{problem.id}"
|
5
|
+
puts "type: #{problem.type}"
|
6
|
+
puts "rating: #{problem.rating}"
|
7
|
+
puts "difficulty: #{problem.difficulty}"
|
8
|
+
puts "=" * 40
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.show_list(list)
|
12
|
+
list.each_with_index {|item, i| puts "#{i}...#{item}"}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.show(object)
|
16
|
+
case object
|
17
|
+
when ::Gotasku::Problem
|
18
|
+
self.show_problem(object)
|
19
|
+
when ::Array
|
20
|
+
self.show_list(object)
|
21
|
+
else
|
22
|
+
puts "Sorry, I can't show that.."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Gotasku::Parser < SGF::Parser
|
2
|
+
|
3
|
+
# validates and parses sgf string
|
4
|
+
def parse(sgf)
|
5
|
+
super(validate(sgf))
|
6
|
+
end
|
7
|
+
|
8
|
+
# validates sgf string for common goproblems.com issues
|
9
|
+
def validate(sgf)
|
10
|
+
validate_for(validate_for(sgf, :AB), :AW)
|
11
|
+
end
|
12
|
+
|
13
|
+
# validates for a property (AB or AW)
|
14
|
+
def validate_for(sgf, property)
|
15
|
+
regs = {AB: /AB/, AW: /AW/}
|
16
|
+
|
17
|
+
# find the first instance, all will be replaced and the first
|
18
|
+
# instance will be readded
|
19
|
+
start = sgf =~ regs[property]
|
20
|
+
|
21
|
+
if start
|
22
|
+
sgf.gsub!(regs[property], '')
|
23
|
+
sgf = sgf[0...start] + property.to_s + sgf[start..-1]
|
24
|
+
end
|
25
|
+
|
26
|
+
sgf
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
class Gotasku::Problem
|
2
|
+
|
3
|
+
@@uri = "http://www.goproblems.com/"
|
4
|
+
|
5
|
+
attr_reader :id
|
6
|
+
|
7
|
+
# initializes with a hash of options
|
8
|
+
def initialize(options = {})
|
9
|
+
@id = options["id"]
|
10
|
+
@sgf = options["sgf"]
|
11
|
+
|
12
|
+
if @sgf
|
13
|
+
# checks if sgf is a sgf file, and reads it if it is
|
14
|
+
if @sgf =~ /\.sgf/ && File.exists?(@sgf)
|
15
|
+
@sgf = File.open(@sgf, 'r').read
|
16
|
+
end
|
17
|
+
|
18
|
+
@data ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# overrides sgf and id if they are provided in the option data
|
22
|
+
if options["data"]
|
23
|
+
@data = options["data"]
|
24
|
+
@id = @data["id"] || @id
|
25
|
+
@sgf = @data["sgf"] || @sgf
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# get the id of the last problem on goproblems.com
|
30
|
+
def self.last_problem_id
|
31
|
+
@@last_problem_id ||= begin
|
32
|
+
doc = Nokogiri::HTML(open('http://www.goproblems.com/').read)
|
33
|
+
link = doc.css('td a').select {|a| a[:href] =~ /^\/\d+\z/}[0]
|
34
|
+
link[:href].slice(/\d+/).to_i
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# print out information on problem on screen
|
39
|
+
def display
|
40
|
+
Gotasku::Display.show(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
# access sgf or strip problem sgf from goproblems.com
|
44
|
+
def sgf
|
45
|
+
@sgf ||= data["sgf"]
|
46
|
+
end
|
47
|
+
|
48
|
+
# strip problem difficulty from goproblems.com
|
49
|
+
def difficulty
|
50
|
+
data["difficulty"]
|
51
|
+
end
|
52
|
+
|
53
|
+
# strip problem type from goproblems.com
|
54
|
+
def type
|
55
|
+
data["type"]
|
56
|
+
end
|
57
|
+
|
58
|
+
# strip problem rating from goproblems.com
|
59
|
+
def rating
|
60
|
+
data["rating"]
|
61
|
+
end
|
62
|
+
|
63
|
+
# access or get the tree from the Parser
|
64
|
+
def tree
|
65
|
+
@tree ||= Gotasku::Parser.new.parse(sgf)
|
66
|
+
end
|
67
|
+
|
68
|
+
# saves the sgf
|
69
|
+
def save
|
70
|
+
tree.save("#{id || Time.now.to_i}.sgf")
|
71
|
+
end
|
72
|
+
|
73
|
+
# this method is not finished
|
74
|
+
# uploads sgf to goproblems.com
|
75
|
+
def upload
|
76
|
+
# don't upload a problem that came from goproblems
|
77
|
+
unless @id
|
78
|
+
sess = Gotasku::Session.current
|
79
|
+
|
80
|
+
add_info_page = sess.link_with(href: /add/).click
|
81
|
+
add_page = add_info_page.link_with(text: /direct/).click
|
82
|
+
|
83
|
+
add_page.form_with(
|
84
|
+
action: 'addtest.php3') do |f|
|
85
|
+
source_field = f.field_with(name: "source")
|
86
|
+
source_field.value = ''
|
87
|
+
|
88
|
+
name_field = f.field_with(name: "name")
|
89
|
+
name_field.value = ''
|
90
|
+
|
91
|
+
sgf_field = f.field_with(name: "sgf")
|
92
|
+
sgf_field.value = @sgf
|
93
|
+
|
94
|
+
intro_field = f.field_with(name: "intro")
|
95
|
+
intro_field.value = ''
|
96
|
+
|
97
|
+
type_select = f.field_with(name: "genre")
|
98
|
+
type_select.value = 'elementary'
|
99
|
+
|
100
|
+
group_select = f.field_with(name: "usergroup[]")
|
101
|
+
group_select.value = ['none']
|
102
|
+
|
103
|
+
@submit = f.submit
|
104
|
+
end
|
105
|
+
|
106
|
+
# after submit, goes to flash app to confirm and complete
|
107
|
+
# the problem
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def blank?
|
112
|
+
sgf == '(;)'
|
113
|
+
end
|
114
|
+
|
115
|
+
def data
|
116
|
+
@data ||= begin
|
117
|
+
# get sgf from go problems
|
118
|
+
doc = Nokogiri::HTML(open("#{@@uri}#{@id}").read)
|
119
|
+
sgf_text = doc.css("div#player-container").text.gsub(/\r?\n/, '')
|
120
|
+
|
121
|
+
raise Gotasku::NotFound if sgf_text.empty?
|
122
|
+
|
123
|
+
difficulty = Gotasku::DifficultyString.new(
|
124
|
+
doc.css("div.difficulty a").text)
|
125
|
+
type = doc.css("div.prob_genre").text
|
126
|
+
rating = Gotasku::RatingString.new(
|
127
|
+
doc.css("div.probstars")[0][:class]).convert
|
128
|
+
|
129
|
+
{
|
130
|
+
"id" => @id,
|
131
|
+
"sgf" => sgf_text,
|
132
|
+
"difficulty" => difficulty,
|
133
|
+
"diff_num" => difficulty.convert,
|
134
|
+
"type" => type,
|
135
|
+
"rating" => rating
|
136
|
+
}
|
137
|
+
|
138
|
+
rescue Gotasku::NotFound
|
139
|
+
# puts "This problem cannot be found"
|
140
|
+
|
141
|
+
# at the moment, this seems the best solution, not ideal though
|
142
|
+
# because it allows for problems to be saved even if they are not
|
143
|
+
# found
|
144
|
+
{sgf: '(;)'}
|
145
|
+
|
146
|
+
rescue SocketError
|
147
|
+
puts "Poor internet connection"
|
148
|
+
{sgf: '(;)'}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
class Gotasku::Query
|
2
|
+
@@problems = Mongo::MongoClient.new.db('sgf').collection('problems')
|
3
|
+
|
4
|
+
attr_reader :found
|
5
|
+
|
6
|
+
# gets a Mongo::Cursor object
|
7
|
+
def initialize(query)
|
8
|
+
@found = @@problems.find(query)
|
9
|
+
end
|
10
|
+
|
11
|
+
# returns an array of items in @found
|
12
|
+
def to_a
|
13
|
+
@found.rewind!
|
14
|
+
@found.to_a
|
15
|
+
end
|
16
|
+
|
17
|
+
# returns a Mongo::Cursor object
|
18
|
+
def self.create
|
19
|
+
self.new(self.build_query)
|
20
|
+
end
|
21
|
+
|
22
|
+
=begin
|
23
|
+
# key is a string or symbol
|
24
|
+
def self.sort_by(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
# query is a hash
|
28
|
+
def self.query_by(query)
|
29
|
+
end
|
30
|
+
=end
|
31
|
+
|
32
|
+
private
|
33
|
+
# prompts user for input
|
34
|
+
def self.prompt(text, tru = nil, fals = nil)
|
35
|
+
print text
|
36
|
+
|
37
|
+
response = gets.chomp.strip.downcase
|
38
|
+
|
39
|
+
# if values are given to verrifying input as true/false
|
40
|
+
if tru and fals
|
41
|
+
if response =~ /#{tru}/
|
42
|
+
true
|
43
|
+
elsif response =~ /#{fals}/
|
44
|
+
false
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
else # else return an Array
|
49
|
+
response = response.split(',')
|
50
|
+
|
51
|
+
# throw an exception if the input is invalid
|
52
|
+
unless response.class == Array and
|
53
|
+
not(response.find {|item| item =~ /[a-z]/})
|
54
|
+
raise Gotasku::InvalidEntry
|
55
|
+
end
|
56
|
+
|
57
|
+
response.collect {|item| item.to_i}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#gets unique set of types from collection
|
62
|
+
def self.get_types
|
63
|
+
@@problems.distinct(:type).sort
|
64
|
+
end
|
65
|
+
|
66
|
+
#gets unique set of difficulties from collection
|
67
|
+
def self.get_difficulties
|
68
|
+
@@problems.distinct(:difficulty).sort do |x, y|
|
69
|
+
x = Gotasku::DifficultyString.new(x).convert
|
70
|
+
y = Gotasku::DifficultyString.new(y).convert
|
71
|
+
x <=> y
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#gets unique set of ratings from collection
|
76
|
+
def self.get_ratings
|
77
|
+
@@problems.distinct(:rating).sort
|
78
|
+
end
|
79
|
+
|
80
|
+
# runs a prompt to build a query for the database
|
81
|
+
def self.build_query
|
82
|
+
query_build = {}
|
83
|
+
q = {
|
84
|
+
type: -> {self.get_types},
|
85
|
+
difficulty: -> {self.get_difficulties},
|
86
|
+
rating: -> {self.get_ratings}
|
87
|
+
}
|
88
|
+
|
89
|
+
q.keys.each do |key|
|
90
|
+
if self.prompt("Do you want to filter by #{key} (y/n)? ",
|
91
|
+
'y', 'n')
|
92
|
+
list = q[key].call
|
93
|
+
Gotasku::Display.show(list)
|
94
|
+
puts '=' * 40
|
95
|
+
values = self.prompt(
|
96
|
+
"Please type the index/number for the values " +
|
97
|
+
"you would like to filter for (e.g. 0, 3, 4): ")
|
98
|
+
|
99
|
+
values.collect! {|value| list[value]}
|
100
|
+
query_build[key] = { "$in" => values }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
query_build
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class Gotasku::RatingString < String
|
2
|
+
|
3
|
+
# convert string to number
|
4
|
+
def convert
|
5
|
+
sum = self.slice(/sitesum:\d+/).slice(/\d+/).to_i
|
6
|
+
num = self.slice(/sitenum:\d+/).slice(/\d+/).to_i
|
7
|
+
|
8
|
+
# find the average or return the sum (if num is 0)
|
9
|
+
num == 0 ? 0 : sum / num
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Gotasku::Session
|
2
|
+
|
3
|
+
@@current = nil
|
4
|
+
|
5
|
+
attr_reader :account
|
6
|
+
|
7
|
+
# uses mechanize to login to goproblems.com and makes makes this the
|
8
|
+
# current session
|
9
|
+
def initialize(user)
|
10
|
+
a = Mechanize.new
|
11
|
+
a.get('http://www.goproblems.com/') do |page|
|
12
|
+
# Click the login link
|
13
|
+
login_page = a.click(page.link_with(href: /login/))
|
14
|
+
|
15
|
+
# Submit the login form
|
16
|
+
login_page.form_with(
|
17
|
+
action: 'login.php') do |f|
|
18
|
+
username_field = f.field_with(name: "name")
|
19
|
+
username_field.value = user.username
|
20
|
+
|
21
|
+
password_field = f.field_with(name: "password")
|
22
|
+
password_field.value = user.password
|
23
|
+
|
24
|
+
@account = f.submit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@@current = @account
|
29
|
+
end
|
30
|
+
|
31
|
+
# accesses current session
|
32
|
+
def self.current
|
33
|
+
@@current
|
34
|
+
end
|
35
|
+
end
|
data/lib/gotasku/user.rb
ADDED
data/rakefile
ADDED
data/test/cli.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative '../lib/gotasku'
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
|
7
|
+
load 'variables.rb' # username and password for testing
|
8
|
+
|
9
|
+
class GotaskuCLITest < MiniTest::Unit::TestCase
|
10
|
+
|
11
|
+
def test_get_problem
|
12
|
+
io = StringIO.new
|
13
|
+
io.puts(`./bin/gotasku`)
|
14
|
+
io.rewind
|
15
|
+
assert_match /Gotasku::Problem/, io.read
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_get_problem_with_id
|
19
|
+
io = StringIO.new
|
20
|
+
io.puts(`./bin/gotasku --id 1000`)
|
21
|
+
io.rewind
|
22
|
+
assert_match /@id=1000/, io.read
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_get_problem_with_sgf
|
26
|
+
io = StringIO.new
|
27
|
+
io.puts(`./bin/gotasku --sgf '(;AB[aa][bb][cc]AW[dd][ee])'`)
|
28
|
+
io.rewind
|
29
|
+
assert_includes io.read, "AB[aa][bb][cc]AW[dd][ee]"
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_get_problem_with_sgf_from_file
|
33
|
+
File.open('./test/john.sgf', 'w') {|f| f.write('(;AW[cc][cd][ad]AB[aa])')}
|
34
|
+
io = StringIO.new
|
35
|
+
io.puts(`./bin/gotasku --sgf ./test/john.sgf`)
|
36
|
+
io.rewind
|
37
|
+
assert_includes io.read, "AW[cc]"
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_save_problem
|
41
|
+
name = '1000.sgf'
|
42
|
+
|
43
|
+
File.delete(name) if File.exists?(name)
|
44
|
+
`./bin/gotasku save --id 1000 --sgf '(;AB[aa][bb][cc]AW[ab][bc][cd])'`
|
45
|
+
assert_includes Dir.glob('*.sgf'), name
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_display_problem_data
|
49
|
+
io = StringIO.new
|
50
|
+
io.puts(`./bin/gotasku display --id 1000`)
|
51
|
+
io.rewind
|
52
|
+
output = io.read
|
53
|
+
assert_match /id: 1000/, output
|
54
|
+
assert_match /type: life and death/, output
|
55
|
+
assert_match /rating: 3/, output
|
56
|
+
assert_match /difficulty: 6 dan/, output
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_login
|
60
|
+
io = StringIO.new
|
61
|
+
io.puts(`./bin/gotasku login --username #{::USERNAME} --password #{::PASSWORD}`)
|
62
|
+
io.rewind
|
63
|
+
assert_match /Gotasku::Session/, io.read
|
64
|
+
end
|
65
|
+
|
66
|
+
=begin
|
67
|
+
def test_get_problems_list_by_difficulty
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_get_problems_list_by_rating
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_get_problems_list_by_type
|
74
|
+
end
|
75
|
+
=end
|
76
|
+
end
|
77
|
+
|
data/test/parser.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/gotasku'
|
3
|
+
|
4
|
+
class GotaskuParserTest < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@parser = SGF::Parser.new
|
7
|
+
@gparser = Gotasku::Parser.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initialize_parser
|
11
|
+
assert_kind_of Gotasku::Parser, Gotasku::Parser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_inherits_for_sgf_parser
|
15
|
+
assert_includes Gotasku::Parser.ancestors, SGF::Parser
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parses_correct_sgf
|
19
|
+
sgf = '(;AB[lb][lc][ld][kc][jb][ka][ck][dg][ei])'
|
20
|
+
|
21
|
+
assert_equal @parser.parse(sgf), @gparser.parse(sgf)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_validates_sgf
|
25
|
+
correct_sgf = '(;AB[lb][lc][ld])'
|
26
|
+
incorrect_sgf = '(;AB[lb]AB[lc]AB[ld])'
|
27
|
+
|
28
|
+
assert_equal correct_sgf, @gparser.validate(incorrect_sgf)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_validates_sgf_with_AB_and_AW
|
32
|
+
correct_sgf = '(;AB[lb][lc][ld]AW[bb][cc])'
|
33
|
+
incorrect_sgf = '(;AB[lb]AB[lc]AB[ld]AW[bb]AW[cc])'
|
34
|
+
|
35
|
+
assert_equal correct_sgf, @gparser.validate(incorrect_sgf)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_parses_incorrect_sgf
|
39
|
+
correct_sgf = '(;AB[bb][bb][bb])'
|
40
|
+
incorrect_sgf = '(;AB[bb]AB[bb]AB[bb])'
|
41
|
+
|
42
|
+
assert_equal @parser.parse(correct_sgf),
|
43
|
+
@gparser.parse(incorrect_sgf)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_parses_incorrect_sgf_with_AB_and_AW
|
47
|
+
correct_sgf = '(;AB[lb][lc][ld]AW[bb][cc])'
|
48
|
+
incorrect_sgf = '(;AB[lb]AB[lc]AB[ld]AW[bb]AW[cc])'
|
49
|
+
|
50
|
+
assert_equal @parser.parse(correct_sgf),
|
51
|
+
@gparser.parse(incorrect_sgf)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_full_incorrect_sgf_AB
|
55
|
+
incorrect_sgf = '(;AW[pe]AW[pf]AW[qg]AW[qh]AW[oi]AW[ri]AW[oj]AW[pj]AW[rj]AW[sk]AW[rl]AW[sl]AW[rm]AB[pg]AB[ph]AB[rh]AB[pi]AB[qi]AB[qj]AB[pk]AB[qk]AB[rk]AB[pl]C[black to do something effective on the side]SZ[19]GM[1]FF[3]RU[Japanese]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-27]SY[Cgoban 1.9.2]TM[30:00(5x1:00)](;B[sh](;W[sj](;B[qf];W[rg];B[rf]C[RIGHT])(;B[rg];W[qf]))(;W[rg];B[sj];W[si];B[sj]C[snapbackRIGHT]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[rg];W[qf]C[black has nothing now])(;B[qf];W[rg](;B[sh];W[sg])(;B[rf];W[sh]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[qn];W[rg])(;B[qm];W[rg])(;B[rn];W[rg])(;B[rf];W[rg])(;B[sg];W[rg]))'
|
56
|
+
|
57
|
+
validated_sgf = @gparser.validate(incorrect_sgf)
|
58
|
+
|
59
|
+
refute_match /AB\[..\]AB/, validated_sgf
|
60
|
+
assert_match /AB/, validated_sgf
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_full_incorrect_sgf_AW
|
64
|
+
incorrect_sgf = '(;AW[pe]AW[pf]AW[qg]AW[qh]AW[oi]AW[ri]AW[oj]AW[pj]AW[rj]AW[sk]AW[rl]AW[sl]AW[rm]AB[pg]AB[ph]AB[rh]AB[pi]AB[qi]AB[qj]AB[pk]AB[qk]AB[rk]AB[pl]C[black to do something effective on the side]SZ[19]GM[1]FF[3]RU[Japanese]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-27]SY[Cgoban 1.9.2]TM[30:00(5x1:00)](;B[sh](;W[sj](;B[qf];W[rg];B[rf]C[RIGHT])(;B[rg];W[qf]))(;W[rg];B[sj];W[si];B[sj]C[snapbackRIGHT]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[rg];W[qf]C[black has nothing now])(;B[qf];W[rg](;B[sh];W[sg])(;B[rf];W[sh]))(;B[sj];W[si](;B[sh];W[sj](;B[qf];W[rg])(;B[rg];W[qf]))(;B[qf];W[rg])(;B[rg];W[qf]))(;B[qn];W[rg])(;B[qm];W[rg])(;B[rn];W[rg])(;B[rf];W[rg])(;B[sg];W[rg]))'
|
65
|
+
|
66
|
+
validated_sgf = @gparser.validate(incorrect_sgf)
|
67
|
+
|
68
|
+
refute_match /AW\[..\]AW/, validated_sgf
|
69
|
+
assert_match /AW/, validated_sgf
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_does_not_start_with_stone_positions
|
73
|
+
incorrect_sgf = '(;GM[1]FF[3]RU[Japanese]SZ[19]HA[0]KM[5.5]PW[White]PB[Black]GN[White (W) vs. Black (B)]DT[1999-07-28]SY[Cgoban 1.9.2]TM[30:00(5x1:00)];AW[ba]AW[ca]AW[ea]AW[bb]AW[eb]AW[cc]AW[ec]AW[ed]AW[ce]AW[de]AW[ee]AB[fa][fb][fc][bd][cd][dd][fd][be][fe][bf][cf][df][ef][ff](;B[dc];W[db];B[bc];W[ab]C[only ko])(;B[db];W[dc];B[ab];W[ac];B[bc]C[RIGHT])(;B[bc];W[db];B[dc];W[ab]C[only ko])(;B[ab];W[db](;B[dc];W[bc])(;B[bc];W[dc]))(;B[ac];W[ab]))'
|
74
|
+
|
75
|
+
validated_sgf = @gparser.validate(incorrect_sgf)
|
76
|
+
|
77
|
+
assert_match /AW/, validated_sgf
|
78
|
+
refute_match /AW\[..\]AW/, validated_sgf
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/gotasku'
|
3
|
+
|
4
|
+
class GotaskuFeatureTest < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@problem = Gotasku::Problem.new("id" => 1000)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_gets_difficulty
|
10
|
+
# 30k-1k, 1d-9d, 1p-9p => 30 - 1, -1 - -9, -11 - -19
|
11
|
+
assert_equal '6 dan', @problem.difficulty
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_gets_difficulty_conversion_for_sorting
|
15
|
+
# 30k-1k, 1d-9d, 1p-9p => 30 - 1, -1 - -9, -11 - -19
|
16
|
+
assert_equal -6, @problem.difficulty.convert
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_gets_type
|
20
|
+
assert_equal @problem.type, 'life and death'
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_gets_rating
|
24
|
+
assert_equal @problem.rating, 3
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
data/test/query.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative '../lib/gotasku'
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
|
5
|
+
require 'mongo'
|
6
|
+
require 'stringio'
|
7
|
+
|
8
|
+
class GotaskuQueryTest < MiniTest::Unit::TestCase
|
9
|
+
|
10
|
+
def test_makes_query
|
11
|
+
query = Gotasku::Query.new({"id" => 1000})
|
12
|
+
assert_kind_of Mongo::Cursor, query.found
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_builds_query
|
16
|
+
output = StringIO.new
|
17
|
+
file = StringIO.new
|
18
|
+
file << "y\n" << "1, 3\n" << "y\n"
|
19
|
+
file << "9, 15, 1\n" << "y\n" << "3\n"
|
20
|
+
file.rewind
|
21
|
+
|
22
|
+
temp_in = $stdin
|
23
|
+
temp_out = $stdout
|
24
|
+
$stdin = file
|
25
|
+
$stdout = output
|
26
|
+
|
27
|
+
query = Gotasku::Query.create
|
28
|
+
|
29
|
+
$stdin = temp_in
|
30
|
+
$stdout = temp_out
|
31
|
+
|
32
|
+
assert_kind_of Mongo::Cursor, query.found
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_handles_big_query
|
36
|
+
query = Gotasku::Query.new({
|
37
|
+
"id" => {"$lt" => 15000},
|
38
|
+
"diff_num" => {"$gt" => 1, "$lt" => 9},
|
39
|
+
"rating" => {"$in" =>
|
40
|
+
["life and death",
|
41
|
+
"elementary"]}
|
42
|
+
})
|
43
|
+
assert_kind_of Mongo::Cursor, query.found
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_new_problem_from_database
|
47
|
+
query = Gotasku::Query.new({"id" => 17642})
|
48
|
+
prob = Gotasku::Problem.new("data" => query.to_a.first)
|
49
|
+
assert prob.sgf
|
50
|
+
assert prob.id
|
51
|
+
end
|
52
|
+
end
|
data/test/sessions.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/gotasku'
|
3
|
+
|
4
|
+
require_relative '../variables'
|
5
|
+
|
6
|
+
class GotaskuSessionTest < MiniTest::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@user = Gotasku::User.new(::USERNAME, ::PASSWORD)
|
10
|
+
@session = Gotasku::Session.new(@user)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_creates_session
|
14
|
+
assert_kind_of Gotasku::Session, @session
|
15
|
+
end
|
16
|
+
end
|
data/test/sgf.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/gotasku'
|
3
|
+
|
4
|
+
class GotaskuProblemTest < MiniTest::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@problem = Gotasku::Problem.new("id" => 17625)
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_gets_id
|
10
|
+
assert_equal 17625, @problem.id
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_gets_sgf
|
14
|
+
sgf = '(;AB[se]AB[rd]AB[qd]AB[qe]AB[qf]AB[qg]AB[oe]AB[oc]AB[pc]AB[ob]AW[mb]AW[mc]AW[me]AW[mg]AW[og]AW[ph]AW[qh]AW[rg]AW[re]AW[rf]AW[sc]AW[qc]AW[qb]AW[pb]AW[rb]AP[goproblems]
|
15
|
+
(;B[ra]
|
16
|
+
(;W[rc]
|
17
|
+
(;B[sd];W[sa];B[sb]C[CHOICERIGHT])
|
18
|
+
(;B[pa];W[qa])
|
19
|
+
(;B[qa];W[pa])
|
20
|
+
(;B[sa];W[sd])
|
21
|
+
(;B[sb];W[qa]))
|
22
|
+
(;W[sd];B[sb]C[RIGHT]))
|
23
|
+
(;B[oa];W[sd])
|
24
|
+
(;B[pa];W[sd])
|
25
|
+
(;B[qa];W[pa])
|
26
|
+
(;B[sa];W[sd])
|
27
|
+
(;B[sb];W[rc]TR[sd]TR[sa])
|
28
|
+
(;B[rc];W[sb]TR[sd]TR[pa]))'.gsub(/\r?\n/, '')
|
29
|
+
|
30
|
+
assert_equal sgf, @problem.sgf
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_gets_another_sgf
|
34
|
+
sgf = '(;GM[1]FF[4]VW[]AP[Many Faces of Go:10.0]
|
35
|
+
SZ[13]
|
36
|
+
HA[0]
|
37
|
+
ST[1]
|
38
|
+
DT[2001-08-13]
|
39
|
+
KM[0.0]
|
40
|
+
RU[GOE]
|
41
|
+
AB[lb][lc][ld][kc][jb][ka][ck][dg][ei][fb][fc][fd][ee][db][cd][ej][fk][fl][fh][eg][fg][lj][kk][jj][ij][md][lf][gm][fm][gc]
|
42
|
+
AW[ib][jc][kd][jd][le][jf][hc][fi][gb][gd][fe][ff][ia][fj][gl][gk][gh][gg][jk][kg][lh][kl][ll][ik][me][ke][hm][hl]
|
43
|
+
C[After the previous problem, Black has played at 1 rather than
|
44
|
+
protecting his corner. How can white punish him?
|
45
|
+
]
|
46
|
+
PL[W]LB[gc:A]
|
47
|
+
(;W[mb]
|
48
|
+
(;B[ma]
|
49
|
+
(;W[la];B[ge];W[ma];B[mc];W[ja];B[ma];W[kb]
|
50
|
+
C[White has given up too much when black plays A
|
51
|
+
]LB[ge:A]
|
52
|
+
)
|
53
|
+
(;W[ja];B[kb];W[la]
|
54
|
+
C[RIGHT
|
55
|
+
]))
|
56
|
+
(;B[ja];W[la]))
|
57
|
+
(;W[ja];B[mb]
|
58
|
+
C[Black is unconditionally alive.
|
59
|
+
])
|
60
|
+
(;W[ge];B[ja]
|
61
|
+
C[Black has had another chance to repair his defect.
|
62
|
+
]))'.gsub(/\r?\n/, '')
|
63
|
+
|
64
|
+
assert_equal sgf, Gotasku::Problem.new("id" => 1000).sgf
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_gets_tree_for_sgf
|
68
|
+
assert_kind_of ::SGF::Tree, @problem.tree
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_saves_problem
|
72
|
+
file = Dir.pwd + '/17625.sgf'
|
73
|
+
|
74
|
+
File.delete(file) if File.exist?(file)
|
75
|
+
@problem.save
|
76
|
+
|
77
|
+
assert_includes Dir.glob(Dir.pwd + '/*.sgf'), file
|
78
|
+
end
|
79
|
+
end
|
data/test/user.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/gotasku'
|
3
|
+
|
4
|
+
require_relative '../variables'
|
5
|
+
|
6
|
+
class GotaskuUserTest < MiniTest::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@user = Gotasku::User.new(::USERNAME, ::PASSWORD)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_creates_user
|
13
|
+
assert_kind_of Gotasku::User, @user
|
14
|
+
end
|
15
|
+
end
|
data/variables.rb
ADDED
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gotasku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- jphager2
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A gem to download SGFs of go problems
|
14
|
+
email: jphager2@gmail.com
|
15
|
+
executables:
|
16
|
+
- gotasku
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- LICENSE
|
21
|
+
- README.md
|
22
|
+
- bin/gotasku
|
23
|
+
- db/tasks/db-tasks.rb
|
24
|
+
- db/tasks/populate_database.rb
|
25
|
+
- lib/gotasku.rb
|
26
|
+
- lib/gotasku/difficulty_string.rb
|
27
|
+
- lib/gotasku/display.rb
|
28
|
+
- lib/gotasku/exceptions.rb
|
29
|
+
- lib/gotasku/gotasku.rb
|
30
|
+
- lib/gotasku/parser.rb
|
31
|
+
- lib/gotasku/problem.rb
|
32
|
+
- lib/gotasku/query.rb
|
33
|
+
- lib/gotasku/rating_string.rb
|
34
|
+
- lib/gotasku/session.rb
|
35
|
+
- lib/gotasku/user.rb
|
36
|
+
- rakefile
|
37
|
+
- test/cli.rb
|
38
|
+
- test/parser.rb
|
39
|
+
- test/problem_features.rb
|
40
|
+
- test/query.rb
|
41
|
+
- test/sessions.rb
|
42
|
+
- test/sgf.rb
|
43
|
+
- test/user.rb
|
44
|
+
- variables.rb
|
45
|
+
homepage: https://github.com/jphager2/gotasku
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.2.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: Downloads SGFs of go problems
|
69
|
+
test_files: []
|