OSDN Git Service

Thread.abort_on_exception = false
[shogi-server/shogi-server.git] / pairing.rb
1 module ShogiServer
2
3   class Pairing
4
5     class << self
6       def default_pairing
7         return SwissPairing.new
8         #return ExcludeSacrifice.new(SwissPairing.new)
9         #return RandomPairing.new
10         #return ExcludeSacrifice.new(RandomPairing.new)
11       end
12     end
13
14     def match(players)
15       if players.size < 2
16         log_message("Floodgate[%s]: too few players [%d]" % 
17                     [self.class, players.size])
18       else
19         log_message("Floodgate[%s]: found %d players. Pairing them..." % 
20                     [self.class, players.size])
21       end
22     end
23
24     def start_game(p1, p2)
25       p1.sente = true
26       p2.sente = false
27       Game.new(p1.game_name, p1, p2)
28     end
29
30     def delete_player_at_random(players)
31       return players.delete_at(rand(players.size))
32     end
33
34     def delete_player_at_random_except(players, a_player)
35       candidates = players - [a_player]
36       return delete_player_at_random(candidates)
37     end
38     
39     def delete_most_playing_player(players)
40       max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
41       return players.delete(max_player)
42     end
43
44     def delete_least_rate_player(players)
45       min_player = players.min {|a,b| a.rate <=> b.rate}
46       return players.delete(min_player)
47     end
48
49     def pairing_and_start_game(players)
50       return if players.size < 2
51       if players.size % 2 == 1
52         log_warning("#Players should be even: %d" % [players.size])
53         return
54       end
55       sorted = players.sort{ rand < 0.5 ? 1 : -1 }
56
57       pairs = [[sorted.shift]]
58       while !sorted.empty? do
59         if pairs.last.size < 2
60           pairs.last << sorted.shift
61         else
62           pairs << [sorted.shift]
63         end 
64       end
65       pairs.each do |pair|
66         start_game(pair.first, pair.last)
67       end
68     end
69   end # Pairing
70
71   class RandomPairing < Pairing
72     def match(players)
73       super
74       return if players.size < 2
75
76       if players.size % 2 == 1
77         delete_most_playing_player(players)
78       end
79       pairing_and_start_game(players)
80     end
81   end # RadomPairing
82
83   class SwissPairing < Pairing
84     def match(players)
85       super
86       return if players.size < 2
87
88       win_players = players.find_all {|a| a.last_game_win?}
89       remains     = players - win_players
90       if win_players.size >= 2
91         if win_players.size % 2 == 1
92           remains << delete_least_rate_player(win_players)
93         end         
94         pairing_and_start_game(win_players)
95       else
96         remains.concat(win_players)
97       end
98       return if remains.size < 2
99       if remains.size % 2 == 1
100         delete_most_playing_player(remains)
101       end
102       pairing_and_start_game(remains)
103     end
104   end # SwissPairing
105
106   class ExcludeSacrifice
107     attr_accessor :sacrifice
108
109     def initialize(pairing)
110       @pairing  = pairing
111       @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
112     end
113
114     def match(players)
115       if @sacrifice && 
116          players.size % 2 == 1 && 
117          players.find{|a| a.id == @sacrifice}
118         log_message("Floodgate: first, exclude %s" % [@sacrifice])
119         players.delete_if{|a| a.id == @sacrifice}
120       end
121       @pairing.match(players)
122     end
123
124     # Delegate to @pairing
125     def method_missing(message, *arg)
126       @pairing.send(message, *arg)
127     end
128   end # class ExcludeSacrifice
129 end # ShogiServer