goshrine_bot 0.1.11 → 0.1.13
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.
- data/README.rdoc +16 -0
- data/Rakefile +2 -2
- data/goshrine_bot.gemspec +1 -1
- data/lib/goshrine_bot/client.rb +16 -41
- data/lib/goshrine_bot/cookies.rb +6 -5
- data/lib/goshrine_bot/game.rb +9 -8
- data/lib/goshrine_bot/goshrine_request.rb +45 -0
- data/lib/goshrine_bot.rb +2 -1
- metadata +15 -14
data/README.rdoc
CHANGED
@@ -24,6 +24,18 @@ The GTP protocol is documented here: http://www.lysator.liu.se/~gunnar/gtp/
|
|
24
24
|
|
25
25
|
* Run goshrine_bot botname
|
26
26
|
|
27
|
+
== ADVANCED CONFIGURATION:
|
28
|
+
|
29
|
+
In addition to the options listed above, there are some other configuration options that allow you avoid having your bot shut down for idle, and to limit the maximum number of games your bot will play at a time.
|
30
|
+
|
31
|
+
local_gnugo:
|
32
|
+
:login: gnugo
|
33
|
+
:password: testpass
|
34
|
+
:gtp_cmd_line: gnugo --mode gtp --level 10
|
35
|
+
:idle_shutdown_timeout: 10
|
36
|
+
:maximum_concurrent_games: 1
|
37
|
+
:debug: false
|
38
|
+
|
27
39
|
== TROUBLESHOOTING:
|
28
40
|
|
29
41
|
Look for a file named something like gtp_08e03daa.log. An exit status of 255 means that your program could not be found.
|
@@ -33,6 +45,10 @@ Look for a file named something like gtp_08e03daa.log. An exit status of 255 me
|
|
33
45
|
* json
|
34
46
|
* eventmachine
|
35
47
|
|
48
|
+
== THANKS:
|
49
|
+
|
50
|
+
Thanks to Gareth Davies for improvements that allow goshrine_bot to run pachi (and likely other bots as well).
|
51
|
+
|
36
52
|
== LICENSE:
|
37
53
|
|
38
54
|
(The MIT License)
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
require 'rake'
|
8
8
|
require 'rspec/core/rake_task'
|
9
|
-
require '
|
9
|
+
require 'rubygems/package_task'
|
10
10
|
|
11
11
|
task :default => :spec
|
12
12
|
|
@@ -16,7 +16,7 @@ end
|
|
16
16
|
|
17
17
|
load(File.join(File.dirname(__FILE__), "goshrine_bot.gemspec"))
|
18
18
|
|
19
|
-
|
19
|
+
Gem::PackageTask.new(SPEC) do |package|
|
20
20
|
# do nothing: I just need a gem but this block is required
|
21
21
|
end
|
22
22
|
|
data/goshrine_bot.gemspec
CHANGED
@@ -21,7 +21,7 @@ SPEC = Gem::Specification.new do |s|
|
|
21
21
|
END_DESCRIPTION
|
22
22
|
|
23
23
|
s.add_dependency('eventmachine', '>= 0.12.10')
|
24
|
-
s.add_dependency('em-http-request', '>=
|
24
|
+
s.add_dependency('em-http-request', '>= 0.3')
|
25
25
|
s.add_dependency('faye', '>= 0.6.0')
|
26
26
|
s.add_dependency('json', '>= 1.5.0')
|
27
27
|
|
data/lib/goshrine_bot/client.rb
CHANGED
@@ -4,11 +4,9 @@ module GoshrineBot
|
|
4
4
|
|
5
5
|
class Client
|
6
6
|
|
7
|
-
class << self
|
8
|
-
end
|
9
|
-
|
10
7
|
def initialize(options)
|
11
8
|
@base_url = URI::parse(options[:server_url])
|
9
|
+
GoshrineRequest.base_url = @base_url
|
12
10
|
@options = options
|
13
11
|
@password = options[:password]
|
14
12
|
@login = options[:login]
|
@@ -27,8 +25,8 @@ module GoshrineBot
|
|
27
25
|
|
28
26
|
def login(&blk)
|
29
27
|
# login
|
30
|
-
|
31
|
-
|
28
|
+
request = GoshrineRequest.new('/sessions/create').post(:body => {'login' => @login, 'password' => @password})
|
29
|
+
request.callback { |http|
|
32
30
|
if http.response_header.status == 401
|
33
31
|
puts "Invalid Login or Password"
|
34
32
|
EventMachine::stop
|
@@ -47,38 +45,14 @@ module GoshrineBot
|
|
47
45
|
end
|
48
46
|
end
|
49
47
|
}
|
50
|
-
|
48
|
+
request.errback { |http|
|
51
49
|
puts "Login failed (network issue?)";
|
52
50
|
}
|
53
51
|
end
|
54
|
-
|
55
|
-
def http_post(path, data = nil)
|
56
|
-
conn = EM::HttpRequest.new("#{@base_url}#{path}")
|
57
|
-
conn.use CookiePersist
|
58
|
-
headers = {'Accept' => 'application/json'}
|
59
|
-
|
60
|
-
http = conn.post(:head => headers, :body => data)
|
61
|
-
|
62
|
-
http.headers { |head|
|
63
|
-
CookiePersist.cookies << head[EM::HttpClient::SET_COOKIE]
|
64
|
-
}
|
65
|
-
http
|
66
|
-
end
|
67
|
-
|
68
|
-
def http_get(path)
|
69
|
-
conn = EM::HttpRequest.new("#{@base_url}#{path}")
|
70
|
-
conn.use CookiePersist
|
71
|
-
|
72
|
-
http = conn.get(:head => {'Accept' => 'application/json'})
|
73
|
-
|
74
|
-
http.headers { |head|
|
75
|
-
CookiePersist.cookies << head[EM::HttpClient::SET_COOKIE]
|
76
|
-
}
|
77
|
-
http
|
78
|
-
end
|
79
|
-
|
52
|
+
|
80
53
|
def subscribe
|
81
54
|
@faye_client.subscribe("/user/private/" + @queue_id) do |m|
|
55
|
+
puts "msg"
|
82
56
|
msg_type = m["type"]
|
83
57
|
case msg_type
|
84
58
|
when 'match_requested'
|
@@ -106,6 +80,7 @@ module GoshrineBot
|
|
106
80
|
load_existing_games {
|
107
81
|
if @options[:idle_shutdown_timeout] > 0
|
108
82
|
EM::add_periodic_timer( @options[:idle_shutdown_timeout] ) {
|
83
|
+
puts "checking"
|
109
84
|
@games.each do |token, game|
|
110
85
|
game.idle_check(@options[:idle_shutdown_timeout])
|
111
86
|
end
|
@@ -116,9 +91,9 @@ module GoshrineBot
|
|
116
91
|
end
|
117
92
|
|
118
93
|
def load_existing_games(&blk)
|
119
|
-
|
120
|
-
|
121
|
-
#puts "Got #{
|
94
|
+
request = GoshrineRequest.new('/game/active').get
|
95
|
+
request.callback { |http|
|
96
|
+
#puts "Got #{http.inspect}"
|
122
97
|
games = JSON.parse(http.response)
|
123
98
|
puts "#{games.count} game(s) in progress"
|
124
99
|
games.each do |game_attrs|
|
@@ -147,12 +122,12 @@ module GoshrineBot
|
|
147
122
|
|
148
123
|
def handle_match_accept(token)
|
149
124
|
game = GameInProgress.new(self)
|
150
|
-
|
151
|
-
|
125
|
+
request = GoshrineRequest.new("/g/#{token}")
|
126
|
+
request.callback { |http|
|
152
127
|
attrs = JSON.parse(http.response)
|
153
128
|
game.update_from_game_list(attrs)
|
154
129
|
add_game(game)
|
155
|
-
}
|
130
|
+
}
|
156
131
|
end
|
157
132
|
|
158
133
|
def handle_match_request(request)
|
@@ -161,8 +136,8 @@ module GoshrineBot
|
|
161
136
|
game = GameInProgress.new(self)
|
162
137
|
game.update_from_match_request(request)
|
163
138
|
if max_games.nil? || active_games.count < max_games
|
164
|
-
|
165
|
-
|
139
|
+
request = GoshrineRequest.new("/match/accept?id=#{game.challenge_id}").get
|
140
|
+
request.callback { |http|
|
166
141
|
attrs = JSON.parse(http.response)
|
167
142
|
game.update_from_game_list(attrs)
|
168
143
|
add_game(game)
|
@@ -171,7 +146,7 @@ module GoshrineBot
|
|
171
146
|
count = max_games > 1 ? "#{max_games} games" : "1 game"
|
172
147
|
reason = "#{@login} only plays #{count} at a time."
|
173
148
|
puts "Rejecting match: #{reason}"
|
174
|
-
|
149
|
+
GoshrineRequest.new("/match/reject?id=#{game.challenge_id}&reason=#{URI.escape(reason)}").get
|
175
150
|
end
|
176
151
|
end
|
177
152
|
|
data/lib/goshrine_bot/cookies.rb
CHANGED
@@ -23,23 +23,24 @@ class CookieHash < Hash #:nodoc:
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class CookiePersist
|
26
|
-
|
26
|
+
|
27
|
+
def cookies
|
27
28
|
Thread.current[:cookies] ||= []
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
+
def cookie_hash
|
31
32
|
CookieHash.new.tap { |hsh|
|
32
33
|
cookies.uniq.each { |c| hsh.add_cookies(c) }
|
33
34
|
}
|
34
35
|
end
|
35
36
|
|
36
|
-
def
|
37
|
+
def request(client, head, body)
|
37
38
|
head['cookie'] = cookie_hash.to_cookie_string
|
38
|
-
#puts "Sending cookies: #{head['cookie']}"
|
39
39
|
[head, body]
|
40
40
|
end
|
41
41
|
|
42
|
-
def
|
42
|
+
def response(resp)
|
43
|
+
cookies << resp.response_header[EM::HttpClient::SET_COOKIE]
|
43
44
|
resp
|
44
45
|
end
|
45
46
|
end
|
data/lib/goshrine_bot/game.rb
CHANGED
@@ -29,7 +29,7 @@ module GoshrineBot
|
|
29
29
|
user_id = m["id"].to_i
|
30
30
|
puts "User arrived (#{token}): #{m["login"]}"
|
31
31
|
if state == 'new' && (user_id == black_player_id || user_id == white_player_id)
|
32
|
-
|
32
|
+
GoshrineRequest.new("/game/#{token}/attempt_start").post
|
33
33
|
end
|
34
34
|
when 'game_started'
|
35
35
|
self.state = "in-play"
|
@@ -102,7 +102,7 @@ module GoshrineBot
|
|
102
102
|
puts "Got private game message: #{m.inspect}"
|
103
103
|
case m["type"]
|
104
104
|
when 'undo_requested'
|
105
|
-
|
105
|
+
GoshrineReuqest.new("/game/accept_undo/" + m["request_id"]).post
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
@@ -157,7 +157,7 @@ module GoshrineBot
|
|
157
157
|
end
|
158
158
|
|
159
159
|
if state == 'new'
|
160
|
-
|
160
|
+
GoshrineRequest.new("/game/#{token}/attempt_start").post
|
161
161
|
end
|
162
162
|
|
163
163
|
end
|
@@ -177,15 +177,16 @@ module GoshrineBot
|
|
177
177
|
res.callback { |response_move|
|
178
178
|
puts "Generated move: #{response_move} (#{token})"
|
179
179
|
self.move_number += 1
|
180
|
+
request = nil
|
180
181
|
if response_move.upcase == 'PASS'
|
181
|
-
|
182
|
+
request = GoshrineRequest.new("/game/#{token}/pass").post
|
182
183
|
elsif response_move.upcase == 'RESIGN'
|
183
|
-
|
184
|
+
request = GoshrineRequest.new("/game/#{token}/resign").post
|
184
185
|
else
|
185
186
|
rgo_coord = gtp_coord_to_goshrine_coord(response_move.upcase, board_size)
|
186
|
-
|
187
|
+
request = GoshrineRequest.new("/game/#{token}/move/#{rgo_coord}").post
|
187
188
|
end
|
188
|
-
|
189
|
+
request.callback { |http|
|
189
190
|
if http.response_header.status == 200
|
190
191
|
self.moves << [my_color, response_move]
|
191
192
|
else
|
@@ -193,7 +194,7 @@ module GoshrineBot
|
|
193
194
|
end
|
194
195
|
}
|
195
196
|
}
|
196
|
-
end
|
197
|
+
end
|
197
198
|
|
198
199
|
def start_engine
|
199
200
|
gtp = GtpStdioClient.new(@client.gtp_cmd_line, "gtp_#{token}.log")
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module GoshrineBot
|
2
|
+
|
3
|
+
class GoshrineRequest
|
4
|
+
|
5
|
+
def self.base_url=(url)
|
6
|
+
@@base_url = url
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@url = "#{@@base_url}#{path}"
|
11
|
+
@conn = EventMachine::HttpRequest.new(@url)
|
12
|
+
@cookie_persist = CookiePersist.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def do_http(verb,options)
|
16
|
+
puts "Sending"
|
17
|
+
options[:head] =
|
18
|
+
{'Accept' => 'application/json',
|
19
|
+
'cookie' => @cookie_persist.cookie_hash.to_cookie_string}
|
20
|
+
|
21
|
+
http = @conn.send verb, options
|
22
|
+
|
23
|
+
defer = EM::DefaultDeferrable.new
|
24
|
+
http.callback {
|
25
|
+
puts "Persisting cookies"
|
26
|
+
@cookie_persist.cookies << http.response_header[EM::HttpClient::SET_COOKIE]
|
27
|
+
defer.succeed(http)
|
28
|
+
}
|
29
|
+
http.errback {
|
30
|
+
puts "failed goshrine request"
|
31
|
+
defer.fail(http)
|
32
|
+
}
|
33
|
+
defer
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(options = {})
|
37
|
+
do_http(:post, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(options = {})
|
41
|
+
do_http(:get, options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/goshrine_bot.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goshrine_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.13
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-12-15 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement: &
|
16
|
+
requirement: &70254337655960 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,21 +21,21 @@ dependencies:
|
|
21
21
|
version: 0.12.10
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70254337655960
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: em-http-request
|
27
|
-
requirement: &
|
27
|
+
requirement: &70254337655480 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: '0.3'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70254337655480
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: faye
|
38
|
-
requirement: &
|
38
|
+
requirement: &70254337655020 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.6.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70254337655020
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: json
|
49
|
-
requirement: &
|
49
|
+
requirement: &70254337654560 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.5.0
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70254337654560
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rspec
|
60
|
-
requirement: &
|
60
|
+
requirement: &70254337654180 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70254337654180
|
69
69
|
description: The GoShrine bot client is a library that allows you connect a local
|
70
70
|
Go playing program that speaks GTP (like gnugo) to http://goshrine.com.
|
71
71
|
email:
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- lib/goshrine_bot/core_ext/hash.rb
|
92
92
|
- lib/goshrine_bot/faye_auth_ext.rb
|
93
93
|
- lib/goshrine_bot/game.rb
|
94
|
+
- lib/goshrine_bot/goshrine_request.rb
|
94
95
|
- lib/goshrine_bot/gtp_stdio_client.rb
|
95
96
|
- lib/goshrine_bot/runner.rb
|
96
97
|
homepage: http://github.com/ps2/goshrine_bot
|
@@ -113,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
114
|
version: '0'
|
114
115
|
requirements: []
|
115
116
|
rubyforge_project:
|
116
|
-
rubygems_version: 1.8.
|
117
|
+
rubygems_version: 1.8.12
|
117
118
|
signing_key:
|
118
119
|
specification_version: 3
|
119
120
|
summary: A client to connect GTP go programs to GoShrine
|