shogi-server source
リビジョン | ee9fe66e7d60579abef52f382040d6d13496aa01 (tree) |
---|---|
日時 | 2017-09-02 20:26:54 |
作者 | Daigo Moriwaki <daigo@debi...> |
コミッター | Daigo Moriwaki |
Merge remote-tracking branch 'origin/master' into wdoor-stable
@@ -1,3 +1,28 @@ | ||
1 | +2017-09-02 Daigo Moriwaki <daigo at debian dot org> | |
2 | + | |
3 | + * [shogi-server] Write more game results in record files | |
4 | + As the CSA Record Format standard states, "%TIME_UP" and | |
5 | + "%SENNICHITE" are now recorded in CSA files when a game ends for | |
6 | + timed up or sennichite respectively. | |
7 | + (Closes #37490) | |
8 | + * [shogi-server] [mk_game_results] Change schema to add number of | |
9 | + moves The format of an internal game result summary file, '00LIST', | |
10 | + has been changed to add number of moves of a game, which would be of | |
11 | + help for a script calculating rating scores. | |
12 | + (Closes #37491) | |
13 | + * Update Revision to 20170902 | |
14 | + | |
15 | +2017-02-26 Daigo Moriwaki <daigo at debian dot org> | |
16 | + | |
17 | + * utils/csa-filter.rb: Allow csa-filter.rb to filter games by a winner or loser | |
18 | + New command lines, --winner <player> or --loser <player>, are now | |
19 | + supported to supply extra filtering conditions. Note that each | |
20 | + filter is combined as AND condition. | |
21 | + E.g. | |
22 | + - % ./csa-filter.rb ~/Downloads/20160314 --white gpsfish_xeon --loser gpsfish_xeon | |
23 | + - % ./csa-filter.rb ~/Downloads/20160314 --white gpsfish_xeon --winner gpsfish_xeon | |
24 | + (Closes #37023) | |
25 | + | |
1 | 26 | 2016-12-11 Daigo Moriwaki <daigo at debian dot org> |
2 | 27 | |
3 | 28 | * Update Revision to 20161211. |
@@ -47,13 +47,14 @@ | ||
47 | 47 | require 'getoptlong' |
48 | 48 | |
49 | 49 | # Parse a CSA file. A tab-delimited line format is |
50 | -# time state black_mark black_id white_id white_mark filepath | |
50 | +# time state black_mark black_id white_id white_mark filepath moves | |
51 | 51 | # time:: YYYY/MM/DD hh:mm:ss |
52 | 52 | # black_mark:: win lose draw |
53 | 53 | # black_id:: black player's id |
54 | 54 | # white_mark:: win lose draw |
55 | 55 | # white_id:: white player's id |
56 | 56 | # filepath:: absolute file path |
57 | +# moves:: number of moves | |
57 | 58 | # |
58 | 59 | # @parameter file an absolute path of a csa file |
59 | 60 | # |
@@ -64,36 +65,49 @@ def grep(file) | ||
64 | 65 | str = File.open(file, "r").read |
65 | 66 | end |
66 | 67 | |
67 | - if /^N\+(.*)$/ =~ str then black_name = $1.strip end | |
68 | - if /^N\-(.*)$/ =~ str then white_name = $1.strip end | |
68 | + black_name = "" | |
69 | + black_mark = "" | |
70 | + black_id = "" | |
71 | + white_name = "" | |
72 | + white_mark = "" | |
73 | + white_id = "" | |
74 | + state = "" | |
75 | + time = "" | |
76 | + moves = 0 | |
77 | + str.each_line do |line| | |
78 | + line.strip! | |
79 | + case line | |
80 | + when /^[\+\-]\d{4}\w{2}.*$/ | |
81 | + moves += 1 | |
82 | + when /^N\+(.*)$/ | |
83 | + black_name = $1.strip | |
84 | + when /^N\-(.*)$/ | |
85 | + white_name = $1.strip | |
86 | + when /^'summary:(.*)$/ | |
87 | + state, p1, p2 = $1.split(":").map {|a| a.strip} | |
88 | + p1_name, p1_mark = p1.split(" ") | |
89 | + p2_name, p2_mark = p2.split(" ") | |
90 | + if p1_name == black_name | |
91 | + black_name, black_mark = p1_name, p1_mark | |
92 | + white_name, white_mark = p2_name, p2_mark | |
93 | + elsif p2_name == black_name | |
94 | + black_name, black_mark = p2_name, p2_mark | |
95 | + white_name, white_mark = p1_name, p1_mark | |
96 | + else | |
97 | + raise "Never reach!: #{black_name}, #{p1}, #{p2} in #{file}" | |
98 | + end | |
99 | + when /^'\$END_TIME:(.*)$/ | |
100 | + time = $1.strip | |
101 | + when /^'rating:(.*)$/ | |
102 | + black_id, white_id = $1.split(":").map {|a| a.strip} | |
103 | + end # case | |
104 | + end # do line | |
69 | 105 | |
70 | - if /^'summary:(.*)$/ =~ str | |
71 | - state, p1, p2 = $1.split(":").map {|a| a.strip} | |
72 | - p1_name, p1_mark = p1.split(" ") | |
73 | - p2_name, p2_mark = p2.split(" ") | |
74 | - if p1_name == black_name | |
75 | - black_name, black_mark = p1_name, p1_mark | |
76 | - white_name, white_mark = p2_name, p2_mark | |
77 | - elsif p2_name == black_name | |
78 | - black_name, black_mark = p2_name, p2_mark | |
79 | - white_name, white_mark = p1_name, p1_mark | |
80 | - else | |
81 | - raise "Never reach!: #{black} #{white} #{p3} #{p2}" | |
82 | - end | |
83 | - end | |
84 | - | |
85 | - if /^'\$END_TIME:(.*)$/ =~ str | |
86 | - time = $1.strip | |
87 | - end | |
88 | - | |
89 | - if /^'rating:(.*)$/ =~ str | |
90 | - black_id, white_id = $1.split(":").map {|a| a.strip} | |
91 | - if black_id && white_id && (black_id != white_id) && | |
92 | - black_mark && white_mark && state && time | |
93 | - puts [time, state, black_mark, black_id, white_id, white_mark, file].join("\t") | |
94 | - end | |
106 | + if black_id && white_id && (black_id != white_id) && | |
107 | + black_mark && white_mark && state && time | |
108 | + puts [time, state, black_mark, black_id, white_id, white_mark, file, moves].join("\t") | |
109 | + $stdout.flush | |
95 | 110 | end |
96 | - $stdout.flush | |
97 | 111 | end |
98 | 112 | |
99 | 113 | # Show Usage |
@@ -144,4 +158,4 @@ if __FILE__ == $0 | ||
144 | 158 | main |
145 | 159 | end |
146 | 160 | |
147 | -# vim: ts=2 sw=2 sts=0 | |
161 | +# vim: tabstop=4 shiftwidth=4 expandtab |
@@ -52,7 +52,7 @@ Default_Max_Moves = 256 | ||
52 | 52 | Default_Least_Time_Per_Move = 0 |
53 | 53 | One_Time = 10 |
54 | 54 | Login_Time = 300 # time for LOGIN |
55 | -Revision = "20161211" | |
55 | +Revision = "20170902" | |
56 | 56 | |
57 | 57 | RELOAD_FILES = ["shogi_server/league/floodgate.rb", |
58 | 58 | "shogi_server/league/persistent.rb", |
@@ -52,7 +52,8 @@ class LoggingObserver | ||
52 | 52 | black_name, |
53 | 53 | white_name, |
54 | 54 | game_result.white_result, |
55 | - game_result.game.logfile] | |
55 | + game_result.game.logfile, | |
56 | + game_result.game.board.move_count] | |
56 | 57 | begin |
57 | 58 | # Note that this is proccessed in the gian lock. |
58 | 59 | File.open(@logfile, "a") do |f| |
@@ -194,7 +195,7 @@ class GameResultTimeoutWin < GameResultWin | ||
194 | 195 | def process |
195 | 196 | @winner.write_safe("#TIME_UP\n#WIN\n") |
196 | 197 | @loser.write_safe( "#TIME_UP\n#LOSE\n") |
197 | - # no log | |
198 | + log("%TIME_UP") # a player in turn lost | |
198 | 199 | log_summary |
199 | 200 | notify |
200 | 201 | end |
@@ -337,7 +338,7 @@ class GameResultSennichiteDraw < GameResultDraw | ||
337 | 338 | @players.each do |player| |
338 | 339 | player.write_safe("#SENNICHITE\n#DRAW\n") |
339 | 340 | end |
340 | - # no log | |
341 | + log("%SENNICHITE") | |
341 | 342 | log_summary |
342 | 343 | notify |
343 | 344 | end |
@@ -10,12 +10,20 @@ $league.event = "TC_game_result" | ||
10 | 10 | module ShogiServer |
11 | 11 | class BasicPlayer |
12 | 12 | attr_accessor :sente, :status |
13 | + | |
14 | + def write_safe(dummy) | |
15 | + end | |
13 | 16 | end |
14 | 17 | end |
15 | 18 | |
16 | 19 | class TestGameResult < Test::Unit::TestCase |
17 | 20 | class DummyGame |
18 | 21 | attr_accessor :game_name |
22 | + attr_reader :board | |
23 | + | |
24 | + def initialize | |
25 | + @board = "" | |
26 | + end | |
19 | 27 | end |
20 | 28 | |
21 | 29 | def setup |
@@ -105,10 +113,28 @@ class TestGameResult < Test::Unit::TestCase | ||
105 | 113 | |
106 | 114 | def test_game_result_sennichite_draw |
107 | 115 | gr = ShogiServer::GameResultSennichiteDraw.new(@game, @p1, @p2) |
116 | + $cache_state = [] | |
117 | + def gr.log(s) | |
118 | + $cache_state << s | |
119 | + end | |
108 | 120 | assert_equal(@p1.last_game_win, false) |
109 | 121 | assert_equal(@p2.last_game_win, false) |
110 | 122 | assert_equal("sennichite", gr.log_summary_type) |
123 | + | |
124 | + gr.delete_observers | |
125 | + gr.process | |
126 | + assert_equal("%SENNICHITE", $cache_state[0]) | |
111 | 127 | end |
112 | 128 | |
129 | + def test_game_result_timeout | |
130 | + gr = ShogiServer::GameResultTimeoutWin.new(@game, @p2, @p1) | |
131 | + $cache_state = [] | |
132 | + def gr.log(s) | |
133 | + $cache_state << s | |
134 | + end | |
135 | + gr.delete_observers | |
136 | + gr.process | |
137 | + assert_equal("%TIME_UP", $cache_state[0]) | |
138 | + end | |
113 | 139 | end |
114 | 140 |
@@ -5,7 +5,7 @@ | ||
5 | 5 | # you will see such files under the some_dir directory. |
6 | 6 | # |
7 | 7 | # Author:: Daigo Moriwaki <daigo at debian dot org> |
8 | -# Copyright:: Copyright (C) 2006-2012 Daigo Moriwaki <daigo at debian dot org> | |
8 | +# Copyright:: Copyright (C) 2006-2017 Daigo Moriwaki <daigo at debian dot org> | |
9 | 9 | # |
10 | 10 | # $Id$ |
11 | 11 | # |
@@ -77,20 +77,18 @@ class CsaFileReader | ||
77 | 77 | if /^'\$END_TIME:(.*)$/ =~ @str |
78 | 78 | @end_time = Time.parse($1.strip) |
79 | 79 | end |
80 | - if /^'rating:(.*)$/ =~ @str | |
81 | - black_id, white_id = $1.split(":").map {|a| a.strip} | |
80 | + if /^'summary:.*?:(.*)$/ =~ @str | |
81 | + black_id, bresult, white_id, wresult = $1.split(":").map {|a| a.strip.split(" ")}.flatten | |
82 | 82 | @black_id = identify_id(black_id) |
83 | 83 | @white_id = identify_id(white_id) |
84 | - if @black_id && @white_id && (@black_id != @white_id) && | |
85 | - @black_mark && @white_mark | |
84 | + if @black_id && @white_id | |
86 | 85 | if black_mark == WIN_MARK && white_mark == LOSS_MARK |
87 | 86 | @winner, @loser = @black_id, @white_id |
88 | 87 | elsif black_mark == LOSS_MARK && white_mark == WIN_MARK |
89 | 88 | @winner, @loser = @white_id, @black_id |
90 | - elsif black_mark == DRAW_MARK && white_mark == DRAW_MARK | |
89 | + else | |
90 | + # draw or errors | |
91 | 91 | @winner, @loser = nil, nil |
92 | - else | |
93 | - raise "Never reached!" | |
94 | 92 | end |
95 | 93 | end |
96 | 94 | end |
@@ -135,6 +133,8 @@ if $0 == __FILE__ | ||
135 | 133 | puts " --players player_a,player_b select games of the player_a vs the player_b" |
136 | 134 | puts " --black player select games of which the player is Black" |
137 | 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 | 138 | exit 1 |
139 | 139 | end |
140 | 140 |
@@ -143,7 +143,9 @@ if $0 == __FILE__ | ||
143 | 143 | parser = GetoptLong.new( |
144 | 144 | ['--black', GetoptLong::REQUIRED_ARGUMENT], |
145 | 145 | ['--white', GetoptLong::REQUIRED_ARGUMENT], |
146 | - ['--players', GetoptLong::REQUIRED_ARGUMENT] | |
146 | + ['--players', GetoptLong::REQUIRED_ARGUMENT], | |
147 | + ['--winner', GetoptLong::REQUIRED_ARGUMENT], | |
148 | + ['--loser', GetoptLong::REQUIRED_ARGUMENT] | |
147 | 149 | ) |
148 | 150 | begin |
149 | 151 | parser.each_option do |name, arg| |
@@ -175,6 +177,14 @@ if $0 == __FILE__ | ||
175 | 177 | if $OPT_WHITE |
176 | 178 | next unless csa.white_id.downcase.index($OPT_WHITE.downcase) == 0 |
177 | 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 | + | |
178 | 188 | puts csa.file_name |
179 | 189 | end |
180 | 190 | end |