OSDN Git Service

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