OSDN Git Service

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