gotasku 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|