OSDN Git Service

4f6ac2bb6fd253e6aa5593ae5a5c8d0c76305374
[shogi-server/shogi-server.git] / shogi_server / league / floodgate.rb
1 require 'thread'
2 require 'ostruct'
3 require 'pathname'
4
5 module ShogiServer
6
7 class League
8   class Floodgate
9     class << self
10       # "floodgate-900-0"
11       #
12       def game_name?(str)
13         return /^floodgate\-\d+\-\d+$/.match(str) ? true : false
14       end
15     end
16
17     attr_reader :next_time, :league
18
19     def initialize(league, hash={})
20       @league = league
21       @next_time = hash[:next_time] || nil
22       @game_name = hash[:game_name] || "floodgate-900-0"
23       charge
24     end
25
26     def game_name?(str)
27       return Regexp.new(@game_name).match(str) ? true : false
28     end
29
30     def charge
31       now = Time.now
32       unless $DEBUG
33         # each 30 minutes
34         if now.min < 30
35           @next_time = Time.mktime(now.year, now.month, now.day, now.hour, 30)
36         else
37           @next_time = Time.mktime(now.year, now.month, now.day, now.hour) + 3600
38         end
39       else
40         # for test, each 30 seconds
41         if now.sec < 30
42           @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min, 30)
43         else
44           @next_time = Time.mktime(now.year, now.month, now.day, now.hour, now.min) + 60
45         end
46       end
47     end
48
49     def match_game
50       players = @league.find_all_players do |pl|
51         pl.status == "game_waiting" &&
52         game_name?(pl.game_name) &&
53         pl.sente == nil
54       end
55       Pairing.match(players)
56     end
57
58
59     #
60     #
61     class History
62       @@mutex = Mutex.new
63
64       class << self
65         def factory
66           file = Pathname.new $options["floodgate-history"]
67           history = History.new file
68           history.load
69           return history
70         end
71       end
72
73       attr_reader :records
74
75       # Initialize this instance.
76       # @param file_path_name a Pathname object for this storage
77       #
78       def initialize(file_path_name)
79         @records = []
80         @max_records = 100
81         @file = file_path_name
82       end
83
84       # Return a hash describing the game_result
85       # :game_id: game id
86       # :black:   Black's player id
87       # :white:   White's player id
88       # :winner:  Winner's player id or nil for the game without a winner
89       # :loser:   Loser's player id or nil for the game without a loser
90       #
91       def make_record(game_result)
92         hash = Hash.new
93         hash[:game_id] = game_result.game.game_id
94         hash[:black]   = game_result.black.player_id
95         hash[:white]   = game_result.white.player_id
96         case game_result
97         when GameResultWin
98           hash[:winner] = game_result.winner.player_id
99           hash[:loser]  = game_result.loser.player_id
100         else
101           hash[:winner] = nil
102           hash[:loser]  = nil
103         end
104         return hash
105       end
106
107       def load
108         return unless @file.exist?
109
110         @records = YAML.load_file(@file)
111         unless @records && @records.instance_of?(Array)
112           $logger.error "%s is not a valid yaml file. Instead, an empty array will be used and updated." % [@file]
113           @records = []
114         end
115       end
116
117       def save
118         begin
119           @file.open("w") do |f| 
120             f << YAML.dump(@records)
121           end
122         rescue Errno::ENOSPC
123           # ignore
124         end
125       end
126
127       def update(game_result)
128         record = make_record(game_result)
129         @@mutex.synchronize do 
130           load
131           @records << record
132           while @records.size > @max_records
133             @records.shift
134           end
135           save
136         end
137       end
138       
139       def last_win?(player_id)
140         rc = last_valid_game(player_id)
141         return false unless rc
142         return rc[:winner] == player_id
143       end
144       
145       def last_lose?(player_id)
146         rc = last_valid_game(player_id)
147         return false unless rc
148         return rc[:loser] == player_id
149       end
150
151       def last_valid_game(player_id)
152         records = nil
153         @@mutex.synchronize do
154           records = @records.reverse
155         end
156         rc = records.find do |rc|
157           rc[:winner] && 
158           rc[:loser]  && 
159           (rc[:black] == player_id || rc[:white] == player_id)
160         end
161         return rc
162       end
163     end # class History
164
165
166   end # class Floodgate
167
168
169 end # class League
170 end # module ShogiServer