OSDN Git Service

Merge branch 'logger'
[shogi-server/shogi-server.git] / utils / players_graph.rb
1 #!/usr/bin/ruby
2 #    This generates graphs of evaluation values from comments in CSA files.
3 #    Ruby libraries that are required: 
4 #      - RubyGems: http://rubyforge.org/projects/rubygems/
5 #                  On Debian, $ sudo install rubygems1.8
6 #      - gnuplot:  http://rubyforge.org/projects/rgplot/
7 #                  On Debian, $ sudo gem install gnuplot
8 #    OS librariles that is required:
9 #      - Gnuplot:  http://www.gnuplot.info/
10 #                  On Debian, $ sudo apt-get install gnuplot
11 #    
12 #    Copyright (C) 2008  Daigo Moriwaki <daigo@debian.org>
13 #
14 #    Version: $Id$
15 #
16 #    This program is free software; you can redistribute it and/or modify
17 #    it under the terms of the GNU General Public License as published by
18 #    the Free Software Foundation; either version 2 of the License, or
19 #    (at your option) any later version.
20 #
21 #    This program is distributed in the hope that it will be useful,
22 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
23 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 #    GNU General Public License for more details.
25 #
26 #    You should have received a copy of the GNU General Public License
27 #    along with this program; if not, write to the Free Software
28 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
29
30 require 'pathname'
31 require 'getoptlong'
32 require 'yaml'
33 require 'date'
34 require 'set'
35 require 'logger'
36 require 'rubygems'
37 require 'gnuplot'
38
39 $log = Logger.new(STDERR)
40 $log.level = Logger::WARN
41 $players = {}
42
43 class Format
44   attr_reader :ext, :size
45   def initialize(root)
46     @root = root
47     @size = "1,1"
48   end
49 end
50
51 class LargeFormat < Format
52   def initialize(root)
53     super
54   end
55
56   def apply(plot)
57     plot.terminal @ext
58     plot.size     @size
59     plot.format 'x "%y/%m/%d"'
60     plot.ytics  "100"
61     plot.mytics "5"
62     plot.xlabel  "Date"
63     plot.ylabel  "Rate"
64   end
65 end
66
67 class LargePngFormat < LargeFormat
68   def initialize(root)
69     super
70     @ext = "png"
71   end
72
73   def to_image_file(name)
74     return File.join(@root, "#{name}-large.#{ext}")
75   end
76 end
77
78 class SmallPngFormat < Format
79   def initialize(root)
80     super
81     @ext = "png"
82   end
83
84   def to_image_file(name)
85     return File.join(@root, "#{name}-small.#{ext}")
86   end
87
88   def apply(plot)
89     plot.terminal "png size 320,200 crop"
90     plot.format   'x "%b"'
91     plot.ytics    "200"
92     plot.mytics   "2"
93   end
94 end
95
96 class SvgFormat < LargeFormat
97   def initialize(root)
98     super
99     @ext = "svg"
100   end
101
102   def to_image_file(name)
103     return File.join(@root, "#{name}.#{ext}")
104   end
105 end
106
107 def plot(format, name, dates, rates, rdates, rrates)
108   Gnuplot.open do |gp|
109     Gnuplot::Plot.new( gp ) do |plot|
110       format.apply(plot)
111       plot.title  name
112       plot.output format.to_image_file(name)
113       
114       #plot.size    "ratio #{1/1.618}"
115       plot.xdata   "time"
116       plot.timefmt '"%Y-%m-%d"'
117       plot.xrange  "[\"%s\":\"%s\"]" % 
118                     [dates.first.strftime("%Y-%m-%d"),
119                      dates.last.strftime("%Y-%m-%d")]
120       ymin = ((rates + rrates).min/50) * 50
121       ymax = ((rates + rrates).max/50 + 1) * 50
122       plot.yrange "[%s:%s]" % [ymin, ymax]
123       plot.grid
124       data = []
125       data << Gnuplot::DataSet.new([dates, rates]) do |ds|
126                 ds.using = "1:2"
127                 ds.with  = "lines"
128                 ds.title = "original"
129               end
130       if !rdates.empty?
131         data << Gnuplot::DataSet.new([rdates, rrates]) do |ds|
132                   ds.using = "1:2"
133                   ds.with  = "lines"
134                   ds.title = "relative (rate24)"
135                 end
136       end
137       plot.data = data
138     end
139   end  
140 end
141
142 def load_file(file_name)
143   if /^.*-(\d{8}).yaml$/ =~ file_name
144     date = Date::parse($1)
145   else
146     $log.error("Invalid file name: %s" % [file_name])
147     return
148   end
149   db = YAML::load_file(file_name)
150   unless db['players'] && db['players'][0]
151     $log.error("Invalid file format: %s" % [file_name])
152     return
153   end
154   db['players'][0].each do |name, hash|
155     $players[name] ||= {}
156     $players[name][date] = hash['rate'].to_i
157   end
158 end
159
160 def empty_file?(file)
161   if !FileTest.exists?(file)
162     $log.error("Could not find the file: %s" % [file])
163     return true
164   end
165   if FileTest.zero?(file)
166     $log.error("Empty file: %s" % [file])
167     return true
168   end
169
170   return false
171 end
172
173 if $0 == __FILE__
174   def usage
175     puts "Usage: #{$0} --output-dir dir <players_yaml_file> <players_yaml_file>..."
176     puts "Options:"
177     puts "  --output-dir dir  Images will be located in the dir."
178     exit 1
179   end
180
181   usage if ARGV.empty?
182
183   parser = GetoptLong.new
184   parser.set_options(['--output-dir', '-o', GetoptLong::REQUIRED_ARGUMENT])
185   begin
186     parser.each_option do |name, arg|
187       eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
188     end
189   rescue
190     usage
191   end
192   usage if !$OPT_OUTPUT_DIR || ARGV.size < 2
193   
194   while file = ARGV.shift
195     next if empty_file?(file)
196     load_file(file)
197   end
198
199   if !$players || $players.empty?
200     exit 0 
201   end
202   
203   formats = [LargePngFormat.new($OPT_OUTPUT_DIR),
204              SmallPngFormat.new($OPT_OUTPUT_DIR),
205              SvgFormat.new($OPT_OUTPUT_DIR)]
206
207   $players.each do |name, hash|
208     dates, rates = hash.sort.transpose
209     rdates = dates.find_all do |date| 
210       $players["YSS+707d4f98d9d2620cdaab58f19d02a2e4"] &&
211       $players["YSS+707d4f98d9d2620cdaab58f19d02a2e4"][date] 
212     end
213     rrates = rdates.map do |date|
214       2300 - $players["YSS+707d4f98d9d2620cdaab58f19d02a2e4"][date] + hash[date]
215     end
216     formats.each do |format|
217       plot(format, name, dates, rates, rdates, rrates)
218     end
219   end
220 end
221