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 # * rgplot: http://rubyforge.org/projects/rgplot/
6 # OS librariles that is required:
7 # * Gnuplot: http://www.gnuplot.info/
8 # * On Debian, $ sudo apt-get install gnuplot
10 # Author:: Daigo Moriwaki <daigo at debian dot org>
11 # Copyright:: Copyright (C) 2006-2012 Daigo Moriwaki <daigo at debian dot org>
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.
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.
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
36 def to_svg_file(csa_file)
41 str.gsub(%r!<svg.*?>!m, <<-END)
42 <svg viewBox="0 0 800 600" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
46 # Parse play time from the game_name, then return it. If the game_name is
47 # not valid, return 0.
49 def play_time(game_name)
50 if /.*?\+.*?\-(\d*?)\-/ =~ game_name
57 def parse_comment(str)
60 items = str.split(" ")
67 module_function :parse_comment
70 attr_accessor :theOther
71 attr_reader :name, :comments, :start_time
78 @regexp_move = Regexp.new("^\\#{@type}\\d{4}\\w{2}")
79 @regexp_name = Regexp.new("^N\\#{@type}(.*)")
80 @regexp_time = Regexp.new(/^T(\d+)/)
81 @regexp_comment = Regexp.new(/^'\*\*(.*)/)
105 @comments << EvalGraph::parse_comment($1)
110 when /\$START_TIME:(.*)/
112 when /^'Increment:(.*)/
117 # Return times for each move which the player played.
118 # return[0] is the initial play_time.
120 def time_values(y_max, play_time)
123 values << 1.0*y_max/play_time*consume
125 consume += @increment
133 values << 1.0*y_max/play_time*consume
141 @name ? "#{@name} (B)" : "black"
144 # Gluplot can not show nil vlaues so that a return value has to contain
145 # [[0], [0]] at least.
148 comments.each_with_index do |c, i|
152 [moves, comments.compact.unshift(0)]
155 # Return moves and times. For example, [[0,1,3], [900, 899, 898]]
156 def time_values(y_max, play_time)
159 return [moves, values] if values.size <= 1
162 values[1, values.size-1].each do |v|
166 return [moves, values]
172 @name ? "#{@name} (W)" : "white"
177 comments.each_with_index do |c, i|
181 [moves, comments.compact.unshift(0)]
184 def time_values(y_max, play_time)
187 return [moves, values] if values.size <= 1
190 values[1, values.size-1].each do |v|
194 return [moves, values]
200 black = Black.new("+")
201 white = White.new("-")
202 black.theOther = white
203 white.theOther = black
206 module_function :create_players
207 end # module EvalGraph
210 def plot(csa_file, title, black, white, a_play_time)
211 width = [black.comments.size, white.comments.size].max * 2 + 1
213 Gnuplot::Plot.new( gp ) do |plot|
214 plot.terminal "svg size 800 500 fixed" # or png
215 plot.output to_svg_file(csa_file)
218 plot.size "ratio #{1/1.618}"
220 plot.ylabel "Evaluation Value"
221 plot.xrange "[0:#{width}]"
222 plot.yrange "[-3000:3000]"
225 plot.ytics %Q!("2000" 2000, "-2000" -2000)!
226 plot.xzeroaxis "lt -1"
231 plot.style "line 1 linewidth 5 linetype -1 linecolor rgbcolor \"red\""
232 plot.style "line 2 linewidth 4 linetype -1 linecolor rgbcolor \"dark-green\""
234 plot.data << Gnuplot::DataSet.new( black.eval_values ) do |ds|
235 ds.with = "lines ls 1"
236 ds.title = black.name
239 plot.data << Gnuplot::DataSet.new( white.eval_values ) do |ds|
240 ds.with = "lines ls 2"
241 ds.title = white.name
245 plot.style "line 5 linewidth 1 linetype -1 linecolor rgbcolor \"red\""
246 plot.style "line 6 linewidth 1 linetype -1 linecolor rgbcolor \"green\""
247 plot.style "fill solid 0.25 noborder"
249 plot.data << Gnuplot::DataSet.new( black.time_values(3000, a_play_time) ) do |ds|
250 ds.with = "boxes notitle ls 5"
253 plot.data << Gnuplot::DataSet.new( white.time_values(-3000, a_play_time) ) do |ds|
254 ds.with = "boxes notitle ls 6"
263 # Read kifu, a record of moves, to generate a graph file.
264 # lines are contents of the kifu.
265 # file is a file name of a genrating image file.
266 # original_file_name is a file name of the csa file.
268 def read(lines, file_name, original_file_name=nil)
269 lines.map! {|l| l.strip}
270 original_file_name ||= file_name
272 black,white = EvalGraph.create_players
273 while l = lines.shift do
278 title = "#{file_name}"
279 a_play_time = play_time(original_file_name)
280 plot(file_name, title, black, white, a_play_time)
286 puts "Usage: #{$0} [--update] <csa_files>..."
288 puts " --update Update .svg files if exist."
294 parser = GetoptLong.new
295 parser.set_options(['--update', GetoptLong::NO_ARGUMENT])
297 parser.each_option do |name, arg|
298 eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
304 while file = ARGV.shift
305 next if !$OPT_UPDATE && File.exists?(to_svg_file(file))
306 read(Pathname.new(file).readlines, file)
307 str = reformat_svg(Pathname.new(to_svg_file(file)).read)
308 open(to_svg_file(file),"w+") {|f| f << str}