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-2008 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(/^'\*\*(.*)/)
104 @comments << EvalGraph::parse_comment($1)
109 when /\$START_TIME:(.*)/
114 # Return times for each move which the player played.
115 # return[0] is the initial play_time.
117 def time_values(y_max, play_time)
120 values << 1.0*y_max/play_time*consume
129 values << 1.0*y_max/play_time*consume
137 @name ? "#{@name} (B)" : "black"
140 # Gluplot can not show nil vlaues so that a return value has to contain
141 # [[0], [0]] at least.
144 comments.each_with_index do |c, i|
148 [moves, comments.compact.unshift(0)]
151 # Return moves and times. For example, [[0,1,3], [900, 899, 898]]
152 def time_values(y_max, play_time)
155 return [moves, values] if values.size <= 1
158 values[1, values.size-1].each do |v|
162 return [moves, values]
168 @name ? "#{@name} (W)" : "white"
173 comments.each_with_index do |c, i|
177 [moves, comments.compact.unshift(0)]
180 def time_values(y_max, play_time)
183 return [moves, values] if values.size <= 1
186 values[1, values.size-1].each do |v|
190 return [moves, values]
196 black = Black.new("+")
197 white = White.new("-")
198 black.theOther = white
199 white.theOther = black
202 module_function :create_players
203 end # module EvalGraph
206 def plot(csa_file, title, black, white, a_play_time)
207 width = [black.comments.size, white.comments.size].max * 2 + 1
209 Gnuplot::Plot.new( gp ) do |plot|
210 plot.terminal "svg size 800 500 fixed" # or png
211 plot.output to_svg_file(csa_file)
214 plot.size "ratio #{1/1.618}"
216 plot.ylabel "Evaluation Value"
217 plot.xrange "[0:#{width}]"
218 plot.yrange "[-3000:3000]"
221 plot.ytics %Q!("2000" 2000, "-2000" -2000)!
222 plot.xzeroaxis "lt -1"
227 plot.style "line 1 linewidth 5 linetype 0 linecolor rgbcolor \"red\""
228 plot.style "line 2 linewidth 4 linetype 0 linecolor rgbcolor \"dark-green\""
230 plot.data << Gnuplot::DataSet.new( black.eval_values ) do |ds|
231 ds.with = "lines ls 1"
232 ds.title = black.name
235 plot.data << Gnuplot::DataSet.new( white.eval_values ) do |ds|
236 ds.with = "lines ls 2"
237 ds.title = white.name
241 plot.style "line 5 linewidth 1 linetype 0 linecolor rgbcolor \"red\""
242 plot.style "line 6 linewidth 1 linetype 0 linecolor rgbcolor \"green\""
243 plot.style "fill solid 0.25 noborder"
245 plot.data << Gnuplot::DataSet.new( black.time_values(3000, a_play_time) ) do |ds|
246 ds.with = "boxes notitle ls 5"
249 plot.data << Gnuplot::DataSet.new( white.time_values(-3000, a_play_time) ) do |ds|
250 ds.with = "boxes notitle ls 6"
259 # Read kifu, a record of moves, to generate a graph file.
260 # lines are contents of the kifu.
261 # file is a file name of a genrating image file.
262 # original_file_name is a file name of the csa file.
264 def read(lines, file_name, original_file_name=nil)
265 lines.map! {|l| l.strip}
266 original_file_name ||= file_name
268 black,white = EvalGraph.create_players
269 while l = lines.shift do
274 title = "#{file_name}"
275 a_play_time = play_time(original_file_name)
276 plot(file_name, title, black, white, a_play_time)
282 puts "Usage: #{$0} [--update] <csa_files>..."
284 puts " --update Update .svg files if exist."
290 parser = GetoptLong.new
291 parser.set_options(['--update', GetoptLong::NO_ARGUMENT])
293 parser.each_option do |name, arg|
294 eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
300 while file = ARGV.shift
301 next if !$OPT_UPDATE && File.exists?(to_svg_file(file))
302 read(Pathname.new(file).readlines, file)
303 str = reformat_svg(Pathname.new(to_svg_file(file)).read)
304 open(to_svg_file(file),"w+") {|f| f << str}