OSDN Git Service

Merge remote-tracking branch 'origin/wdoor-stable'
[shogi-server/shogi-server.git] / utils / statistics.rb
1 #!/usr/bin/ruby
2 # This program shows statistics of CSA kifu files like following: 
3 #   - Monthly #games and #players
4 #   - Game results
5 #   - Time of each move
6 #   - Time of each game
7 #   - Moves of each game
8 #
9 # Sample command line:
10 #   $ ./statistics.rb /dev/shm/floodgate
11 #
12 # Author::    Daigo Moriwaki <daigo at debian dot org>
13 # Copyright:: Copyright (C) 2009-2012 Daigo Moriwaki <daigo at debian dot org>
14 #
15 # $Id$
16 #
17 #--
18 # This program is free software; you can redistribute it and/or modify
19 # it under the terms of the GNU General Public License as published by
20 # the Free Software Foundation; either version 2 of the License, or
21 # (at your option) any later version.
22 #
23 # This program is distributed in the hope that it will be useful,
24 # but WITHOUT ANY WARRANTY; without even the implied warranty of
25 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26 # GNU General Public License for more details.
27 #
28 # You should have received a copy of the GNU General Public License
29 # along with this program; if not, write to the Free Software
30 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
31 #++
32
33 $:.unshift File.dirname(__FILE__)
34 require 'csa-filter'
35 require 'set'
36
37 class Monthly
38   def initialize
39     @games = Hash.new {|hash,key| hash[key] = 0}
40     @players = Hash.new {|hash,key| hash[key] = Set.new}
41   end
42
43   def add(csa)
44     st = csa.start_time
45     month = st.strftime("%Y%m")
46
47     @games[month] += 1
48
49     [csa.black_id, csa.white_id].each do |player|
50       @players[month].add(player)
51     end
52   end
53
54   def print
55     puts "YYYYMM\t#games\t#players"
56     @games.sort {|a,b| a[0] <=> b[0]}.each do |key,value|
57       puts "%s\t% 6d\t% 2d" % [key, value, @players[key].size]
58     end
59   end
60 end
61
62 class Values
63   def initialize
64     @v = []
65   end
66
67   def add(value)
68     case value
69     when Array 
70      @v.concat value
71     else
72       @v << value
73     end
74   end
75
76   def print(file)
77     total = @v.inject(0){|sum, item| sum+item}
78     avg   = 1.0*total/@v.size
79     puts "avg: %f sec (size: %d)" % [avg, @v.size]
80
81     File.open(file, "w") do |f|
82       @v.each {|v| f.puts v}
83     end
84   end
85 end
86
87 class State
88   def initialize
89     @hash = Hash.new {|hash,key| hash[key] = 0}
90   end
91
92   def add(value)
93     if value.nil? || value.empty?
94       value = "error"
95     end
96     @hash[value] += 1
97   end
98
99   def print
100     puts "status\t#games"
101     @hash.sort {|a,b| b[1] <=> a[1]}.each do |key, value|
102       puts "%s\t% 6d" % [key, value]
103     end
104   end
105 end
106
107 $monthly  = Monthly.new
108 $gametime = Values.new
109 $movetime = Values.new
110 $moves    = Values.new
111 $states   = State.new
112
113 def do_file(file)
114   $OPT_REPEAT -= 1 if $OPT_REPEAT > 0
115   csa = CsaFileReader.new(file)
116
117   # See games between 2008/03 to 2009/07
118   return if csa.start_time.nil? ||
119             csa.start_time <  Time.parse("2008/03/01") ||
120             csa.start_time >= Time.parse("2009/08/01")
121
122   # Want to see complete games
123   $states.add csa.state
124   return unless csa.state == "toryo"
125
126   # Process monthly
127   $monthly.add(csa)
128
129   # Process gametime
130   duration = (csa.end_time - csa.start_time).to_i
131   if duration > 2200
132     $stderr.puts "Too long game: #{file}"
133     return
134   end
135   $gametime.add duration.to_i
136
137   # Process movetime
138   values = csa.movetimes
139   $movetime.add values
140
141   #Process moves
142   $moves.add values.size
143
144 rescue => ex
145   $stderr.puts "ERROR: %s" % [file]
146   throw ex
147 end
148
149 if $0 == __FILE__
150   def usage
151     puts "Usage: #{$0} [OPTIONS] dir [...]"
152     puts "Options:"
153     exit 1
154   end
155
156   usage if ARGV.empty?
157
158   parser = GetoptLong.new(
159              ['--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT]
160            )
161   begin
162     parser.each_option do |name, arg|
163       eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
164     end
165   rescue
166     usage
167   end
168
169   $OPT_REPEAT = $OPT_REPEAT.to_i
170   if $OPT_REPEAT == 0
171     $OPT_REPEAT = -1
172   end
173
174   while (cmd = ARGV.shift)
175
176     if FileTest.directory?(cmd)
177       Dir.glob(File.join(cmd, "**", "*.csa")).each do |file|
178         break if $OPT_REPEAT == 0
179         do_file(file)
180       end
181     elsif FileTest.file?(cmd)
182       break if $OPT_REPEAT == 0
183       do_file(cmd)
184     else
185       throw "Unknown file or directory: #{cmd}"
186     end
187
188     puts "States"
189     puts "------"
190     $states.print
191     puts
192     puts "=== Toryo Games ==="
193     puts
194     puts "Montly"
195     puts "------"
196     $monthly.print
197     puts
198     puts "Play Time"
199     puts "---------"
200     $gametime.print("gametime.dat")
201     puts
202     puts "Move Time"
203     puts "---------"
204     $movetime.print("movetime.dat")
205     puts
206     puts "Moves"
207     puts "-----"
208     $moves.print("moves.dat")
209   end
210 end
211