OSDN Git Service

The utility programs have been moved under 'shogi-server'
[shogi-server/shogi-server.git] / utils / eval_graph.rb
1 #!/usr/bin/env 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 #      - 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
9 #    
10 #    Copyright (C) 2006  Daigo Moriwaki <daigo@debian.org>
11 #
12 #    Version: $Id$
13 #
14 #    This program is free software; you can redistribute it and/or modify
15 #    it under the terms of the GNU General Public License as published by
16 #    the Free Software Foundation; either version 2 of the License, or
17 #    (at your option) any later version.
18 #
19 #    This program is distributed in the hope that it will be useful,
20 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
21 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 #    GNU General Public License for more details.
23 #
24 #    You should have received a copy of the GNU General Public License
25 #    along with this program; if not, write to the Free Software
26 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
27
28 require 'pathname'
29 require 'getoptlong'
30 require 'rubygems'
31 require 'gnuplot'
32
33 def to_svg_file(csa_file)
34   "#{csa_file}.svg"
35 end
36
37 def reformat_svg(str)
38   str.gsub(%r!<svg.*?>!m, <<-END) 
39                  <svg viewBox="0 0 800 600" 
40                                               xmlns="http://www.w3.org/2000/svg" 
41                                                                 xmlns:xlink="http://www.w3.org/1999/xlink">
42                  END
43 end
44
45 module EvalGraph
46   def parse_comment(str)
47     return nil unless str
48     str.strip!
49     items = str.split(" ")
50     if items.size > 0
51       return items[0]
52     else
53       return nil
54     end
55   end
56   module_function :parse_comment
57   
58   class Player
59     attr_accessor :theOther
60     attr_reader   :name, :comments, :start_time
61     
62     def initialize(type)
63                         @comments = []
64       @type = type
65       @regexp_move = Regexp.new("^\\#{@type}\\d{4}\\w{2}")
66       @regexp_name = Regexp.new("^N\\#{@type}(.*)")
67       @flag = false
68       @name = nil
69     end
70
71     def reset
72       if @flag
73         @comments << nil
74       end
75       @flag = false
76     end
77
78     def <<(comment)
79       case comment
80       when @regexp_move
81         @flag = true
82         @theOther.reset
83       when /^'\*\*(.*)/
84         if @flag
85           @comments << EvalGraph::parse_comment($1)
86           @flag = false
87         end
88       when @regexp_name
89         @name = $1
90       when /\$START_TIME:(.*)/
91         @start_time = $1
92       end
93     end
94
95   end
96
97   class Black < Player
98     def name
99       @name ? "#{@name} (B)" : "black"
100     end
101
102     # Gluplot can not show nil vlaues so that a return value has to contain
103     # [[0], [0]] at least.
104     def eval_values
105       moves = []
106       comments.each_with_index do |c, i|
107         moves << i*2 + 1 if c
108       end
109       moves.unshift 0
110       [moves, comments.compact.unshift(0)]
111     end
112   end
113
114   class White < Player
115     def name
116       @name ? "#{@name} (W)" : "white"
117     end
118
119     def eval_values
120       moves = []
121       comments.each_with_index do |c, i|
122         moves << i*2 if c
123       end
124       moves.unshift 0
125       [moves, comments.compact.unshift(0)]
126     end
127   end
128
129   
130   def create_players
131     black = Black.new("+")
132     white = White.new("-")
133     black.theOther = white
134     white.theOther = black
135     return black,white
136   end
137   module_function :create_players
138 end
139
140
141 def plot(csa_file, title, black, white)
142   width = [black.comments.size, white.comments.size].max * 2 + 1
143   Gnuplot.open do |gp|
144     Gnuplot::Plot.new( gp ) do |plot|
145       plot.terminal "svg" # or png
146       plot.output   to_svg_file(csa_file)
147       
148       plot.title  title
149       plot.size   "ratio #{1/1.618}"
150       plot.xlabel "Moves"
151       plot.ylabel "Evaluation Value"
152       plot.xrange "[0:#{width}]"
153       plot.yrange "[-3000:3000]"
154       plot.xtics  "20"
155       plot.mxtics "2"
156       plot.ytics  %Q!("2000" 2000, "-2000" -2000)!
157       plot.xzeroaxis "lt -1"
158       plot.grid
159       plot.size   "0.9,0.9"
160       plot.key "left"
161      
162       plot.data << Gnuplot::DataSet.new( black.eval_values ) do |ds|
163         ds.with  = "lines"
164         ds.title = black.name
165       end
166       
167       plot.data << Gnuplot::DataSet.new( white.eval_values ) do |ds|
168         ds.with  = "lines"
169         ds.title = white.name
170       end
171       
172     end
173   end  
174 end
175
176
177
178 def read(lines, file_name)
179   lines.map! {|l| l.strip}
180   
181   black,white = EvalGraph.create_players
182   while l = lines.shift do
183     black << l
184     white << l
185   end
186   
187   title = "#{file_name}" 
188   plot(file_name, title, black, white)
189 end
190
191
192 if $0 == __FILE__
193   def usage
194     puts "Usage: #{$0} [--update] <csa_files>..."
195     puts "Options:"
196     puts "  --update        Update .svg files if exist."
197     exit 1
198   end
199
200   usage if ARGV.empty?
201
202   parser = GetoptLong.new
203   parser.set_options(['--update', GetoptLong::NO_ARGUMENT])
204   begin
205     parser.each_option do |name, arg|
206       eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
207     end
208   rescue
209     usage
210   end
211   
212   while file = ARGV.shift
213     next if !$OPT_UPDATE && File.exists?(to_svg_file(file))
214     read(Pathname.new(file).readlines, file)
215     str = reformat_svg(Pathname.new(to_svg_file(file)).read)
216     open(to_svg_file(file),"w+") {|f| f << str}
217   end
218 end