OSDN Git Service

deadf2149f01fcc251d7c473c7c760c0dc603a51
[shogi-server/shogi-server.git] / utils / csa-filter.rb
1 #!/usr/bin/ruby
2 # This program filters CSA files. For example, if you want only CSA files
3 # played by GPS vs Bonanza,
4 #   $ ./csa-filter.rb --players gps-l,bonanza some_dir
5 # you will see such files under the some_dir directory.
6 #
7 # Author::    Daigo Moriwaki <daigo at debian dot org>
8 # Copyright:: Copyright (C) 2006-2017 Daigo Moriwaki <daigo at debian dot org>
9 #
10 # $Id$
11 #
12 #--
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
26 #++
27
28 require 'time'
29 require 'pathname'
30 require 'getoptlong'
31 require 'nkf'
32
33 class CsaFileReader
34   WIN_MARK  = "win"
35   LOSS_MARK = "lose"
36   DRAW_MARK = "draw"
37
38   attr_reader :file_name
39   attr_reader :str
40   attr_reader :black_name, :white_name
41   attr_reader :black_id, :white_id
42   attr_reader :winner, :loser
43   attr_reader :state
44   attr_reader :start_time, :end_time
45   attr_reader :ply
46
47   def initialize(file_name, encoding="Shift_JIS:EUC-JP")
48     @file_name = file_name
49     @encoding = encoding
50     @ply = 0
51     grep
52   end
53
54   def grep
55     @str = File.open(@file_name, "r:#{@encoding}").read
56
57
58     if /^N\+(.*)$/ =~ @str then @black_name = $1.strip end
59     if /^N\-(.*)$/ =~ @str then @white_name = $1.strip end
60     if /^'summary:(.*)$/ =~ @str
61       @state, p1, p2 = $1.split(":").map {|a| a.strip}    
62       p1_name, p1_mark = p1.split(" ")
63       p2_name, p2_mark = p2.split(" ")
64       if p1_name == @black_name
65         @black_name, black_mark = p1_name, p1_mark
66         @white_name, white_mark = p2_name, p2_mark
67       elsif p2_name == @black_name
68         @black_name, black_mark = p2_name, p2_mark
69         @white_name, white_mark = p1_name, p1_mark
70       else
71         raise "Never reach!: #{black} #{white} #{p3} #{p2}"
72       end
73     end
74     if /^\$START_TIME:(.*)$/ =~ @str
75       @start_time = Time.parse($1.strip)
76     end
77     if /^'\$END_TIME:(.*)$/ =~ @str
78       @end_time = Time.parse($1.strip)
79     end
80     if /^'summary:.*?:(.*)$/ =~ @str
81       black_id, bresult, white_id, wresult = $1.split(":").map {|a| a.strip.split(" ")}.flatten
82       @black_id = identify_id(black_id)
83       @white_id = identify_id(white_id)
84       if @black_id && @white_id
85         if black_mark == WIN_MARK && white_mark == LOSS_MARK
86           @winner, @loser = @black_id, @white_id
87         elsif black_mark == LOSS_MARK && white_mark == WIN_MARK
88           @winner, @loser = @white_id, @black_id
89         else
90           # draw or errors
91           @winner, @loser = nil, nil
92         end
93       end
94     end
95
96     @str.each_line do |line|
97       if /^[\+\-]\d{4}[A-Z]{2}/ =~ line
98         @ply += 1
99       end
100     end
101   end
102
103   def movetimes
104     ret = []
105     @str.gsub(%r!^T(\d+)!) do |match|
106       ret << $1.to_i
107     end
108     return ret
109   end
110
111   def to_s
112     return "Summary: #{@file_name}\n" +
113            "BlackName #{@black_name}, WhiteName #{@white_name}\n" +
114            "BlackId #{@black_id}, WhiteId #{@white_id}\n" +
115            "Winner #{@winner}, Loser #{@loser}\n"    +
116            "Start #{@start_time}, End #{@end_time}\n" +
117            "Ply #{@ply}"
118   end
119
120   def identify_id(id)
121     if /@NORATE\+/ =~ id # the player having @NORATE in the name should not be rated
122       return nil
123     end
124     id.gsub(/@.*?\+/,"+")
125   end
126 end
127
128
129 if $0 == __FILE__
130   def usage
131     puts "Usage: #{$0} [OPTIONS] dir [...]"
132     puts "Options:"
133     puts "  --players player_a,player_b  select games of the player_a vs the player_b"
134     puts "  --black player               select games of which the player is Black"
135     puts "  --white player               select games of which the player is White"
136     puts "  --winner player              select games that the player won"
137     puts "  --loser player               select games that the player lose"
138     exit 1
139   end
140
141   usage if ARGV.empty?
142
143   parser = GetoptLong.new(
144              ['--black',   GetoptLong::REQUIRED_ARGUMENT],
145              ['--white',   GetoptLong::REQUIRED_ARGUMENT],
146              ['--players', GetoptLong::REQUIRED_ARGUMENT],
147              ['--winner',  GetoptLong::REQUIRED_ARGUMENT],
148              ['--loser',   GetoptLong::REQUIRED_ARGUMENT]
149            )
150   begin
151     parser.each_option do |name, arg|
152       eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_').upcase} = '#{arg}'"
153     end
154   rescue
155     usage
156   end
157   
158   while dir = ARGV.shift
159     Dir.glob(File.join(dir, "**", "*.csa")).each do |file|
160       csa = CsaFileReader.new(file)
161
162       next unless csa.black_id && csa.white_id
163
164       if $OPT_PLAYERS
165         players = $OPT_PLAYERS.split(",")
166         unless (csa.black_id.downcase.index(players[0].downcase) == 0 &&
167                 csa.white_id.downcase.index(players[1].downcase) == 0) ||
168                (csa.black_id.downcase.index(players[1].downcase) == 0 &&
169                 csa.white_id.downcase.index(players[0].downcase) == 0)
170           next
171         end
172       end
173       
174       if $OPT_BLACK
175         next unless csa.black_id.downcase.index($OPT_BLACK.downcase) == 0
176       end
177       if $OPT_WHITE
178         next unless csa.white_id.downcase.index($OPT_WHITE.downcase) == 0
179       end
180
181       if $OPT_WINNER
182         next unless csa.winner && csa.winner.downcase.index($OPT_WINNER.downcase) == 0
183       end
184       if $OPT_LOSER
185         next unless csa.loser && csa.loser.downcase.index($OPT_LOSER.downcase) == 0
186       end
187
188       puts csa.file_name
189     end
190   end
191 end