shogi-server source
修訂 | 0800fb9ae1c8d130852e28e66386b45eafd33ca1 (tree) |
---|---|
時間 | 2004-06-05 19:50:14 |
作者 | nabeken <nabeken@b8c6...> |
Commiter | nabeken |
need to update message from server to client
@@ -21,7 +21,6 @@ DEFAULT_TIMEOUT = 10 # for single socket operation | ||
21 | 21 | Total_Time = 1500 |
22 | 22 | Least_Time_Per_Move = 1 |
23 | 23 | Watchdog_Time = 30 # time for ping |
24 | -Agree_Time = 300 # time for AGREE | |
25 | 24 | Login_Time = 300 # time for LOGIN |
26 | 25 | |
27 | 26 | Release = "$Name$".split[1].sub(/\A[^\d]*/, '').gsub(/_/, '.') |
@@ -87,6 +86,19 @@ class League | ||
87 | 86 | return false |
88 | 87 | end |
89 | 88 | end |
89 | + def get_player(status, game_name, sente) | |
90 | + @hash.each do |name, player| | |
91 | + if ((player.status == status) | |
92 | + (player.game_name == game_name) | |
93 | + (player.sente == sente)) | |
94 | + return player | |
95 | + end | |
96 | + end | |
97 | + return nil | |
98 | + end | |
99 | + def new_game(game_name, player0, player1) | |
100 | + game = Game::new(game_name, player0, player1) | |
101 | + end | |
90 | 102 | end |
91 | 103 | |
92 | 104 |
@@ -97,11 +109,12 @@ class Player | ||
97 | 109 | @name = nil |
98 | 110 | @password = nil |
99 | 111 | @socket = socket |
100 | - @state = "connected" # wait_game -> game | |
112 | + @status = "connected" # game_waiting -> agree_waiting -> start_waiting -> game | |
101 | 113 | |
102 | 114 | @x1 = false # extention protocol |
103 | 115 | @eol = "\m" # favorite eol code |
104 | 116 | @game = nil |
117 | + @game_name = "" | |
105 | 118 | @mytime = Total_Time |
106 | 119 | @sente = nil |
107 | 120 | @watchdog_thread = nil |
@@ -109,12 +122,44 @@ class Player | ||
109 | 122 | login(str) |
110 | 123 | end |
111 | 124 | |
112 | - attr_accessor :name, :password, :socket, :state | |
113 | - attr_accessor :x1, :eol, :game, :mytime, :watchdog_thread | |
125 | + attr_accessor :name, :password, :socket, :status | |
126 | + attr_accessor :x1, :eol, :game, :mytime, :watchdog_thread, :game_name, :sente | |
114 | 127 | |
128 | + def finish | |
129 | + Thread::kill(@watchdog_thread) if @watchdog_thread | |
130 | + @socket.close | |
131 | + end | |
132 | + | |
133 | + def watchdog(time) | |
134 | + while true | |
135 | + begin | |
136 | + Ping.pingecho(@socket.addr[3]) | |
137 | + rescue | |
138 | + end | |
139 | + sleep(time) | |
140 | + end | |
141 | + end | |
142 | + | |
143 | + def to_s | |
144 | + if ((status == "game_waiting") || | |
145 | + (status == "agree_waiting") || | |
146 | + (status == "game")) | |
147 | + if (@sente) | |
148 | + return sprintf("%s %s %s +", @name, @status, @game_name, "+") | |
149 | + else | |
150 | + return sprintf("%s %s %s -", @name, @status, @game_name, "-") | |
151 | + end | |
152 | + else | |
153 | + return sprintf("%s %s", @name, @status) | |
154 | + end | |
155 | + end | |
156 | + | |
157 | + def write_help(str) | |
158 | + @socket.write_safe('## available commands "%%WHO", "%%CHAT str", "%%GAME game_name +", "%%GAME game_name -"') | |
159 | + end | |
115 | 160 | |
116 | 161 | def write_safe(str) |
117 | - @socket.write_safe(str + @eol) | |
162 | + @socket.write_safe(str.gsub(/[\r\n]+/, @eol)) | |
118 | 163 | end |
119 | 164 | |
120 | 165 | def login(str) |
@@ -123,12 +168,15 @@ class Player | ||
123 | 168 | str.chomp! |
124 | 169 | (login, @name, @password, ext) = str.split |
125 | 170 | @x1 = true if (ext) |
171 | + @watchdog_thread = Thread::start do | |
172 | + watchdog(Watchdog_Time) | |
173 | + end | |
126 | 174 | end |
127 | - | |
175 | + | |
128 | 176 | def run |
129 | 177 | if (@x1) |
130 | 178 | log_message(sprintf("user %s run in x1 mode", @name)) |
131 | - write_safe("## LOGIN in x1 mode") | |
179 | + write_safe("## LOGIN in x1 mode\n") | |
132 | 180 | else |
133 | 181 | log_message(sprintf("user %s run in CSA mode", @name)) |
134 | 182 | end |
@@ -136,11 +184,37 @@ class Player | ||
136 | 184 | while (str = @socket.gets_safe) |
137 | 185 | str.chomp! |
138 | 186 | case str |
187 | + when /^[\+\-%][^%]/ | |
188 | + if (@status == "game") | |
189 | + @game.handle_one_move(str, self) | |
190 | + else | |
191 | + write_safe("## you are in %s status. %s in game status\n", @status) | |
192 | + next | |
193 | + end | |
194 | + when /^AGREE/ | |
195 | + if (@status == "agree_waiting") | |
196 | + @status = "start_waiting" | |
197 | + if ((@game.sente.status == "start_waiting") && | |
198 | + (@game.gote.status == "start_waiting")) | |
199 | + @game.start | |
200 | + @game.sente.status = "game" | |
201 | + @game.gote.status = "game" | |
202 | + end | |
203 | + else | |
204 | + write_safe("## you are in %s status. AGREE is valid in agree_waiting status\n", @status) | |
205 | + next | |
206 | + end | |
139 | 207 | when /^%%HELP/ |
140 | 208 | write_help |
141 | 209 | when /^%%GAME\s+(\S+)\s+([\+\-])/ |
142 | - game_name = $1 | |
143 | - @state = "game_waiting" | |
210 | + if ((@status == "connected") || (@status == "game_waiting")) | |
211 | + @status = "game_waiting" | |
212 | + else | |
213 | + write_safe("## you are in %s status. GAME is valid in connected or game_waiting status\n", @status) | |
214 | + next | |
215 | + end | |
216 | + @status = "game_waiting" | |
217 | + @game_name = $1 | |
144 | 218 | if ($2 == "+") |
145 | 219 | @sente = true |
146 | 220 | rival_sente = false |
@@ -148,25 +222,26 @@ class Player | ||
148 | 222 | @sente = false |
149 | 223 | rival_sente = true |
150 | 224 | end |
151 | - rival = LEAGUE.get_player(game_name, rival_sente) | |
225 | + rival = LEAGUE.get_player("game_waiting", @game_name, rival_sente) | |
152 | 226 | if (rival) |
153 | - @state = "game" | |
154 | - LEAGUE.start_game(game_name, self, rival) | |
227 | + LEAGUE.new_game(@game_name, self, rival) | |
228 | + self.status = "agree_waiting" | |
229 | + rival.status = "agree_waiting" | |
155 | 230 | end |
156 | 231 | when /^%%CHAT\s+(\S+)/ |
157 | 232 | message = $1 |
158 | 233 | LEAGUE.hash.each do |name, player| |
159 | - s = player.write_safe(sprintf("## [%s] %s", @name, message)) | |
234 | + s = player.write_safe(sprintf("## [%s] %s\n", @name, message)) | |
160 | 235 | player.status = "zombie" if (! s) |
161 | 236 | end |
162 | 237 | when /^%%WHO/ |
163 | 238 | LEAGUE.hash.each do |name, player| |
164 | - write_safe(sprintf("## %s %s", name, player.state)) | |
239 | + write_safe(sprintf("## %s\n", player.to_s)) | |
165 | 240 | end |
166 | 241 | when /^%%LOGOUT/ |
167 | 242 | break |
168 | 243 | else |
169 | - write_safe(sprintf("## unknown command %s", str)) | |
244 | + write_safe(sprintf("## unknown command %s\n", str)) | |
170 | 245 | end |
171 | 246 | end |
172 | 247 | end |
@@ -176,24 +251,83 @@ class Board | ||
176 | 251 | end |
177 | 252 | |
178 | 253 | class Game |
179 | - def initialize(event, sente, gote) | |
180 | - @id = sprintf("%s-%s-%s-%s", event, sente.name, gote.name, Time::new.strftime("%Y%m%d%H%M%S")) | |
254 | + def initialize(game_name, player0, player1) | |
255 | + @game_name = game_name | |
256 | + if (player0.sente) | |
257 | + @sente = player0 | |
258 | + @gote = player1 | |
259 | + else | |
260 | + @sente = player0 | |
261 | + @gote = player1 | |
262 | + end | |
263 | + @current_player = @sente | |
264 | + @next_player = @gote | |
265 | + | |
266 | + @sente.game = self | |
267 | + @gote.game = self | |
268 | + @sente.status = "agree_waiting" | |
269 | + @gote.status = "agree_waiting" | |
270 | + @id = sprintf("%s-%s-%s-%s", @game_name, @sente.name, @gote.name, Time::new.strftime("%Y%m%d%H%M%S")) | |
271 | + log_message(sprintf("game created %s %s %s", game_name, sente.name, gote.name)) | |
272 | + | |
181 | 273 | @logfile = @id + ".csa" |
182 | - @sente = sente | |
183 | - @gote = gote | |
184 | - @sente.sg_flag = "+" | |
185 | - @gote.sg_flag = "-" | |
186 | 274 | @board = Board::new |
187 | - @currnet_player = sente | |
188 | - @next_player = gote | |
275 | + @start_time = nil | |
189 | 276 | @fh = nil |
190 | - printf("%s: new game %s %s %s\n", Time::new.to_s, @id, @sente.name, @gote.name) | |
277 | + | |
278 | + propose | |
279 | + end | |
280 | + attr_accessor :game_name, :sente, :gote, :id, :board, :current_player, :next_player, :fh | |
281 | + | |
282 | + def handle_one_move(str, player) | |
283 | + if (@current_player == player) | |
284 | + @end_time = Time::new | |
285 | + t = @end_time - @start_time | |
286 | + t = Least_Time_Per_Move if (t < Least_Time_Per_Move) | |
287 | + @sente.write_safe(sprintf("%s,T%d\n", str, t)) | |
288 | + @gote.write_safe(sprintf("%s,T%d\n", str, t)) | |
289 | + @current_player.mytime = @current_player.mytime - t | |
290 | + if (@current_player < 0) | |
291 | + timeout_end() | |
292 | + elsif (str =~ /%KACHI/) | |
293 | + kachi_end() | |
294 | + elsif (str =~ /%TORYO/) | |
295 | + toryo_end | |
296 | + end | |
297 | + (@current_player, @next_player) = [@next_player, @current_player] | |
298 | + @start_time = Time::new | |
299 | + end | |
300 | + end | |
301 | + | |
302 | + def timeout_end | |
303 | + @current_player.status = "connected" | |
304 | + @next_player.status = "connected" | |
305 | + @current_player.write("#TIME_UP\n#LOSE\n") | |
306 | + @next_player.write("#TIME_UP\n#WIN\n") | |
307 | + end | |
308 | + | |
309 | + def kachi_end | |
310 | + @current_player.status = "connected" | |
311 | + @next_player.status = "connected" | |
312 | + @current_player.write("#JISHOGI\n#WIN\n") | |
313 | + @next_player.write("#JISHOGI\n#LOSE\n") | |
191 | 314 | end |
315 | + | |
316 | + def toryo_end | |
317 | + @current_player.status = "connected" | |
318 | + @next_player.status = "connected" | |
319 | + @current_player.write("#RESIGN\n#LOSE\n") | |
320 | + @next_player.write("#RESIGN\n#WIN\n") | |
321 | + end | |
322 | + | |
192 | 323 | def start |
193 | - begin | |
194 | - @sente.watchdog_start(Watchdog_Time) | |
195 | - @gote.watchdog_start(Watchdog_Time) | |
324 | + @sente.write_safe(sprintf("START:%s\n", @id)) | |
325 | + @gote.write_safe(sprintf("START:%s\n", @id)) | |
326 | + @start_time = Time::new | |
327 | + end | |
196 | 328 | |
329 | + def propose | |
330 | + begin | |
197 | 331 | @fh = open(@logfile, "w") |
198 | 332 | @fh.sync = true |
199 | 333 |
@@ -201,10 +335,9 @@ class Game | ||
201 | 335 | @fh.printf("N+%s\n", @sente.name) |
202 | 336 | @fh.printf("N-%s\n", @gote.name) |
203 | 337 | @fh.printf("$EVENT:%s\n", @id) |
204 | - @sente.write(start_message("+")) | |
205 | - @gote.write(start_message("-")) | |
206 | - @sente.wait_agree(Agree_Time) | |
207 | - @gote.wait_agree(Agree_Time) | |
338 | + | |
339 | + @sente.write_safe(propose_message("+")) | |
340 | + @gote.write_safe(propose_message("-")) | |
208 | 341 | |
209 | 342 | @fh.printf("$START_TIME:%s\n", Time::new.strftime("%Y/%m/%d %H:%M:%S")) |
210 | 343 | @fh.print <<EOM |
@@ -219,83 +352,10 @@ P8 * +KA * * * * * +HI * | ||
219 | 352 | P9+KY+KE+GI+KI+OU+KI+GI+KE+KY |
220 | 353 | + |
221 | 354 | EOM |
222 | - | |
223 | - @sente.write(sprintf("START:%s\n", @id)) | |
224 | - @gote.write(sprintf("START:%s\n", @id)) | |
225 | - while(true) | |
226 | - @currnet_player = @sente | |
227 | - @next_player = @gote | |
228 | - handle_one_move(@currnet_player, @next_player) | |
229 | - | |
230 | - @currnet_player = @gote | |
231 | - @next_player = @sente | |
232 | - handle_one_move(@currnet_player, @next_player) | |
233 | - end | |
234 | - rescue ShogiWatchdogTimeout | |
235 | - sg_flag_of_timeout = $!.message | |
236 | - if (sg_flag_of_timeout == "+") | |
237 | - loser = @sente | |
238 | - winner = @gote | |
239 | - else | |
240 | - loser = @sente | |
241 | - winner = @gote | |
242 | - end | |
243 | - printf("watchdog timeout by %s\n", loser.name) | |
244 | - loser.write("#TIME_UP\n#LOSE\n") | |
245 | - winner.write("#TIME_UP\n#WIN\n") | |
246 | - rescue TimeoutError, ShogiTimeout | |
247 | - printf("%s: end timeup by %s\n", Time::new.to_s, @currnet_player.name) | |
248 | - @currnet_player.write("#TIME_UP\n#LOSE\n") | |
249 | - @next_player.write("#TIME_UP\n#WIN\n") | |
250 | - rescue ShogiReject | |
251 | - sender = $!.message | |
252 | - printf("%s: reject by %s\n", Time::new.to_s, sender) | |
253 | - str = sprintf("REJECT:%s by %s\n", @id, sender) | |
254 | - @sente.write(str) | |
255 | - @gote.write(str) | |
256 | - rescue ShogiIllegalMove | |
257 | - printf("%s: end illegal move by %s\n", Time::new.to_s, @currnet_player.name) | |
258 | - move = $!.message | |
259 | - @fh.printf("%%ERROR\n") | |
260 | - @currnet_player.write(sprintf("%s\n#ILLEGAL_MOVE\n#LOSE\n", move)) | |
261 | - @next_player.write(sprintf("%s\n#ILLEGAL_MOVE\n#WIN\n", move)) | |
262 | - rescue ShogiEnd | |
263 | - printf("%s: end by %s\n", Time::new.to_s, @currnet_player.name) | |
264 | - move = $!.message | |
265 | - case move | |
266 | - when "%TORYO" | |
267 | - @currnet_player.write(sprintf("%s\n#RESIGN\n#LOSE\n", move)) | |
268 | - @next_player.write(sprintf("%s\n#RESIGN\n#WIN\n", move)) | |
269 | - when "%KACHI" | |
270 | - @currnet_player.write(sprintf("%s\n#JISHOGI\n#WIN\n", move)) | |
271 | - @next_player.write(sprintf("%s\n#JISHOGI\n#LOSE\n", move)) | |
272 | - end | |
273 | 355 | end |
274 | - @fh.printf("'END_TIME:%s\n", Time::new.strftime("%Y/%m/%d %H:%M:%S")) | |
275 | - end | |
276 | - | |
277 | - def handle_one_move(current_player, next_player) | |
278 | - start_time = Time::new | |
279 | - str = current_player.get_move | |
280 | - @fh.printf("%s\n", str) | |
281 | - end_time = Time::new | |
282 | - time = (end_time - start_time).truncate | |
283 | - time = Least_Time_Per_Move if (time < Least_Time_Per_Move) | |
284 | - current_player.sub_time(time) | |
285 | - raise ShogiEnd, str if (str =~ /\A%/) | |
286 | - @sente.write(sprintf("%s,T%d\n", str, time)) | |
287 | - @gote.write(sprintf("%s,T%d\n", str, time)) | |
288 | - @fh.printf("T%s\n", time) | |
289 | - end | |
290 | - | |
291 | - def finish | |
292 | - @sente.finish | |
293 | - @gote.finish | |
294 | - @fh.close | |
295 | - printf("%s: end game %s %s %s\n", Time::new.to_s, @id, @sente.name, @gote.name) | |
296 | 356 | end |
297 | 357 | |
298 | - def start_message(sg_flag) | |
358 | + def propose_message(sg_flag) | |
299 | 359 | str = <<EOM |
300 | 360 | Protocol_Mode:Server |
301 | 361 | Format:Shogi 1.0 |
@@ -324,6 +384,8 @@ P9+KY+KE+GI+KI+OU+KI+GI+KE+KY | ||
324 | 384 | P+ |
325 | 385 | P- |
326 | 386 | + |
387 | +END Position | |
388 | +END Game_Summary | |
327 | 389 | EOM |
328 | 390 | return str |
329 | 391 | end |
@@ -425,7 +487,7 @@ def main | ||
425 | 487 | if (good_login?(str)) |
426 | 488 | player = Player::new(str, client) |
427 | 489 | if (LEAGUE.duplicated?(player)) |
428 | - client.write_safe(sprintf("username %s is already connected", player.name)) | |
490 | + client.write_safe(sprintf("username %s is already connected%s", player.name, eol)) | |
429 | 491 | next |
430 | 492 | end |
431 | 493 | LEAGUE.add(player) |