OSDN Git Service

* [shogi-server]
[shogi-server/shogi-server.git] / shogi_server / pairing.rb
1 ## $Id$
2
3 ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
4 ## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org)
5 ##
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
10 ##
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ## GNU General Public License for more details.
15 ##
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, write to the Free Software
18 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 require 'shogi_server/util'
21
22 module ShogiServer
23
24   class Pairing
25
26     class << self
27       def default_pairing
28         #return SwissPairing.new
29         return ExcludeSacrifice.new(SwissPairing.new)
30         #return RandomPairing.new
31         #return ExcludeSacrifice.new(RandomPairing.new)
32       end
33     end
34
35     def match(players)
36       if players.size < 2
37         log_message("Floodgate[%s]: too few players [%d]" % 
38                     [self.class, players.size])
39       else
40         log_message("Floodgate[%s]: found %d players. Pairing them..." % 
41                     [self.class, players.size])
42       end
43     end
44
45     def start_game(p1, p2)
46       p1.sente = true
47       p2.sente = false
48       Game.new(p1.game_name, p1, p2)
49     end
50
51     def include_newbie?(players)
52       return players.find{|a| a.rate == 0} == nil ? false : true
53     end
54
55     def delete_player_at_random(players)
56       return players.delete_at(rand(players.size))
57     end
58
59     def delete_player_at_random_except(players, a_player)
60       candidates = players - [a_player]
61       return delete_player_at_random(candidates)
62     end
63     
64     def delete_most_playing_player(players)
65       # TODO ??? undefined method `<=>' for nil:NilClass
66       max_player = players.max {|a,b| a.win + a.loss <=> b.win + b.loss}
67       return players.delete(max_player)
68     end
69
70     def delete_least_rate_player(players)
71       min_player = players.min {|a,b| a.rate <=> b.rate}
72       return players.delete(min_player)
73     end
74
75     def pairing_and_start_game(players)
76       return if players.size < 2
77       if players.size % 2 == 1
78         log_warning("#Players should be even: %d" % [players.size])
79         return
80       end
81
82       ShogiServer.shuffle(players)
83
84       pairs = [[players.shift]]
85       while !players.empty? do
86         if pairs.last.size < 2
87           pairs.last << players.shift
88         else
89           pairs << [players.shift]
90         end 
91       end
92       pairs.each do |pair|
93         start_game(pair.first, pair.last)
94       end
95     end
96   end # Pairing
97
98   class RandomPairing < Pairing
99     def match(players)
100       super
101       return if players.size < 2
102
103       if players.size % 2 == 1
104         delete_player_at_random(players)
105       end
106       pairing_and_start_game(players)
107     end
108   end # RadomPairing
109
110   class SwissPairing < Pairing
111     def match(players)
112       super
113       return if players.size < 2
114
115       win_players = players.find_all {|a| a.last_game_win?}
116       remains     = players - win_players
117       if win_players.size >= 2
118         if win_players.size % 2 == 1
119 #          if include_newbie?(win_players)
120             remains << delete_player_at_random(win_players)
121 #          else
122 #            remains << delete_least_rate_player(win_players)
123 #          end
124         end         
125         pairing_and_start_game(win_players)
126       else
127         remains.concat(win_players)
128       end
129       return if remains.size < 2
130       if remains.size % 2 == 1
131         delete_player_at_random(remains)
132         # delete_most_playing_player(remains)
133       end
134       pairing_and_start_game(remains)
135     end
136   end # SwissPairing
137
138   class ExcludeSacrifice
139     attr_accessor :sacrifice
140
141     def initialize(pairing)
142       @pairing  = pairing
143       @sacrifice = "gps500+e293220e3f8a3e59f79f6b0efffaa931"
144     end
145
146     def match(players)
147       if @sacrifice && 
148          players.size % 2 == 1 && 
149          players.find{|a| a.player_id == @sacrifice}
150         log_message("Floodgate: first, exclude %s" % [@sacrifice])
151         players.delete_if{|a| a.player_id == @sacrifice}
152       end
153       @pairing.match(players)
154     end
155
156     # Delegate to @pairing
157     def method_missing(message, *arg)
158       @pairing.send(message, *arg)
159     end
160   end # class ExcludeSacrifice
161 end # ShogiServer