OSDN Git Service

[shogi-server] [mk_game_results] Change schema to add number of moves
[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   end
42
43   def update(game_result)
44     end_time_str = game_result.end_time.strftime("%Y/%m/%d %H:%M:%S")
45     black = game_result.black
46     white = game_result.white
47     black_name = black.rated? ? black.player_id : black.name
48     white_name = white.rated? ? white.player_id : white.name
49     msg = [end_time_str,
50            game_result.log_summary_type,
51            game_result.black_result,
52            black_name,
53            white_name,
54            game_result.white_result,
55            game_result.game.logfile,
56            game_result.game.board.move_count]
57     begin
58       # Note that this is proccessed in the gian lock.
59       File.open(@logfile, "a") do |f|
60         f << msg.join("\t") << "\n"
61       end
62     rescue => e
63       # ignore
64       $stderr.puts "Failed to write to the game result file: #{@logfile}" if $DEBUG
65     end
66   end
67 end
68
69 # Base abstract class for a game result.
70 # Imediate subclasses are GameResultWin and GameResultDraw.
71 #
72 class GameResult
73   include Observable
74
75   # Game object
76   attr_reader :game
77   # Array of players
78   attr_reader :players
79   # Black player object
80   attr_reader :black
81   # White plyer object
82   attr_reader :white
83   # Command to send monitors such as '%TORYO' etc...
84   attr_reader :result_type
85   # Result types to write the main log file such as 'toryo' etc... 
86   attr_reader :log_summary_type
87   # Time when the game ends
88   attr_reader :end_time
89
90   def initialize(game, p1, p2)
91     @game = game
92     @players = [p1, p2]
93     if p1.sente && !p2.sente
94       @black, @white = p1, p2
95     elsif !p1.sente && p2.sente
96       @black, @white = p2, p1
97     else
98       raise "Never reached!: %s p1: %s p2: %s" % [game.game_id, p1.sente, p2.sente]
99     end
100     @players.each do |player|
101       player.status = "connected"
102     end
103     @result_type = ""
104     @end_time = Time.now
105     regist_observers
106   end
107
108   def regist_observers
109     add_observer MonitorObserver.new
110     add_observer LoggingObserver.new
111
112     if League::Floodgate.game_name?(@game.game_name) &&
113        @game.sente.player_id && @game.gote.player_id
114       path = League::Floodgate.history_file_path(@game.game_name) 
115       history = League::Floodgate::History.factory(path)
116       add_observer history if history
117     end
118   end
119
120   def process
121     raise "Implement me!"
122   end
123
124   def notify
125     changed
126     notify_observers(self)
127   end
128
129   def log(str)
130     @game.log_game(str)
131   end
132
133   def log_board
134     log(@game.board.to_s.gsub(/^/, "\'").chomp)
135   end
136
137   def black_result
138     return "Implemet me!"
139   end
140
141   def white_result
142     return "Implemet me!"
143   end
144
145   def log_summary
146     log_board
147     log("'summary:%s:%s %s:%s %s" % [@log_summary_type, 
148                                      @black.name, black_result,
149                                      @white.name, white_result])
150   end
151 end
152
153 class GameResultWin < GameResult
154   attr_reader :winner, :loser
155
156   def initialize(game, winner, loser)
157     super
158     @winner, @loser = winner, loser
159     @winner.last_game_win = true
160     @loser.last_game_win  = false
161   end
162
163   def black_result
164     return @black == @winner ? "win" : "lose"
165   end
166
167   def white_result
168     return @black == @winner ? "lose" : "win"
169   end
170 end
171
172 class GameResultAbnormalWin < GameResultWin
173   def initialize(game, winner, loser)
174     super
175     @log_summary_type = "abnormal"
176     @result_type      = "%TORYO"
177   end
178
179   def process
180     @winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
181     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
182     log(@result_type)
183     log_summary
184     notify
185   end
186 end
187
188 class GameResultTimeoutWin < GameResultWin
189   def initialize(game, winner, loser)
190     super
191     @log_summary_type = "time up"
192     @result_type      = "#TIME_UP"
193   end
194
195   def process
196     @winner.write_safe("#TIME_UP\n#WIN\n")
197     @loser.write_safe( "#TIME_UP\n#LOSE\n")
198     log("%TIME_UP")  # a player in turn lost
199     log_summary
200     notify
201   end
202 end
203
204 # A player declares (successful) Kachi
205 class GameResultKachiWin < GameResultWin
206   def initialize(game, winner, loser)
207     super
208     @log_summary_type = "kachi"
209     @result_type      = "%KACHI"
210   end
211
212   def process
213     @winner.write_safe("%KACHI\n#JISHOGI\n#WIN\n")
214     @loser.write_safe( "%KACHI\n#JISHOGI\n#LOSE\n")
215     log(@result_type)
216     log_summary
217     notify
218   end
219 end
220
221 # A player declares wrong Kachi
222 class GameResultIllegalKachiWin < GameResultWin
223   def initialize(game, winner, loser)
224     super
225     @log_summary_type = "illegal kachi"
226     @result_type      = "%KACHI"
227   end
228
229   def process
230     @winner.write_safe("%KACHI\n#ILLEGAL_MOVE\n#WIN\n")
231     @loser.write_safe( "%KACHI\n#ILLEGAL_MOVE\n#LOSE\n")
232     log(@result_type)
233     log_summary
234     notify
235   end
236 end
237
238 class GameResultIllegalWin < GameResultWin
239   def initialize(game, winner, loser, cause)
240     super(game, winner, loser)
241     @log_summary_type = cause
242     @result_type      = "#ILLEGAL_MOVE"
243   end
244
245   def process
246     @winner.write_safe("#ILLEGAL_MOVE\n#WIN\n")
247     @loser.write_safe( "#ILLEGAL_MOVE\n#LOSE\n")
248     # no log
249     log_summary
250     notify
251   end
252 end
253
254 class GameResultIllegalMoveWin < GameResultIllegalWin
255   def initialize(game, winner, loser)
256     super(game, winner, loser, "illegal move")
257   end
258 end
259
260 class GameResultUchifuzumeWin < GameResultIllegalWin
261   def initialize(game, winner, loser)
262     super(game, winner, loser, "uchifuzume")
263   end
264 end
265
266 class GameResultOuteKaihiMoreWin < GameResultIllegalWin
267   def initialize(game, winner, loser)
268     super(game, winner, loser, "oute_kaihimore")
269   end
270 end
271
272 # This won't happen, though.
273 #
274 class GameResultOutoriWin < GameResultIllegalWin
275   def initialize(game, winner, loser)
276     super(game, winner, loser, "outori")
277   end
278 end
279
280 class GameResultToryoWin < GameResultWin
281   def initialize(game, winner, loser)
282     super
283     @log_summary_type = "toryo"
284     @result_type      = "%TORYO"
285   end
286
287   def process
288     @winner.write_safe("%TORYO\n#RESIGN\n#WIN\n")
289     @loser.write_safe( "%TORYO\n#RESIGN\n#LOSE\n")
290     log(@result_type)
291     log_summary
292     notify
293   end
294 end
295
296 class GameResultOuteSennichiteWin < GameResultWin
297   def initialize(game, winner, loser)
298     super
299     @log_summary_type = "oute_sennichite"
300     @result_type      = "#OUTE_SENNICHITE"
301   end
302
303   def process
304     @winner.write_safe("#OUTE_SENNICHITE\n#WIN\n")
305     @loser.write_safe( "#OUTE_SENNICHITE\n#LOSE\n")
306     # no log
307     log_summary
308     notify
309   end
310 end
311
312 # Draw
313 #
314 class GameResultDraw < GameResult
315   def initialize(game, p1, p2)
316     super
317     p1.last_game_win = false
318     p2.last_game_win = false
319   end
320   
321   def black_result
322     return "draw"
323   end
324
325   def white_result
326     return "draw"
327   end
328 end
329
330 class GameResultSennichiteDraw < GameResultDraw
331   def initialize(game, winner, loser)
332     super
333     @log_summary_type = "sennichite"
334     @result_type      = "#SENNICHITE"
335   end
336
337   def process
338     @players.each do |player|
339       player.write_safe("#SENNICHITE\n#DRAW\n")
340     end
341     log("%SENNICHITE")
342     log_summary
343     notify
344   end
345 end
346
347 class GameResultMaxMovesDraw < GameResultDraw
348   def initialize(game, winner, loser)
349     super
350     @log_summary_type = "max_moves"
351     @result_type      = "#MAX_MOVES"
352   end
353
354   def process
355     @players.each do |player|
356       player.write_safe("#MAX_MOVES\n#CENSORED\n")
357     end
358     # no log
359     log_summary
360     notify
361   end
362 end
363
364 end # ShogiServer