OSDN Git Service

When there are odd players, RandomPairing will delete a player at random.
[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       # TODO ??? undefined method `<=>' for nil:NilClass
41       max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
42       return players.delete(max_player)
43     end
44
45     def delete_least_rate_player(players)
46       min_player = players.min {|a,b| a.rate <=> b.rate}
47       return players.delete(min_player)
48     end
49
50     def pairing_and_start_game(players)
51       return if players.size < 2
52       if players.size % 2 == 1
53         log_warning("#Players should be even: %d" % [players.size])
54         return
55       end
56       sorted = players.sort{ rand < 0.5 ? 1 : -1 }
57
58       pairs = [[sorted.shift]]
59       while !sorted.empty? do
60         if pairs.last.size < 2
61           pairs.last << sorted.shift
62         else
63           pairs << [sorted.shift]
64         end 
65       end
66       pairs.each do |pair|
67         start_game(pair.first, pair.last)
68       end
69     end
70   end # Pairing
71
72   class RandomPairing < Pairing
73     def match(players)
74       super
75       return if players.size < 2
76
77       if players.size % 2 == 1
78         delete_player_at_random(players)
79       end
80       pairing_and_start_game(players)
81     end
82   end # RadomPairing
83
84   class SwissPairing < Pairing
85     def match(players)
86       super
87       return if players.size < 2
88
89       win_players = players.find_all {|a| a.last_game_win?}
90       remains     = players - win_players
91       if win_players.size >= 2
92         if win_players.size % 2 == 1
93           remains << delete_least_rate_player(win_players)
94         end         
95         pairing_and_start_game(win_players)
96       else
97         remains.concat(win_players)
98       end
99       return if remains.size < 2
100       if remains.size % 2 == 1
101         delete_player_at_random(remains)
102         # delete_most_playing_player(remains)
103       end
104       pairing_and_start_game(remains)
105     end
106   end # SwissPairing
107
108   class ExcludeSacrifice
109     attr_accessor :sacrifice
110
111     def initialize(pairing)
112       @pairing  = pairing
113       @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
114     end
115
116     def match(players)
117       if @sacrifice && 
118          players.size % 2 == 1 && 
119          players.find{|a| a.id == @sacrifice}
120         log_message("Floodgate: first, exclude %s" % [@sacrifice])
121         players.delete_if{|a| a.id == @sacrifice}
122       end
123       @pairing.match(players)
124     end
125
126     # Delegate to @pairing
127     def method_missing(message, *arg)
128       @pairing.send(message, *arg)
129     end
130   end # class ExcludeSacrifice
131 end # ShogiServer