• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

shogi-server source


Commit MetaInfo

Revisiona0947c435b603ae1fa2371870217b6b6217e1cc6 (tree)
Time2015-12-13 21:28:20
AuthorDaigo Moriwaki <daigo@debi...>
CommiterDaigo Moriwaki

Log Message

Merge branch 'master' into wdoor-stable

Conflicts:
shogi_server.rb

Change Summary

Incremental Difference

--- a/Makefile
+++ b/Makefile
@@ -6,12 +6,12 @@ doc: clean
66
77 .PHONY: test-run
88 test-run:
9- ./shogi-server --floodgate-games floodgate-900-0,floodgate-3600-0 hoge 4000
9+ ./shogi-server --floodgate-games floodgate-600-10,floodgate-3600-0 hoge 4000
1010
1111
1212 .PHONY: test-run-daemon
1313 test-run-daemon:
14- ./shogi-server --floodgate-games floodgate-900-0,floodgate-3600-0 --daemon . --pid-file ./shogi-server.pid --player-log-dir ./player-logs hoge 4000
14+ ./shogi-server --floodgate-games floodgate-600-10,floodgate-3600-0 --daemon . --pid-file ./shogi-server.pid --player-log-dir ./player-logs hoge 4000
1515
1616 .PHONY: stop-daemn
1717 stop-daemon:
--- a/changelog
+++ b/changelog
@@ -1,3 +1,27 @@
1+2015-12-13 Daigo Moriwaki <daigo at debian dot org>
2+
3+ * [shogi-server] Enhance capability of Floodgate configuration file
4+ - New parameter: Max_Moves, defined in the CSA protocol
5+ ex. set Max_Moves 256
6+ - New parameter: Least_Time_Per_Move, defined in the CSA protocol
7+ ex. set Least_Time_Per_Move 0
8+ - Proposed messages distributed to each player upon starting a new
9+ game will include Max_Moves as well as Least_Time_Per_Move.
10+ - CSA files produced by the server will include settings of
11+ Max_Moves and Least_Time_Per_Move in comment lines as follows:
12+ 'Max_Moves:256
13+ 'Least_Time_Per_Move:0
14+ - The official Shogi-server on wdoor.c.u-tokyo.ac.jp will
15+ be running with different parameters, depending on game names.
16+ a) Max_Moves will be 256 for floodgate-600-10 games;
17+ otherwise, 0.
18+ b) Least_Time_Per_Move will be 0 for floodgate-600-10 games;
19+ otherwise 1.
20+ (Closes: #35839)
21+ * [shogi-server] shogi_server/pairing.rb:
22+ - LeastDiff attempts more trials, depending of a number of players
23+ to be matched, top achieve more optimized matching combinations.
24+
125 2015-11-27 Daigo Moriwaki <daigo at debian dot org>
226
327 * [shogi-server] shogi_server/time_clock.rb:
--- a/shogi-server
+++ b/shogi-server
@@ -278,10 +278,10 @@ def check_command_line
278278 $options["floodgate-history"] = nil
279279 end
280280
281- $options["max-moves"] ||= 256
281+ $options["max-moves"] ||= ShogiServer::Default_Max_Moves
282282 $options["max-moves"] = $options["max-moves"].to_i
283283
284- $options["least-time-per-move"] ||= 0
284+ $options["least-time-per-move"] ||= ShogiServer::Default_Least_Time_Per_Move
285285 $options["least-time-per-move"] = $options["least-time-per-move"].to_i
286286 end
287287
--- a/shogi_server.rb
+++ b/shogi_server.rb
@@ -48,9 +48,11 @@ module ShogiServer # for a namespace
4848 Max_Identifier_Length = 32
4949 Default_Timeout = 60 # for single socket operation
5050 Default_Game_Name = "default-1500-0"
51+Default_Max_Moves = 256
52+Default_Least_Time_Per_Move = 0
5153 One_Time = 10
5254 Login_Time = 300 # time for LOGIN
53-Revision = "20150201"
55+Revision = "20151213"
5456
5557 RELOAD_FILES = ["shogi_server/league/floodgate.rb",
5658 "shogi_server/league/persistent.rb",
--- a/shogi_server/board.rb
+++ b/shogi_server/board.rb
@@ -84,18 +84,25 @@ EOF
8484 end
8585
8686
87- def initialize(move_count=0)
87+ def initialize(options={})
8888 @sente_hands = Array::new
8989 @gote_hands = Array::new
9090 @history = Hash::new(0)
9191 @sente_history = Hash::new(0)
9292 @gote_history = Hash::new(0)
9393 @array = [[], [], [], [], [], [], [], [], [], []]
94- @move_count = move_count
94+ @move_count = 0
9595 @teban = nil # black => true, white => false
9696 @initial_moves = []
9797 @move = nil
9898 @ous = [nil, nil] # keep OU pieces of Sente and Gote
99+
100+ @max_moves = options[:max_moves] ||
101+ ($options && $options["max-moves"]) ||
102+ Default_Max_Moves
103+ @least_time_per_move = options[:least_time_per_move] ||
104+ ($options && $options["least-time-per-move"]) ||
105+ Default_Least_Time_Per_Move
99106 end
100107 attr_accessor :array, :sente_hands, :gote_hands, :history, :sente_history, :gote_history, :teban
101108 attr_reader :move_count
@@ -110,6 +117,12 @@ EOF
110117 #
111118 attr_reader :move
112119
120+ # Max_Moves of the CSA protocol
121+ attr_reader :max_moves
122+
123+ # Least_Time_Per_Move of the CSA protocol
124+ attr_reader :least_time_per_move
125+
113126 # See if self equals rhs, including a logical board position (i.e.
114127 # not see object IDs) and sennichite stuff.
115128 #
@@ -717,8 +730,7 @@ EOF
717730 # New rule that CSA introduced in November 2014.
718731 # If a game with 256 plies does not end, make the game a draw.
719732 # When running test cases $options might be nil.
720- if $options && $options["max-moves"] &&
721- $options["max-moves"] > 0 && @move_count >= $options["max-moves"]
733+ if @max_moves > 0 && @move_count >= @max_moves
722734 return :max_moves
723735 end
724736
--- a/shogi_server/game.rb
+++ b/shogi_server/game.rb
@@ -71,7 +71,7 @@ class Game
7171 @total_time = $1.to_i
7272 @byoyomi = $2.to_i
7373
74- @time_clock = TimeClock::factory($options["least-time-per-move"], @game_name)
74+ @time_clock = TimeClock::factory(board.least_time_per_move, @game_name)
7575 end
7676
7777 if (player0.sente)
@@ -325,6 +325,8 @@ class Game
325325 @fh.puts("V2")
326326 @fh.puts("N+#{@sente.name}")
327327 @fh.puts("N-#{@gote.name}")
328+ @fh.puts("'Max_Moves:#{@board.max_moves}")
329+ @fh.puts("'Least_Time_Per_Move:#{@board.least_time_per_move}")
328330 @fh.puts("$EVENT:#{@game_id}")
329331
330332 @sente.write_safe(propose_message("+"))
@@ -371,11 +373,12 @@ Name+:#{@sente.name}
371373 Name-:#{@gote.name}
372374 Rematch_On_Draw:NO
373375 To_Move:+
376+Max_Moves:#{@board.max_moves}
374377 BEGIN Time
375378 Time_Unit:#{@time_clock.time_unit}
376379 Total_Time:#{@total_time}
377380 Byoyomi:#{@byoyomi}
378-Least_Time_Per_Move:#{$options["least-time-per-move"]}
381+Least_Time_Per_Move:#{@board.least_time_per_move}
379382 Remaining_Time+:#{@sente.mytime}
380383 Remaining_Time-:#{@gote.mytime}
381384 Last_Move:#{@last_move}
@@ -405,11 +408,12 @@ Name-:#{@gote.name}
405408 Your_Turn:#{sg_flag}
406409 Rematch_On_Draw:NO
407410 To_Move:#{@board.teban ? "+" : "-"}
411+Max_Moves:#{@board.max_moves}
408412 BEGIN Time
409413 Time_Unit:#{@time_clock.time_unit}
410414 Total_Time:#{@total_time}
411415 Byoyomi:#{@byoyomi}
412-Least_Time_Per_Move:#{$options["least-time-per-move"]}
416+Least_Time_Per_Move:#{@board.least_time_per_move}
413417 END Time
414418 BEGIN Position
415419 #{@board.initial_string.chomp}
--- a/shogi_server/league/floodgate.rb
+++ b/shogi_server/league/floodgate.rb
@@ -36,8 +36,10 @@ class League
3636 # Options will be updated by NextTimeGenerator and then passed to a
3737 # pairing factory.
3838 @options = {}
39- @options[:pairing_factory] = hash[:pairing_factory] || "default_factory"
40- @options[:sacrifice] = hash[:sacrifice] || "gps500+e293220e3f8a3e59f79f6b0efffaa931"
39+ @options[:pairing_factory] = hash[:pairing_factory] || "default_factory"
40+ @options[:sacrifice] = hash[:sacrifice] || "gps500+e293220e3f8a3e59f79f6b0efffaa931"
41+ @options[:max_moves] = hash[:max_moves] || Default_Max_Moves
42+ @options[:least_time_per_move] = hash[:least_time_per_move] || Default_Least_Time_Per_Move
4143 charge if @next_time.nil?
4244 end
4345
@@ -53,12 +55,22 @@ class League
5355 return @options[:sacrifice]
5456 end
5557
58+ def max_moves
59+ return @options[:max_moves]
60+ end
61+
62+ def least_time_per_move
63+ return @options[:least_time_per_move]
64+ end
65+
5666 def charge
5767 ntg = NextTimeGenerator.factory(@game_name)
5868 if ntg
5969 @next_time = ntg.call(Time.now)
60- @options[:pairing_factory] = ntg.pairing_factory
61- @options[:sacrifice] = ntg.sacrifice
70+ @options[:pairing_factory] = ntg.pairing_factory
71+ @options[:sacrifice] = ntg.sacrifice
72+ @options[:max_moves] = ntg.max_moves
73+ @options[:least_time_per_move] = ntg.least_time_per_move
6274 else
6375 @next_time = nil
6476 end
@@ -80,7 +92,7 @@ class League
8092 def match_game
8193 log_message("Starting Floodgate games...: %s, %s" % [@game_name, @options])
8294 logics = Pairing.send(@options[:pairing_factory], @options)
83- Pairing.match(select_players(), logics)
95+ Pairing.match(select_players(), logics, @options)
8496 end
8597
8698 #
@@ -110,12 +122,16 @@ class League
110122
111123 attr_reader :pairing_factory
112124 attr_reader :sacrifice
125+ attr_reader :max_moves
126+ attr_reader :least_time_per_move
113127
114128 # Constructor.
115129 #
116130 def initialize
117- @pairing_factory = "default_factory"
118- @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
131+ @pairing_factory = "default_factory"
132+ @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
133+ @max_moves = Default_Max_Moves
134+ @least_time_per_move = Default_Least_Time_Per_Move
119135 end
120136 end
121137
@@ -146,6 +162,12 @@ class League
146162 # * sacrifice:
147163 # Specifies a sacrificed player.
148164 # ex. set sacrifice gps500+e293220e3f8a3e59f79f6b0efffaa931
165+ # * max_moves:
166+ # Sepcifies a number of max moves
167+ # ex. set max_moves 256
168+ # * least_time_per_move:
169+ # Sepcifies a least time per move
170+ # ex. set least_time_per_move 0
149171 #
150172 class NextTimeGeneratorConfig < AbstructNextTimeGenerator
151173
@@ -170,6 +192,10 @@ class League
170192 @pairing_factory = $1.chomp
171193 when %r!^\s*set\s+sacrifice\s+(.*)!
172194 @sacrifice = $1.chomp
195+ when %r!^\s*set\s+max_moves\s+(\d+)!
196+ @max_moves = $1.chomp.to_i
197+ when %r!^\s*set\s+least_time_per_move\s+(\d+)!
198+ @least_time_per_move = $1.chomp.to_i
173199 when %r!^\s*(\w+)\s+(\d{1,2}):(\d{1,2})!
174200 dow, hour, minute = $1, $2.to_i, $3.to_i
175201 dow_index = ::ShogiServer::parse_dow(dow)
--- a/shogi_server/league/floodgate_thread.rb
+++ b/shogi_server/league/floodgate_thread.rb
@@ -73,11 +73,12 @@ module ShogiServer
7373 #
7474 def regenerate_leagues(next_instances)
7575 leagues = next_instances.collect do |prev|
76- log_message("Regenerating a floodgate league...: %s %s %s %s" %
77- [prev.game_name, prev.next_time, prev.pairing_factory, prev.sacrifice])
76+ log_message("Regenerating a floodgate league...: %s %s %s %s %d %d" %
77+ [prev.game_name, prev.next_time, prev.pairing_factory, prev.sacrifice, prev.max_moves, prev.least_time_per_move])
7878 floodgate = ShogiServer::League::Floodgate.new($league,
7979 {:game_name => prev.game_name, :next_time => prev.next_time,
80- :pairing_factory => prev.pairing_factory, :sacrifice => prev.sacrifice})
80+ :pairing_factory => prev.pairing_factory, :sacrifice => prev.sacrifice,
81+ :max_moves => prev.max_moves, :least_time_per_move => prev.least_time_per_move})
8182 end
8283 floodgate_reload_log(leagues)
8384 return leagues
--- a/shogi_server/pairing.rb
+++ b/shogi_server/pairing.rb
@@ -69,14 +69,22 @@ module ShogiServer
6969 StartGameWithoutHumans.new]
7070 end
7171
72- def match(players, logics)
72+ def match(players, logics, options)
7373 logics.inject(players) do |result, item|
74+ item.set_options(options)
7475 item.match(result)
7576 result
7677 end
7778 end
7879 end # class << self
7980
81+ def initialize
82+ @options = {}
83+ end
84+
85+ def set_options(options)
86+ @options.merge!(options)
87+ end
8088
8189 # Make matches among players.
8290 # @param players an array of players, which should be updated destructively
@@ -129,7 +137,7 @@ module ShogiServer
129137 log_message("Floodgate: Starting a game: BLACK %s vs WHITE %s" % [p1.name, p2.name])
130138 p1.sente = true
131139 p2.sente = false
132- board = Board.new
140+ board = Board.new(@options)
133141 board.initial
134142 Game.new(p1.game_name, p1, p2, board)
135143 end
@@ -506,6 +514,22 @@ module ShogiServer
506514 ret
507515 end
508516
517+ # Total combinations of possible games among n players
518+ # nC2 * (n-2)C2 * ... * 2C2 / (n/2)!
519+ def total_posibilities(n)
520+ n -= 1 if n.odd?
521+ return 1 if n <= 2
522+
523+ ret = 1
524+ i = n
525+ while i >= 2 do
526+ ret *= ::ShogiServer::nCk(i,2)
527+ i -= 2
528+ end
529+ ret /= ::ShogiServer::factorial(n/2)
530+ return ret
531+ end
532+
509533 def match(players)
510534 super
511535 if players.size < 3
@@ -516,12 +540,16 @@ module ShogiServer
516540 # Reset estimated rate
517541 players.each {|p| p.estimated_rate = 0}
518542
519- # 10 trials
520543 matches = []
521544 scores = []
522545 path = ShogiServer::League::Floodgate.history_file_path(players.first.game_name)
523546 history = ShogiServer::League::Floodgate::History.factory(path)
524- 10.times do
547+
548+ # Increase trials, depending on a number of players
549+ trials = [300, total_posibilities(players.size)/3].min
550+ trials = [10, trials].max
551+ log_message("Floodgate: %d trials" % [trials])
552+ trials.times do
525553 m = random_match(players)
526554 matches << m
527555 scores << calculate_diff_with_penalty(m, history)
--- a/shogi_server/util.rb
+++ b/shogi_server/util.rb
@@ -42,6 +42,25 @@ module ShogiServer
4242 end
4343 module_function :shuffle
4444
45+ def factorial(n)
46+ return 1 if n<=1
47+ ret = 1
48+ while n >= 2
49+ ret *= n
50+ n -= 1
51+ end
52+ return ret
53+ end
54+ module_function :factorial
55+
56+ def nCk(n, k)
57+ return 0 if n < k
58+ numerator = factorial(n)
59+ denominator = factorial(k) * factorial(n - k)
60+ return numerator / denominator
61+ end
62+ module_function :nCk
63+
4564 # See if the file is writable. The file will be created if it does not exist
4665 # yet.
4766 # Return true if the file is writable, otherwise false.
--- a/test/TC_floodgate_next_time_generator.rb
+++ b/test/TC_floodgate_next_time_generator.rb
@@ -249,4 +249,20 @@ class TestNextTimeGeneratorConfig < Test::Unit::TestCase
249249 assert_equal Time.parse("10-06-2010 22:00"), ntc.call(now)
250250 assert_equal("yowai_gps+95908f6c18338f5340371f71523fc5e3", ntc.sacrifice)
251251 end
252+
253+ def test_default_max_moves
254+ now = DateTime.new(2010, 6, 10, 21, 59, 59) # Thu
255+ lines = %w(Thu\ 22:00)
256+ ntc = ShogiServer::League::Floodgate::NextTimeGeneratorConfig.new lines
257+ assert_equal Time.parse("10-06-2010 22:00"), ntc.call(now)
258+ assert_equal(256, ntc.max_moves)
259+ end
260+
261+ def test_read_max_moves
262+ now = DateTime.new(2010, 6, 10, 21, 59, 59) # Thu
263+ lines = %w(set\ max_moves\ 200 Thu\ 22:00)
264+ ntc = ShogiServer::League::Floodgate::NextTimeGeneratorConfig.new lines
265+ assert_equal Time.parse("10-06-2010 22:00"), ntc.call(now)
266+ assert_equal(200, ntc.max_moves)
267+ end
252268 end
--- a/test/TC_functional.rb
+++ b/test/TC_functional.rb
@@ -36,6 +36,8 @@ class TestClientAtmark < BaseClient
3636 V2
3737 N+atmark_B@p1
3838 N-atmark_W@p2
39+'Max_Moves:256
40+'Least_Time_Per_Move:0
3941 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
4042 P2 * -HI * * * * * -KA *
4143 P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
@@ -103,6 +105,8 @@ class TestHandicappedGame < BaseClient
103105 V2
104106 N+hc2p_hoge_B
105107 N-hc2p_hoge_W
108+'Max_Moves:256
109+'Least_Time_Per_Move:0
106110 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
107111 P2 * -HI * * * * * -KA *
108112 P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
--- a/test/TC_game.rb
+++ b/test/TC_game.rb
@@ -7,6 +7,7 @@ require 'shogi_server/player'
77
88 $options = {}
99 $options["least-time-per-move"] = 1
10+$options["max-moves"] = 0
1011
1112 def log_message(str)
1213 $stderr.puts str
@@ -51,6 +52,7 @@ Name-:p2
5152 Your_Turn:+
5253 Rematch_On_Draw:NO
5354 To_Move:+
55+Max_Moves:#{$options["max-moves"]}
5456 BEGIN Time
5557 Time_Unit:1sec
5658 Total_Time:1500
@@ -85,6 +87,7 @@ Name-:p2
8587 Your_Turn:-
8688 Rematch_On_Draw:NO
8789 To_Move:+
90+Max_Moves:#{$options["max-moves"]}
8891 BEGIN Time
8992 Time_Unit:1sec
9093 Total_Time:1500
@@ -113,6 +116,8 @@ EOF
113116 V2
114117 N+p1
115118 N-p2
119+'Max_Moves:#{$options["max-moves"]}
120+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
116121 $EVENT:#{game.game_id}
117122 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
118123 P2 * -HI * * * * * -KA *
@@ -153,6 +158,7 @@ Name-:p2
153158 Your_Turn:+
154159 Rematch_On_Draw:NO
155160 To_Move:-
161+Max_Moves:#{$options["max-moves"]}
156162 BEGIN Time
157163 Time_Unit:1sec
158164 Total_Time:1500
@@ -188,6 +194,7 @@ Name-:p2
188194 Your_Turn:-
189195 Rematch_On_Draw:NO
190196 To_Move:-
197+Max_Moves:#{$options["max-moves"]}
191198 BEGIN Time
192199 Time_Unit:1sec
193200 Total_Time:1500
@@ -217,6 +224,8 @@ EOF
217224 V2
218225 N+p1
219226 N-p2
227+'Max_Moves:#{$options["max-moves"]}
228+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
220229 $EVENT:#{game.game_id}
221230 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
222231 P2 * -HI * * * * * -KA *
@@ -260,6 +269,7 @@ Name-:p2
260269 Your_Turn:+
261270 Rematch_On_Draw:NO
262271 To_Move:+
272+Max_Moves:#{$options["max-moves"]}
263273 BEGIN Time
264274 Time_Unit:1sec
265275 Total_Time:1500
@@ -296,6 +306,7 @@ Name-:p2
296306 Your_Turn:-
297307 Rematch_On_Draw:NO
298308 To_Move:+
309+Max_Moves:#{$options["max-moves"]}
299310 BEGIN Time
300311 Time_Unit:1sec
301312 Total_Time:1500
@@ -326,6 +337,8 @@ EOF
326337 V2
327338 N+p1
328339 N-p2
340+'Max_Moves:#{$options["max-moves"]}
341+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
329342 $EVENT:#{game.game_id}
330343 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
331344 P2 * -HI * * * * * -KA *
--- a/test/TC_game_least_0.rb
+++ b/test/TC_game_least_0.rb
@@ -7,6 +7,7 @@ require 'shogi_server/player'
77
88 $options = {}
99 $options["least-time-per-move"] = 0
10+$options["max-moves"] = 256
1011
1112 def log_message(str)
1213 $stderr.puts str
@@ -51,6 +52,7 @@ Name-:p2
5152 Your_Turn:+
5253 Rematch_On_Draw:NO
5354 To_Move:+
55+Max_Moves:#{$options["max-moves"]}
5456 BEGIN Time
5557 Time_Unit:1sec
5658 Total_Time:1500
@@ -85,6 +87,7 @@ Name-:p2
8587 Your_Turn:-
8688 Rematch_On_Draw:NO
8789 To_Move:+
90+Max_Moves:#{$options["max-moves"]}
8891 BEGIN Time
8992 Time_Unit:1sec
9093 Total_Time:1500
@@ -113,6 +116,8 @@ EOF
113116 V2
114117 N+p1
115118 N-p2
119+'Max_Moves:#{$options["max-moves"]}
120+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
116121 $EVENT:#{game.game_id}
117122 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
118123 P2 * -HI * * * * * -KA *
@@ -153,6 +158,7 @@ Name-:p2
153158 Your_Turn:+
154159 Rematch_On_Draw:NO
155160 To_Move:-
161+Max_Moves:#{$options["max-moves"]}
156162 BEGIN Time
157163 Time_Unit:1sec
158164 Total_Time:1500
@@ -188,6 +194,7 @@ Name-:p2
188194 Your_Turn:-
189195 Rematch_On_Draw:NO
190196 To_Move:-
197+Max_Moves:#{$options["max-moves"]}
191198 BEGIN Time
192199 Time_Unit:1sec
193200 Total_Time:1500
@@ -217,6 +224,8 @@ EOF
217224 V2
218225 N+p1
219226 N-p2
227+'Max_Moves:#{$options["max-moves"]}
228+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
220229 $EVENT:#{game.game_id}
221230 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
222231 P2 * -HI * * * * * -KA *
@@ -260,6 +269,7 @@ Name-:p2
260269 Your_Turn:+
261270 Rematch_On_Draw:NO
262271 To_Move:+
272+Max_Moves:#{$options["max-moves"]}
263273 BEGIN Time
264274 Time_Unit:1sec
265275 Total_Time:1500
@@ -296,6 +306,7 @@ Name-:p2
296306 Your_Turn:-
297307 Rematch_On_Draw:NO
298308 To_Move:+
309+Max_Moves:#{$options["max-moves"]}
299310 BEGIN Time
300311 Time_Unit:1sec
301312 Total_Time:1500
@@ -326,6 +337,8 @@ EOF
326337 V2
327338 N+p1
328339 N-p2
340+'Max_Moves:#{$options["max-moves"]}
341+'Least_Time_Per_Move:#{$options["least-time-per-move"]}
329342 $EVENT:#{game.game_id}
330343 P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
331344 P2 * -HI * * * * * -KA *
--- a/test/TC_pairing.rb
+++ b/test/TC_pairing.rb
@@ -499,6 +499,12 @@ class TestLeastDiff < Test::Unit::TestCase
499499 assert_pairs([@a,@b,@h], players)
500500 end
501501
502+ def test_match_many_players
503+ players = [@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h,@a,@b,@h]
504+ r = @pairing.match(players)
505+ assert true
506+ end
507+
502508 def test_calculate_diff_with_penalty
503509 players = [@a,@b]
504510 assert_equal(@b.rate-@a.rate, @pairing.calculate_diff_with_penalty(players,nil))
@@ -597,6 +603,13 @@ class TestLeastDiff < Test::Unit::TestCase
597603
598604 assert_equal(@b.rate-200, @pairing.get_player_rate(@x, @history))
599605 end
606+
607+ def test_total_posibilities
608+ assert_equal 1, @pairing.total_posibilities(2)
609+ assert_equal 1, @pairing.total_posibilities(3)
610+ assert_equal 3, @pairing.total_posibilities(4)
611+ assert_equal 945, @pairing.total_posibilities(10)
612+ end
600613 end
601614
602615 class TestExcludeUnratedPlayers < Test::Unit::TestCase
--- a/test/TC_util.rb
+++ b/test/TC_util.rb
@@ -61,3 +61,22 @@ class TestMkdir < Test::Unit::TestCase
6161 end
6262
6363 end
64+
65+class TestFactorial < Test::Unit::TestCase
66+ def test_factorial
67+ assert_equal 1, ShogiServer::factorial(0)
68+ assert_equal 1, ShogiServer::factorial(1)
69+ assert_equal 2, ShogiServer::factorial(2)
70+ assert_equal 6, ShogiServer::factorial(3)
71+ end
72+end
73+
74+class TestnCk < Test::Unit::TestCase
75+ def test_nCk
76+ assert_equal 0, ShogiServer::nCk(2,5)
77+ assert_equal 1, ShogiServer::nCk(2,2)
78+ assert_equal 6, ShogiServer::nCk(4,2)
79+ assert_equal 11*5*9, ShogiServer::nCk(12,4)
80+ end
81+end
82+