OSDN Git Service

[shogi-server] Bump up the revision to 20201206
[shogi-server/shogi-server.git] / shogi_server / game_result.rb
1 ## $Id$
2
3 ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
4 ## Copyright (C) 2007-2012 Daigo Moriwaki (daigo at debian dot org)
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 require 'observer'
21
22 module ShogiServer # for a namespace
23  
24 # MonitorObserver observes GameResult to send messages to the monitors
25 # watching the game
26 #
27 class MonitorObserver
28   def update(game_result)
29     game_result.game.each_monitor do |monitor_handler|
30       monitor_handler.write_safe(game_result.game.game_id, game_result.result_type)
31     end
32   end
33 end
34
35 # LoggingObserver appends a result of each game to a log file, which will
36 # be used to calculate rating scores of players.
37 #
38 class LoggingObserver
39   def initialize
40     @logfile           = File.join($league.dir, "00LIST")
41     @logfile_floodgate = File.join($league.dir, "00LIST.floodgate")
42     @logfile_others    = File.join($league.dir, "00LIST.others")
43   end
44
45   def update(game_result)
46     end_time_str = game_result.end_time.strftime("%Y/%m/%d %H:%M:%S")
47     black = game_result.black
48     white = game_result.white
49     black_name = black.rated? ? black.player_id : black.name
50     white_name = white.rated? ? white.player_id : white.name
51     msg = [end_time_str,
52            game_result.log_summary_type,
53            game_result.black_result,
54            black_name,
55            white_name,
56            game_result.white_result,
57            game_result.game.logfile,
58            game_result.game.board.move_count]
59     begin
60       files = [@logfile]
61       if League::Floodgate.game_name?(game_result.game.game_name)
62         files << @logfile_floodgate
63       else
64         files << @logfile_others
65       end
66       # Note that this is proccessed in the gian lock.
67       str = msg.join("\t")
68       files.each do |file|
69         File.open(file, "a") do |f|
70           f.puts str
71         end
72       end
73     rescue => e
74       # ignore
75       $stderr.puts "Failed to write to the game result file: #{@logfile}" if $DEBUG
76     end
77   end
78 end
79
80 # Base abstract class for a game result.
81 # Imediate subclasses are GameResultWin and GameResultDraw.
82 #
83 class GameResult
84   include Observable
85
86   # Game object
87   attr_reader :game
88   # Array of players
89   attr_reader :players
90   # Black player object
91   attr_reader :black
92   # White plyer object
93   attr_reader :white
94   # Command to send monitors such as '%TORYO' etc...
95   attr_reader :result_type
96   # Result types to write the main log file such as 'toryo' etc... 
97   attr_reader :log_summary_type
98   # Time when the game ends
99   attr_reader :end_time
100
101   def initialize(game, p1, p2)
102     @game = game
103     @players = [p1, p2]
104     if p1.sente && !p2.sente
105       @black, @white = p1, p2
106     elsif !p1.sente && p2.sente
107       @black, @white = p2, p1
108     else
109       raise "Never reached!: %s p1: %s p2: %s" % [game.game_id, p1.sente, p2.sente]
110     end
111     @players.each do |player|
112       player.status = "connected"
113     end
114     @result_type = ""
115     @end_time = Time.now
116     regist_observers
117   end
118
119   def regist_observers
120     add_observer MonitorObserver.new
121     add_observer LoggingObserver.new
122
123     if League::Floodgate.game_name?(@game.game_name) &&
124        @game.sente.player_id && @game.gote.player_id
125       path = League::Floodgate.history_file_path(@game.game_name) 
126       history = League::Floodgate::History.factory(path)
127       add_observer history if history
128     end
129   end
130
131   def process
132     raise "Implement me!"
133   end
134
135   def notify
136     changed
137     notify_observers(self)
138   end
139
140   def log(str)
141     @game.log_game(str)
142   end
143
144   def log_board
145     log(@game.board.to_s.gsub(/^/, "\'").chomp)
146   end
147
148   def black_result
149     return "Implemet me!"
150   end
151
152   def white_result
153     return "Implemet me!"
154   end
155
156   def log_summary
157     log_board
158     log("'summary:%s:%s %s:%s %s" % [@log_summary_type, 
159                                      @black.name, black_result,
160                                      @white.name, white_result])
161   end
162 end
163
164 class GameResultWin < GameResult
165   attr_reader :winner, :loser
166
167   def initialize(game, winner, loser)
168     super
169     @winner, @loser = winner, loser
170     @winner.last_game_win = true
171     @loser.last_game_win  = false
172   end
173
174   def black_result
175     return @black == @winner ? "win" : "lose"
176   end
177
178   def white_result
179     return @black == @winner ? "lose" : "win"
180   end
181 end
182
183 class GameResultAbnormalWin < GameResultWin
184   def initialize(game, winner, loser)
185     super
186     @log_summary_type = "abnormal"
187     @result_type      = "%TORYO"
188   end
189
190   def process
191     @winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
192     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
193     log(@result_type)
194     log_summary
195     notify
196   end
197 end
198
199 class GameResultTimeoutWin < GameResultWin
200   def initialize(game, winner, loser)
201     super
202     @log_summary_type = "time up"
203     @result_type      = "#TIME_UP"
204   end
205
206   def process
207     @winner.write_safe("#TIME_UP\n#WIN\n")
208     @loser.write_safe( "#TIME_UP\n#LOSE\n")
209     log("%TIME_UP")  # a player in turn lost
210     log_summary
211     notify
212   end
213 end
214
215 # A player declares (successful) Kachi
216 class GameResultKachiWin < GameResultWin
217   def initialize(game, winner, loser)
218     super
219     @log_summary_type = "kachi"
220     @result_type      = "%KACHI"
221   end
222
223   def process
224     @winner.write_safe("%KACHI\n#JISHOGI\n#WIN\n")
225     @loser.write_safe( "%KACHI\n#JISHOGI\n#LOSE\n")
226     log(@result_type)
227     log_summary
228     notify
229   end
230 end
231
232 # A player declares wrong Kachi
233 class GameResultIllegalKachiWin < GameResultWin
234   def initialize(game, winner, loser)
235     super
236     @log_summary_type = "illegal kachi"
237     @result_type      = "%KACHI"
238   end
239
240   def process
241     @winner.write_safe("%KACHI\n#ILLEGAL_MOVE\n#WIN\n")
242     @loser.write_safe( "%KACHI\n#ILLEGAL_MOVE\n#LOSE\n")
243     log(@result_type)
244     log_summary
245     notify
246   end
247 end
248
249 class GameResultIllegalWin < GameResultWin
250   def initialize(game, winner, loser, cause)
251     super(game, winner, loser)
252     @log_summary_type = cause
253     @result_type      = "#ILLEGAL_MOVE"
254   end
255
256   def process
257     @winner.write_safe("#ILLEGAL_MOVE\n#WIN\n")
258     @loser.write_safe( "#ILLEGAL_MOVE\n#LOSE\n")
259     # no log
260     log_summary
261     notify
262   end
263 end
264
265 class GameResultIllegalMoveWin < GameResultIllegalWin
266   def initialize(game, winner, loser)
267     super(game, winner, loser, "illegal move")
268   end
269 end
270
271 class GameResultUchifuzumeWin < GameResultIllegalWin
272   def initialize(game, winner, loser)
273     super(game, winner, loser, "uchifuzume")
274   end
275 end
276
277 class GameResultOuteKaihiMoreWin < GameResultIllegalWin
278   def initialize(game, winner, loser)
279     super(game, winner, loser, "oute_kaihimore")
280   end
281 end
282
283 # This won't happen, though.
284 #
285 class GameResultOutoriWin < GameResultIllegalWin
286   def initialize(game, winner, loser)
287     super(game, winner, loser, "outori")
288   end
289 end
290
291 class GameResultToryoWin < GameResultWin
292   def initialize(game, winner, loser)
293     super
294     @log_summary_type = "toryo"
295     @result_type      = "%TORYO"
296   end
297
298   def process
299     @winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
300     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
301     log(@result_type)
302     log_summary
303     notify
304   end
305 end
306
307 class GameResultOuteSennichiteWin < GameResultWin
308   def initialize(game, winner, loser)
309     super
310     @log_summary_type = "oute_sennichite"
311     @result_type      = "#OUTE_SENNICHITE"
312   end
313
314   def process
315     @winner.write_safe("#OUTE_SENNICHITE\n#WIN\n")
316     @loser.write_safe( "#OUTE_SENNICHITE\n#LOSE\n")
317     # no log
318     log_summary
319     notify
320   end
321 end
322
323 # Draw
324 #
325 class GameResultDraw < GameResult
326   def initialize(game, p1, p2)
327     super
328     p1.last_game_win = false
329     p2.last_game_win = false
330   end
331   
332   def black_result
333     return "draw"
334   end
335
336   def white_result
337     return "draw"
338   end
339 end
340
341 class GameResultSennichiteDraw < GameResultDraw
342   def initialize(game, winner, loser)
343     super
344     @log_summary_type = "sennichite"
345     @result_type      = "#SENNICHITE"
346   end
347
348   def process
349     @players.each do |player|
350       player.write_safe("#SENNICHITE\n#DRAW\n")
351     end
352     log("%SENNICHITE")
353     log_summary
354     notify
355   end
356 end
357
358 class GameResultMaxMovesDraw < GameResultDraw
359   def initialize(game, winner, loser)
360     super
361     @log_summary_type = "max_moves"
362     @result_type      = "#MAX_MOVES"
363   end
364
365   def process
366     @players.each do |player|
367       player.write_safe("#MAX_MOVES\n#CENSORED\n")
368     end
369     # no log
370     log_summary
371     notify
372   end
373 end
374
375 end # ShogiServer