OSDN Git Service

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