line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# $Id: Swiss.pm 34 2006-06-19 19:19:43Z giel $ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# Algorithm::Pair::Swiss.pm |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright (C) 2006 Gilion Goudsmit ggoudsmit@shebang.nl |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# This library is free software; you can redistribute it and/or modify it |
8
|
|
|
|
|
|
|
# under the same terms as Perl itself, either Perl version 5.8.5 or, at your |
9
|
|
|
|
|
|
|
# option, any later version of Perl 5 you may have available. |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful, but |
12
|
|
|
|
|
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13
|
|
|
|
|
|
|
# or FITNESS FOR A PARTICULAR PURPOSE. |
14
|
|
|
|
|
|
|
# |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Algorithm::Pair::Swiss - Generate unique pairings for tournaments |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 VERSION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
This document describes Algorithm::Pair::Swiss version 0.14 |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 SYNOPSIS |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use Algorithm::Pair::Swiss; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
my $pairer = Algorithm::Pair::Swiss->new; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$pairer->parties(1,2,3,4); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
@round_1 = $pairer->pairs; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
$pairer->exclude(@round_1); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
@round_2 = $pairer->pairs; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 DESCRIPTION |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
This module was created as an alternative for Algorithm::Pair::Best, which |
41
|
|
|
|
|
|
|
probably offers more control over the pairings, in particular regarding |
42
|
|
|
|
|
|
|
ensuring the highest overal quality of pairings. Algorithm::Pair::Swiss is |
43
|
|
|
|
|
|
|
sort of dumb in this regard, but uses a slightly more intuitive interface |
44
|
|
|
|
|
|
|
and an algorithm that should perform noticably faster. The module was |
45
|
|
|
|
|
|
|
primarily designed based on the Swiss rounds system used for Magic: The |
46
|
|
|
|
|
|
|
Gathering tournaments. |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
After creating an Algorithm::Pair::Swiss-EB object, use the B |
49
|
|
|
|
|
|
|
method to supply a list of parties (players or teams) to be paired. At any |
50
|
|
|
|
|
|
|
time the B method can be used to indicate which pairs shouldn't be |
51
|
|
|
|
|
|
|
generated (probably because they've already been paired in an earlier round). |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
The list of parties is sorted and the pairer tries to find a set of pairs that |
54
|
|
|
|
|
|
|
respects the exclude list, and tries to pair the parties that appear first |
55
|
|
|
|
|
|
|
in the sorted list with each other most aggresively. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
To influence the sort order, use objects as parties and overload either the |
58
|
|
|
|
|
|
|
B or B<0+> operators in the object class to sort as desired. |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Algorithm::Pair::Swiss-EB explores the parties and returns the first |
61
|
|
|
|
|
|
|
pairing solution which satisfies the excludes. Because it doesn't exhaustively |
62
|
|
|
|
|
|
|
try all possible solutions, performance is generally pretty reasonable. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
For a large number of parties, it is generally easy to find a non-excluded pair, |
65
|
|
|
|
|
|
|
and for a smaller number of parties traversal of the possible pairs is done |
66
|
|
|
|
|
|
|
reasonably fast. |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
This module uses the parties as keys in a hash, and uses the empty string ('') |
69
|
|
|
|
|
|
|
as a special case in this same hash. For this reason, please observe the |
70
|
|
|
|
|
|
|
following restrictions regarding your party values: |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=over 1 |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=item - make sure it is defined (not undef) |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=item - make sure it is defined when stringified |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=item - make sure each is a non-empty string when stringified |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=item - make sure each is unique when stringified |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=back |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
All the restrictions on the stringifications are compatible with the perl's |
85
|
|
|
|
|
|
|
default stringification of objects, and should be safe for any stringification |
86
|
|
|
|
|
|
|
which returns a unique party-identifier (for instance a primary key from a |
87
|
|
|
|
|
|
|
Class::DBI object). |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
package Algorithm::Pair::Swiss; |
93
|
3
|
|
|
3
|
|
72369
|
use strict; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
106
|
|
94
|
3
|
|
|
3
|
|
17
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
97
|
|
95
|
3
|
|
|
3
|
|
15
|
no warnings 'recursion'; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
260
|
|
96
|
|
|
|
|
|
|
require 5.001; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
our $REVISION = sprintf(q{%d} => q{$Rev: 34 $} =~ /(\d+)/g); |
99
|
|
|
|
|
|
|
our $VERSION = q(0.14); |
100
|
|
|
|
|
|
|
|
101
|
3
|
|
|
3
|
|
15
|
use Carp; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
2458
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
###################################################### |
104
|
|
|
|
|
|
|
# |
105
|
|
|
|
|
|
|
# Public methods |
106
|
|
|
|
|
|
|
# |
107
|
|
|
|
|
|
|
##################################################### |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head1 METHODS |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=over 4 |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=item my $pairer = Bnew>( @parties ) |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
A B Algorithm::Pair::Swiss object is used to generate pairings. |
116
|
|
|
|
|
|
|
Optionally @parties can be given when instantiating the object. This is |
117
|
|
|
|
|
|
|
the same as using the B method described below. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=cut |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub new { |
122
|
4
|
|
|
4
|
1
|
743
|
my $class = shift; |
123
|
4
|
|
|
|
|
15
|
my $self = bless {}, $class; |
124
|
4
|
100
|
|
|
|
22
|
$self->parties(@_) if @_; |
125
|
4
|
|
|
|
|
13
|
return $self; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=item $pairer-EB( @parties ) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Provides the pairer with a complete list of all individuals that can |
131
|
|
|
|
|
|
|
be paired. If no parties are specified, it returns the sorted list |
132
|
|
|
|
|
|
|
of all parties. This allows you to use this method to extract 'rankings' |
133
|
|
|
|
|
|
|
if you happen to have implemented a B operator overload in the |
134
|
|
|
|
|
|
|
class your parties belong to. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub parties { |
139
|
16
|
|
|
16
|
1
|
8605
|
my $self = shift; |
140
|
16
|
100
|
|
|
|
53
|
return sort @{$self->{parties}} unless @_; |
|
12
|
|
|
|
|
341
|
|
141
|
4
|
|
|
|
|
25
|
$self->{parties} = [ @_ ]; |
142
|
4
|
|
|
|
|
10
|
for my $i (@{$self->{parties}}) { |
|
4
|
|
|
|
|
16
|
|
143
|
14
|
50
|
|
|
|
47
|
croak q{All parties must have a defined stringification} |
144
|
|
|
|
|
|
|
unless defined "$i"; |
145
|
14
|
50
|
|
|
|
39
|
croak qq{All parties must have a unique stringification, but "$i" seems to be a duplicate} |
146
|
|
|
|
|
|
|
if exists $self->{exclude}->{"$i"}; |
147
|
14
|
|
|
|
|
41
|
$self->{exclude}->{"$i"}={} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=item @pairs = $pairer-EB |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
Returns the best pairings found as a list of arrayref's, each containing |
154
|
|
|
|
|
|
|
one pair of parties. |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=cut |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub pairs { |
159
|
8
|
|
|
8
|
1
|
39
|
my $self = shift; |
160
|
8
|
|
|
|
|
24
|
my @pairs = _pairs([$self->parties],$self->{exclude}); |
161
|
8
|
|
|
|
|
46
|
return @pairs; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item $pair-EB( @pairs ) |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Excludes the given pairs from further pairing. The @pairs array |
167
|
|
|
|
|
|
|
should consist of a list of references to arrays, each containing the two |
168
|
|
|
|
|
|
|
parties of that pair. This means you can easily feed it the output of |
169
|
|
|
|
|
|
|
a previous call to $pair-EB. The selection given is added |
170
|
|
|
|
|
|
|
to previously excluded pairs. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
If there was an odd number of parties, the lowest ranked party will be |
173
|
|
|
|
|
|
|
paired with 'undef', unless it has already been paired with 'undef'. In |
174
|
|
|
|
|
|
|
that case, the second-lowest ranked party will get that pairing. Etcetera, |
175
|
|
|
|
|
|
|
etcetera. 'Lowest-ranked' is defined as being last in the party-list after |
176
|
|
|
|
|
|
|
sorting. In MTG terms, being paired with 'undef' would mean getting a bye |
177
|
|
|
|
|
|
|
(and getting the full three points for that round as a consequence). |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=cut |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub exclude { |
182
|
6
|
|
|
6
|
1
|
4952
|
my $self = shift; |
183
|
6
|
|
|
|
|
17
|
for my $pair (@_) { |
184
|
12
|
|
|
|
|
40
|
my ($x,$y) = @$pair; |
185
|
12
|
100
|
|
|
|
68
|
$self->{exclude}->{"$x"}->{$y?"$y":''} = 1 if $x; |
|
|
50
|
|
|
|
|
|
186
|
12
|
50
|
|
|
|
124
|
$self->{exclude}->{"$y"}->{$x?"$x":''} = 1 if $y; |
|
|
100
|
|
|
|
|
|
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item $pair-EB( @parties ) |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Excludes the given parties from further pairing. The given parties will |
193
|
|
|
|
|
|
|
be removed from the internal parties list and won't be returned by the |
194
|
|
|
|
|
|
|
parties method anymore. This method is usually used when a participant |
195
|
|
|
|
|
|
|
has decided to quit playing. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub drop { |
200
|
1
|
|
|
1
|
1
|
6617
|
my $self = shift; |
201
|
1
|
|
|
|
|
6
|
my %parties = map { ( "$_" => $_ ) } $self->parties; |
|
3
|
|
|
|
|
16
|
|
202
|
1
|
|
|
|
|
5
|
for my $party (@_) { delete $parties{"$party"} } |
|
1
|
|
|
|
|
6
|
|
203
|
1
|
|
|
|
|
9
|
$self->{parties} = [ values %parties ]; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub _pairs { |
207
|
14
|
|
|
14
|
|
20
|
my ($unpaired,$exclude) = @_; |
208
|
14
|
|
|
|
|
35
|
my @unpaired = @$unpaired; |
209
|
14
|
|
|
|
|
20
|
my $p1 = shift @unpaired; |
210
|
14
|
|
|
|
|
25
|
for my $p2 (@unpaired) { |
211
|
20
|
100
|
|
|
|
62
|
next if exists $exclude->{"$p1"}->{"$p2"}; # already paired |
212
|
9
|
50
|
|
|
|
26
|
next if exists $exclude->{"$p2"}->{"$p1"}; # already paired |
213
|
9
|
100
|
|
|
|
33
|
return [$p1,$p2] if @unpaired==1; # last pair! |
214
|
5
|
|
|
|
|
11
|
my @remaining = grep {"$_" ne "$p2"} @unpaired; # this pair could work |
|
13
|
|
|
|
|
37
|
|
215
|
5
|
|
|
|
|
36
|
my @pairs = _pairs(\@remaining,$exclude); # so try to pair the rest |
216
|
5
|
50
|
|
|
|
21
|
next unless @pairs; # no luck |
217
|
5
|
|
|
|
|
25
|
return [$p1,$p2],@pairs; # yay! return the resultset |
218
|
|
|
|
|
|
|
} |
219
|
5
|
100
|
|
|
|
19
|
if(@unpaired % 2 == 0) { # single player left |
220
|
4
|
100
|
|
|
|
17
|
return if exists $exclude->{"$p1"}->{''}; # already had a bye before |
221
|
3
|
100
|
|
|
|
10
|
return [$p1,undef] unless @unpaired; # return a bye |
222
|
1
|
|
|
|
|
3
|
my @pairs = _pairs(\@unpaired,$exclude); |
223
|
1
|
50
|
|
|
|
4
|
return unless @pairs; |
224
|
1
|
|
|
|
|
3
|
return @pairs,[$p1,undef]; |
225
|
|
|
|
|
|
|
} |
226
|
1
|
|
|
|
|
4
|
return; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
1; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
__END__ |