File Coverage

blib/lib/Games/Tournament/Swiss/Bracket.pm
Criterion Covered Total %
statement 475 558 85.1
branch 165 230 71.7
condition 60 93 64.5
subroutine 51 61 83.6
pod 37 37 100.0
total 788 979 80.4


line stmt bran cond sub pod time code
1             package Games::Tournament::Swiss::Bracket;
2             $Games::Tournament::Swiss::Bracket::VERSION = '0.21';
3             # Last Edit: 2016 Jan 01, 13:44:55
4             # $Id: $
5              
6 26     26   58570 use warnings;
  26         43  
  26         986  
7 26     26   130 use strict;
  26         50  
  26         567  
8 26     26   124 use Carp;
  26         51  
  26         1819  
9              
10 26     26   166 use constant ROLES => @Games::Tournament::Swiss::Config::roles;
  26         41  
  26         1782  
11              
12 26     26   130 use base qw/Games::Tournament::Swiss/;
  26         47  
  26         2592  
13 26     26   548 use Games::Tournament::Contestant::Swiss;
  26         49  
  26         589  
14 26     26   11085 use Games::Tournament::Card;
  26         69  
  26         808  
15 26     26   154 use List::Util qw/max min reduce sum/;
  26         52  
  26         1943  
16 26     26   133 use List::MoreUtils qw/any notall/;
  26         49  
  26         119  
17              
18             =head1 NAME
19              
20             Games::Tournament::Swiss::Bracket - Players with same/similar scores pairable with each other
21              
22             =cut
23              
24             =head1 SYNOPSIS
25              
26             $tourney = Games::Tournament::Swiss>new($rounds, \@entrants);
27             @rankedPlayers = $tourney->assignPairingNumbers;
28             @firstbrackets = $t->formBrackets;
29             ...
30             $tourney->collectCards(@games);
31             @scores = $tourney->updateScores($round);
32             @groups = $tourney->formBrackets;
33              
34             =head1 DESCRIPTION
35              
36             In a Swiss tournament, in each round contestants are paired with other players with the same, or similar, scores. These contestants are grouped into a score group (bracket) in the process of deciding who plays who.
37              
38             The concept of immigration control is applied to impose order on the players floating in and out of these score brackets. That is, floating is like flying.
39             I pulled back on this metaphor. It was probably overengineering.
40              
41             =head1 METHODS
42              
43             =head2 new
44              
45             $group = Games::Tournament::Swiss::Bracket->new( score => 7.5, members => [ $a, $b, $c ], remainderof => $largergroup )
46              
47             members is a reference to a list of Games::Tournament::Contestant::Swiss objects. The order is important. If the score group includes floaters, these members' scores will not be the same as $group->score. Such a heterogenous group is paired in two parts--first the downfloaters, and then the homogeneous remainder group. Remainder groups can be recognized by the existence of a 'remainderof' key that links them to the group they came from. Some members may also float down from a remainder group. Each bracket needs a score to determine the right order they will be paired in. The number, from 1 to the total number of brackets, reflects that order. A3
48              
49             =cut
50              
51             sub new {
52 287     287 1 776 my $self = shift;
53 287         1035 my %args = @_;
54 287         485 my $score = $args{score};
55 287 50       636 die "Bracket has score of: $score?" unless defined $score;
56 287         500 bless \%args, $self;
57 287         595 $args{floatCheck} = "None";
58 287         756 return \%args;
59             }
60              
61              
62             =head2 natives
63              
64             @floaters = $group->natives
65              
66             Returns those members who were in this bracket originally, as that was their birthright, their scores being all the same. One is a native of only one bracket, and you cannot change this status except XXX EVEN by naturalization.
67              
68             =cut
69              
70             sub natives {
71 0     0 1 0 my $self = shift;
72 0 0       0 return () unless @{ $self->members };
  0         0  
73 0         0 my $members = $self->members;
74 0         0 my $foreigners = $self->immigrants;
75             my @natives = grep {
76 0         0 my $member = $_->pairingNumber;
  0         0  
77 0         0 not grep { $member == $_->pairingNumber } @$foreigners
  0         0  
78             } @$members;
79 0         0 return \@natives;
80             }
81              
82              
83             =head2 citizens
84              
85             @floaters = $group->citizens
86              
87             Returns those members who belong to this bracket. These members don't include those have just floated in, even though this floating status may be permanent. One is a citizen of only one bracket, and you cannot change this status except by naturalization.
88              
89             =cut
90              
91             sub citizens {
92 0     0 1 0 my $self = shift;
93 0 0       0 return () unless @{ $self->members };
  0         0  
94 0         0 my $members = $self->members;
95 0         0 my $foreigners = $self->immigrants;
96             my @natives = grep {
97 0         0 my $member = $_->pairingNumber;
  0         0  
98 0         0 not grep { $member == $_->pairingNumber } @$foreigners
  0         0  
99             } @$members;
100 0         0 return \@natives;
101             }
102              
103              
104             =head2 naturalize
105              
106             $citizen = $group->naturalize($foreigner)
107              
108             Gives members who are resident, but not citizens, ie immigrants, having been floated here from other brackets, the same status as natives, making them indistinguishable from them. This will fail if the player is not resident or not an immigrant. Returns the player with their new status.
109              
110             =cut
111              
112             sub naturalize {
113 6     6 1 9 my $self = shift;
114 6         8 my $foreigner = shift;
115 6         11 my $members = $self->residents;
116             return unless any
117 6 50   12   25 { $_->pairingNumber == $foreigner->pairingNumber } @$members;
  12         27  
118 6         21 my $direction = $foreigner->floating;
119 6 50 33     29 return unless $direction eq 'Up' or $direction eq 'Down';
120 6         16 $foreigner->floating('');
121 6         23 return $foreigner;
122             }
123              
124              
125             =head2 immigrants
126              
127             @floaters = @{$group->immigrants}
128              
129             Returns those members who are foreigners, having been floated here from other brackets. At any one point a player may or may not be a foreigner. But if they are, they only can be a foreigner in one bracket.
130              
131             =cut
132              
133             sub immigrants {
134 0     0 1 0 my $self = shift;
135 0 0       0 return () unless @{ $self->members };
  0         0  
136 0         0 my $members = $self->residents;
137 0         0 my @immigrants = grep { $_->floating } @$members;
  0         0  
138 0         0 return \@immigrants;
139             }
140              
141              
142             =head2 downFloaters
143              
144             @floaters = $group->downFloaters
145              
146             Returns those members downfloated here from higher brackets.
147              
148             =cut
149              
150             sub downFloaters {
151 1     1 1 3 my $self = shift;
152 1         3 my $members = $self->members;
153 1 50 33     7 return () unless @$members and $self->trueHetero;
154 1         3 my %members;
155 1         2 for my $member ( @$members )
156             {
157 3 50       9 my $score = defined $member->score? $member->score: 0;
158 3         10 push @{$members{$score}}, $member;
  3         10  
159             }
160 1         5 my $min = min keys %members;
161 1         4 delete $members{$min};
162 1         3 my @floaters = map { @$_ } values %members;
  1         3  
163 1         4 return @floaters;
164             }
165              
166              
167             =head2 upFloaters
168              
169             @s1 = $group->upFloaters
170              
171             Returns those members upfloated from the next bracket.
172              
173             =cut
174              
175             sub upFloaters {
176 57     57 1 74 my $self = shift;
177 57 50       69 return () unless @{ $self->members };
  57         116  
178 57         131 my @members = $self->residents;
179 57 100       86 grep { $_->floating and $_->floating =~ m/^Up/i } @{ $self->members };
  203         490  
  57         113  
180             }
181              
182              
183             =head2 residents
184              
185             $pairables = $bracket->residents
186              
187             Returns the members includeable in pairing procedures for this bracket because they haven't been floated out, or because they have been floated in. That is, they are not an emigrant. At any one point, a player is resident in one and only one bracket, unless they are in transit. At some other point, they may be a resident of another bracket.
188              
189             =cut
190              
191             sub residents {
192 2259     2259 1 2989 my $self = shift;
193 2259         4247 my $members = $self->members;
194 2259         2866 my @residents;
195 2259         4306 my $floated = $self->emigrants;
196 2259         4080 for my $member (@$members) {
197             push @residents, $member
198 8764 50   0   50646 unless any { $member->pairingNumber == $_->pairingNumber } @$floated;
  0         0  
199             }
200 2259         5219 return \@residents;
201             }
202              
203              
204             =head2 emigrants
205              
206             $bracket->emigrants($member)
207             $gone = $bracket->emigrants
208              
209             Sets whether this citizen will not be included in pairing of this bracket. That is whether they have been floated to another bracket for pairing there. Gets all such members. A player may or may not be an emigrant. They can only stop being an emigrant if they move back to their native bracket. To do this, they have to be processed by 'entry'.
210              
211             =cut
212              
213             sub emigrants {
214 2259     2259 1 2719 my $self = shift;
215 2259         3050 my $floater = shift;
216 2259 50       3863 if ($floater) { push @{ $self->{gone} }, $floater; }
  0         0  
  0         0  
217 2259         4152 else { return $self->{gone}; }
218             }
219              
220              
221             =head2 exit
222              
223             $bracket->exit($player)
224              
225             Removes $player from the list of members of the bracket. They are now in the air. So make sure they enter another bracket.
226              
227             =cut
228              
229             sub exit {
230 575     575 1 870 my $self = shift;
231 575         1151 my $members = $self->members;
232 575         840 my $exiter = shift;
233 575         1440 my $myId = $exiter->pairingNumber;
234 575         988 my @stayers = grep { $_->pairingNumber != $myId } @$members;
  1943         4222  
235 575         1206 my $number = $self->number;
236 575 50       1425 croak "Player $myId did not exit Bracket $number" if @stayers == @$members;
237 575         1253 $self->members(\@stayers);
238             #my $immigrants = $self->immigrants;
239             #if ( grep { $_ == $member } @$immigrants ) {
240             # @{ $self->members } = grep { $_ != $member } @$members;
241             #}
242             #else {
243             # $self->emigrants($member);
244             #}
245 575         2028 return;
246             }
247              
248              
249             =head2 entry
250              
251             $bracket->entry($native)
252             $bracket->entry($foreigner)
253              
254             Registers $foreigner as a resident (and was removing $native from the list of emigrants of this bracket, because they have returned from another bracket as in C12, 13).
255              
256             =cut
257              
258             sub entry {
259 575     575 1 765 my $self = shift;
260 575         1154 my $members = $self->residents;
261 575         838 my $enterer = shift;
262 575         1540 my $myId = $enterer->id;
263 575         1286 my $number = $self->number;
264             croak "Player $myId cannot enter Bracket $number. Is already there." if
265 575 50   1268   2459 any { $_->{id} eq $myId } @$members;
  1268         2451  
266 575         1707 unshift @$members, $enterer;
267 575         1252 $self->members(\@$members);
268 575         1832 return;
269             }
270              
271              
272             =head2 reentry
273              
274             $bracket->reentry($member)
275              
276             Removes this native (presumably) member from the list of emigrants of this bracket, because they have returned from another bracket as in C12, 13. Returns undef, if $member wasn't an emigrant. Otherwise returns the updated list of emigrants.
277              
278             =cut
279              
280             sub reentry {
281 0     0 1 0 my $self = shift;
282 0         0 my $returnee = shift;
283 0         0 my $emigrants = $self->emigrants;
284 0 0   0   0 if ( any { $_->pairingNumber == $returnee->pairingNumber } @$emigrants ) {
  0         0  
285             my @nonreturnees = grep {
286 0         0 $_->pairingNumber != $returnee->pairingNumber } @$emigrants;
  0         0  
287             # @{ $self->{gone} } = @nonreturnees;
288 0         0 $self->{gone} = \@nonreturnees;
289 0         0 return @nonreturnees;
290             }
291             #my @updatedlist = grep { $_->id != $returnee->id } @$emigrants;
292             #$self->emigrants($_) for @updatedlist;
293             #return @updatedlist if grep { $_->id == $returnee->id } @$emigrants;
294 0         0 return;
295              
296             }
297              
298              
299             =head2 dissolved
300              
301             $group->dissolved(1)
302             $s1 = $group->s1($players)
303             $s1 = $group->s1
304              
305             Dissolve a bracket, so it is no longer independent, its affairs being controlled by some other group:
306              
307             =cut
308              
309             sub dissolved {
310 6941     6941 1 8718 my $self = shift;
311 6941         8058 my $flag = shift;
312 6941 100       11299 if ( defined $flag )
313             {
314 64         148 $self->{dissolved} = $flag;
315 64 50       205 return $flag? 1: 0;
316             }
317             else {
318 6877 100       25175 return $self->{dissolved}? 1: 0;
319             }
320             }
321              
322              
323             =head2 s1
324              
325             $group->s1
326             $s1 = $group->s1($players)
327             $s1 = $group->s1
328              
329             Getter/setter of the p players in the top half of a homogeneous bracket, or the p downFloaters in a heterogeneous bracket, as an array. A6
330              
331             =cut
332              
333             sub s1 {
334 7091     7091 1 9016 my $self = shift;
335 7091         8492 my $s1 = shift;
336 7091 100       16947 if ( defined $s1 ) {
    100          
337 1984         3123 $self->{s1} = $s1;
338 1984         3859 return $s1;
339             }
340 5099         11437 elsif ( $self->{s1} ) { return $self->{s1}; }
341 8         26 else { $self->resetS12; return $self->{s1}; }
  8         28  
342             }
343              
344              
345             =head2 s2
346              
347             $s2 = $group->s2
348              
349             Getter/Setter of the players in a homogeneous or a heterogeneous bracket who aren't in S1. A6
350              
351             =cut
352              
353             sub s2 {
354 11675     11675 1 15191 my $self = shift;
355 11675         13968 my $s2 = shift;
356 11675 100       27016 if ( defined $s2 ) {
    50          
357 3233         4724 $self->{s2} = $s2;
358 3233         5976 return $s2;
359             }
360 8442         18645 elsif ( $self->{s2} ) { return $self->{s2}; }
361 0         0 else { $self->resetS12; return $self->{s2}; }
  0         0  
362             }
363              
364              
365             =head2 resetS12
366              
367             $group->resetS12
368              
369             Resetter of S1 and S2 to the original members, ranked before exchanges in C8. A6
370              
371             =cut
372              
373             sub resetS12 {
374 1186     1186 1 1756 my $self = shift;
375 1186         2379 my $number = $self->number;
376 1186         2739 my $members = $self->residents;
377 1186 50       2859 return [] unless $#$members >= 1;
378 1186         1476 my (@s1, @s2);
379 26     26   51332 use Games::Tournament;
  26         48  
  26         127602  
380 1186 100       2620 if ( $self->hetero ) {
381 149         205 my %scorers;
382 149         369 for my $member (@$members)
383             {
384 694 100       1752 my $score = defined $member->score? $member->score: 0;
385 694         1694 push @{ $scorers{$score} }, $member;
  694         1967  
386             }
387 149         511 my @scores = reverse sort { $a <=> $b } keys %scorers;
  161         685  
388             #carp @scores . " different scores in Hetero Bracket $number"
389             # if @scores > 2;
390 149         216 @s2 = @{$scorers{$scores[-1]}};
  149         414  
391 149         253 my %s2 = map { $_->pairingNumber => $_ } @s2;
  509         1278  
392 149         604 @s1 = grep { not exists $s2{$_->pairingNumber} } $self->rank(@$members);
  694         1600  
393             }
394             else {
395 1037         2324 my $p = $self->p;
396 1037         4115 @s1 = ( $self->rank(@$members) )[ 0 .. $p - 1 ];
397 1037         6358 @s2 = ( $self->rank(@$members) )[ $p .. $#$members ];
398             }
399 1186         6275 $self->s1(\@s1);
400 1186         2752 $self->s2(\@s2);
401 1186         2141 my @lastS2ids = reverse map { $_->pairingNumber } @s2;
  3167         7723  
402 1186         2278 $self->{lastS2ids} = \@lastS2ids;
403 1186 50   5684   5333 die "undef player in Bracket $number S1, S2" if any { not defined } @s1, @s2;
  5684         7763  
404 1186         4670 return;
405             }
406              
407              
408             =head2 resetShuffler
409              
410             $previous->entry($_) for @returnees;
411             $previous->resetShuffler;
412             return C7;
413              
414             Take precautions to prevent transposing players who are no longer in the bracket in S2, or to make sure they ARE transposed, when finding a different pairing, before returning from C10,12,13 (C11?). Do this by resetting S1 and S2. Don't use this in the wrong place. We don't want to try the same pairing twice.
415              
416             =cut
417              
418             sub resetShuffler {
419 60     60 1 98 my $self = shift;
420 60         117 my $members = $self->members;
421 60         140 my $s1 = $self->s1;
422 60         136 my $s2 = $self->s2;
423 60         114 my %s1 = map { $_->pairingNumber => $_ } @$s1;
  65         181  
424 60         138 my %s2 = map { $_->pairingNumber => $_ } @$s2;
  152         364  
425 60         144 my %members = map { $_->pairingNumber => $_ } @$members;
  219         463  
426             # my %tally; @tally{keys %members} = (0) x keys %members;
427 65     65   398 my $memberChangeTest = ( (notall { exists $members{$_} } keys %s1) or
428 60   66 152   351 (notall { exists $members{$_} } keys %s2) or (@$s1 + @$s2 != @$members));
  152         472  
429 60 100       421 $self->resetS12 if $memberChangeTest;
430             }
431              
432              
433             =head2 p
434              
435             $tables = $group->p
436              
437             Half the number of players in a homogeneous bracket, rounded down to the next lowest integer. Or the number of down floaters in a heterogeneous bracket. Also the number of players in S1, and thus the number of pairings in the pair group. If there are more downfloaters than original members, half the number of players. (See A1,2)A6
438              
439             =cut
440              
441             sub p {
442 3567     3567 1 4472 my $self = shift;
443 3567         6732 my $members = $self->members;
444 3567         5370 my $n = @$members;
445 3567 50       7254 return 0 unless $n >= 2;
446 3567         3798 my $p;
447 3567 100       6947 if ( $self->hetero ) {
448 378         438 my %scorers;
449 378         694 for my $member ( @$members ) {
450 1926 100       4862 my $score = defined $member->score? $member->score: 0;
451 1926         6685 $scorers{$score}++;
452             }
453 378         1412 my $lowestScore = min keys %scorers;
454 378 50       895 return unless defined $lowestScore;
455 378         565 $p = $n - $scorers{$lowestScore};
456 378 50       1218 $p = int( $n / 2 ) if $p > $n/2;
457             }
458             else {
459 3189         5677 $p = int( $n / 2 );
460             }
461 3567         7291 return $p;
462             }
463              
464              
465             =head2 bigGroupP
466              
467             $tables = $group->bigGroupP
468              
469             Half the number of players in a big bracket (group), rounded down to the next lowest integer. Sometimes the number of pairs in a combined bracket, particularly, a heterogeneous bracket and its remainder group is needed. In such cases, p will be just the number of downfloated players, which is not what we want. In a non-heterogeneous bracket, bigGroupP will be the same as p. See C11
470              
471             =cut
472              
473             sub bigGroupP {
474 29     29 1 44 my $self = shift;
475 29         62 my $members = $self->members;
476 29         49 my $n = @$members;
477 29 100       195 if ( $self->{remainderof} )
    50          
478             {
479 17         47 my $remaindered = $self->{remainderof}->members;
480 17         36 $n += @$remaindered;
481             }
482             elsif ( $self->{remaindered} ) {
483 0         0 my $heteroMembers = $self->{remainder}->members;
484 0         0 $n += @$heteroMembers;
485             }
486 29 50       84 return 0 unless $n >= 2;
487 29         58 my $p = int( $n / 2 );
488 29         84 return $p;
489             }
490              
491              
492             =head2 pprime
493              
494             $tables = $group->pprime
495              
496             p is half the number of players in a bracket, but we may have to accept fewer pairings than this number if suitable opponents cannot be found for players, up to the point where p=0. pprime sets/gets this real p number. A8
497              
498             =cut
499              
500             sub pprime {
501 2079     2079 1 3112 my ( $self, $p ) = @_;
502 2079         3053 my $pprime = $self->{pprime};
503 2079 100       5020 if ( defined $p ) { $self->{pprime} = $p; }
  192 100       556  
504 1880         4191 elsif ( defined $pprime ) { return $pprime; }
505             else {
506 7         21 $self->{pprime} = $self->p;
507 7         24 return $self->{pprime};
508             }
509             }
510              
511              
512             =head2 bigGroupPprime
513              
514             $tables = $group->bigGroupPprime
515              
516             bigGroupP is half the number of players in a heterogeneous bracket and its remainder group, but we may have to accept fewer pairings than this number if suitable opponents cannot be found for players, up to the point where no players are paired. bigGroupPprime sets/gets this real p number for the combined groups/brackets. A8
517              
518             =cut
519              
520             sub bigGroupPprime {
521 37     37 1 72 my ( $self, $p ) = @_;
522 37         79 my $bigGroupPprime = $self->{biggrouppprime};
523 37 50       141 if ( defined $p ) {
    100          
524 0         0 $self->{biggrouppprime} = $p;
525 0 0       0 if ( $self->{remainderof} ) {
    0          
526 0         0 $self->{remainderof}->{biggrouppprime} = $p;
527             }
528             elsif ( $self->{remainder} ) {
529 0         0 $self->{remainder}->{biggrouppprime} = $p;
530             }
531 0         0 return;
532             }
533 8         28 elsif ( defined $bigGroupPprime ) { return $bigGroupPprime; }
534             else {
535 29         79 $self->{biggrouppprime} = $self->bigGroupP;
536 29         90 return $self->{biggrouppprime};
537             }
538             }
539              
540              
541             =head2 q
542              
543             $tables = $group->q
544              
545             Number of players in the score bracket divided by 2 and then rounded up. In a homogeneous group with an even number of players, this is the same as p. A8
546              
547             =cut
548              
549             sub q {
550 322     322 1 424 my $self = shift;
551 322         634 my $players = $self->members;
552 322 100       1278 my $q = @$players % 2 ? ( $#$players + 2 ) / 2 : ( $#$players + 1 ) / 2;
553             }
554              
555              
556             =head2 x
557              
558             $tables = $group->x
559              
560             Sets the number, ranging from zero to p, of matches in the score bracket in which players will have their preferences unsatisfied. A8
561              
562             =cut
563              
564             sub x {
565 229     229 1 342 my $self = shift;
566 229         509 my $players = $self->residents;
567             my $numbers = sub {
568 458     458   620 my $n = shift;
569             return scalar grep {
570 458 100       713 $_->preference->role and $_->preference->role eq (ROLES)[$n] }
  1894         4846  
571             @$players;
572 229         815 };
573 229         580 my $w = $numbers->(0);
574 229         494 my $b = $numbers->(1);
575 229         624 my $q = $self->q;
576 229 100       630 my $x = $w >= $b ? $w - $q : $b - $q;
577 229 100       1641 $self->{x} = $x < 0? 0: $x;
578             }
579              
580              
581             =head2 bigGroupX
582              
583             $tables = $group->bigGroupX
584              
585             x is okay for a homogeneous group, uncombined with other groups, but in the case of groups that are interacting to form joined brackets, or in that of a heterogeneous bracket and a remainder group, we need a bigGroupX to tell us how many matches in the total number, ranging from zero to bigGroupP, of matches in the score bracket(s) will have players with unsatisfied preferences. A8
586              
587             =cut
588              
589             sub bigGroupX {
590 93     93 1 126 my $self = shift;
591 93         183 my $players = $self->members;
592             my $w =
593 93 100       176 grep { $_->preference->role and $_->preference->role eq (ROLES)[0] }
  436         1105  
594             @$players;
595 93         169 my $b = @$players - $w;
596 93         283 my $q = $self->q;
597 93 100       258 my $x = $w >= $b ? $w - $q : $b - $q;
598 93         117 my $bigGroupX = $x;
599 93 50       330 if ( $self->{remainderof} ) { $bigGroupX += $self->{remainderof}->x; }
  0 50       0  
600 0         0 elsif ( $self->{remainder} ) { $bigGroupX += $self->{remainder}->x; }
601 93         176 $self->{biggroupx} = $bigGroupX;
602 93         395 return $self->{biggroupx};
603             }
604              
605              
606             =head2 bigGroupXprime
607              
608             $tables = $group->bigGroupXprime
609              
610             xprime is a revised upper limit on matches where preferences are not satisfied, but in the case of a combined bracket (in particular, a heterogeneous bracket and a remainder group) we need a figure for the total number of preference-violating matches over the 2 sections, because the distribution of such matches may change. bigGroupXprime sets/gets this total x number. A8
611              
612             =cut
613              
614             sub bigGroupXprime {
615 244     244 1 363 my $self = shift;
616 244         300 my $x = shift;
617 244         382 my $xprime = $self->{biggroupxprime};
618 244 100       694 if ( defined $x ) {
    100          
619 5         12 $self->{biggroupxprime} = $x;
620 5 50       24 if ( $self->{remainderof} ) {
    50          
621 0         0 $self->{remainderof}->{biggroupxprime} = $x;
622             }
623             elsif ( $self->{remainder} ) {
624 5         13 $self->{remainder}->{biggroupxprime} = $x
625             }
626 5         13 return; }
627 72         243 elsif ( defined $xprime ) { return $xprime; }
628             else {
629 167 100       561 if ( $self->{remainderof} ) {
    100          
630 21         48 my $x = $self->{remainderof}->{biggroupxprime};
631 21 100       93 return $x if defined $x;
632             }
633             elsif ( $self->{remainder} ) {
634 53         99 $x = $self->{remainder}->{biggroupxprime};
635 53 50       285 return $x if defined $x;
636             }
637 93         247 else { return $self->bigGroupX; }
638             }
639             }
640              
641              
642             =head2 xprime
643              
644             $tables = $group->xprime
645              
646             x is the lower limit on matches where preferences are not satisfied, but the number of such undesirable matches may be increased if suitable opponents cannot be found for players, up to the point where only players with Absolute preferences have their preferences satisfied. xprime sets/gets this real x number. A8
647              
648             =cut
649              
650             sub xprime {
651 1716     1716 1 2375 my $self = shift;
652 1716         2072 my $x = shift;
653 1716         2577 my $xprime = $self->{xprime};
654 1716 100       4241 if ( defined $x ) { $self->{xprime} = $x; return; }
  77 100       134  
  77         170  
655 1553         3683 elsif ( defined $xprime ) { return $xprime; }
656             else {
657 86         188 $self->{xprime} = $self->x;
658 86         346 return $self->{xprime};
659             }
660             }
661              
662              
663             =head2 floatCheckWaive
664              
665             $tables = $group->floatCheckWaive
666              
667             There is an ordered sequence in which the checks of compliance with the Relative Criteria B5,6 restriction on recurring floats are relaxed in C9,10. The order is 1. downfloats for players downfloated 2 rounds before, 2. downfloats for players downfloated in the previous round (in C9), 3. upfloats for players floated up 2 rounds before, 4. upfloats for players floated up in the previous round (for players paired with opponents from a higher bracket in a heterogeneous bracket, in C10). (It appears levels are being skipped, eg from B6Down to B6Up or from All to B6Down.) Finally, although it is not explicitly stated, all float checks must be dropped and pairings considered again, before reducing the number of pairs made in the bracket. (This is not entirely correct.) This method sets/gets the float check waive level at the moment. All criteria below that level should be checked for compliance. The possible values in order are 'None', 'B6Down', 'B5Down', 'B6Up', 'B5Up', 'All'. TODO Should there be some way of not requiring the caller to know how to use this method and what the levels are.
668              
669             =cut
670              
671             sub floatCheckWaive {
672 1187     1187 1 1559 my $self = shift;
673 1187         2366 my $number = $self->number;
674 1187         1818 my $level = shift;
675 1187 50 66     3928 warn "Unknown float level: $level" if
676             $level and $level !~ m/^(?:None|B6Down|B5Down|B6Up|B5Up|All)$/i;
677 1187         1838 my $oldLevel = $self->{floatCheck};
678 1187 100       2912 if ( defined $level ) {
    50          
679 222 50 66     2443 warn
      66        
      100        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      33        
      66        
      33        
      33        
680             "Bracket [$number]'s old float check waive level, $oldLevel is now $level."
681             unless $level eq 'None' or
682             $oldLevel eq 'None' and $level eq 'B6Down' or
683             $oldLevel eq 'B6Down' and $level eq 'B5Down' or
684             $oldLevel eq 'B6Down' and $level eq 'B6Up' or
685             $oldLevel eq 'B5Down' and $level eq 'B6Up' or
686             $oldLevel eq 'B6Up' and $level eq 'B5Up' or
687             $oldLevel eq 'B5Up' and $level eq 'All' or
688             # $oldLevel eq 'B5Down' and $level eq 'All' or
689             $oldLevel eq 'All' and $level eq 'None' or
690             $oldLevel eq 'All' and $level eq 'B6Down';
691 222         624 $self->{floatCheck} = $level;
692             }
693 965         4318 elsif ( defined $self->{floatCheck} ) { return $self->{floatCheck}; }
694 0         0 else { return; }
695             }
696              
697              
698             =head2 hetero
699              
700             $group->hetero
701              
702             Gets (but doesn't set) whether this group is heterogeneous, ie includes players who have been downfloated from a higher score group, or upfloated from a lower score group, or if it is homogeneous, ie every player has the same score. A group where half or more of the members have come from a higher bracket is regarded as homogeneous. We use the scores of the players, rather than a floating flag.
703              
704             =cut
705              
706             sub hetero {
707 6964     6964 1 8810 my $self = shift;
708 6964         7716 my @members = @{$self->members};
  6964         12626  
709 6964         9161 my %tally;
710 6964         10848 for my $member ( @members ) {
711 33576 100       83242 my $score = defined $member->score? $member->score: 0;
712 33576         109379 $tally{$score}++ ;
713             }
714 6964         15677 my @range = keys %tally;
715 6964 100       31932 return 0 if @range == 1;
716 1773         6034 my $min = min @range;
717 1773 50       3529 return unless defined $min;
718 1773 100       7262 return 0 if $tally{$min} <= @members/2;
719 1116 50       7397 return 1 if $tally{$min} > @members/2;
720 0         0 return;
721             }
722              
723              
724             =head2 trueHetero
725              
726             $group->trueHetero
727              
728             Gets whether this group is really heterogeneous, ie includes players with different scores, because they been downfloated from a higher score group, or upfloated from a lower score group, even if it is being treated as homogeneous. A group where half or more of the members have come from a higher bracket is regarded as homogeneous, but it is really heterogeneous.
729              
730             =cut
731              
732             sub trueHetero {
733 1     1 1 2 my $self = shift;
734 1         3 my @members = @{$self->members};
  1         12  
735 1         3 my %tally;
736 1         2 for my $member ( @members ) {
737 3 50       10 my $score = defined $member->score? $member->score: 0;
738 3         13 $tally{$score}++;
739             }
740 1         4 my @range = keys %tally;
741 1 50       4 return unless @range;
742 1 50       3 return 0 if @range == 1;
743 1         6 return 1;
744             }
745              
746              
747             =head2 c7shuffler
748              
749             $nextS2 = $bracket->c7shuffler($firstmismatch)
750             if ( @nextS2 compatible )
751             {
752             create match cards;
753             }
754              
755             Gets the next permutation of the second-half players in D1 transposition counting order, as used in C7, that will not have the same incompatible player in the bad position found in the present transposition. If you get an illegal modulus error, check your $firstmismatch is a possible value.
756              
757             =cut
758              
759             sub c7shuffler {
760 1719     1719 1 3510 my $self = shift;
761 1719         2105 my $position = shift;
762 1719         2024 my $bigLastGroup = shift;
763 1719         3232 my $s2 = $self->s2;
764 1719 50       4061 die "C7 shuffle: pos $position past end of S2" if $position > $#$s2;
765 1719         4765 my @players = $self->rank(@$s2);
766 1719 50       8532 @players = $self->reverseRank(@$s2) if $bigLastGroup;
767             # my @players = @$s2;
768 1719         3752 my $p = $self->p;
769 1719         2211 my @pattern;
770 1719         3189 my @copy = @players;
771 1719         3852 for my $i ( 0 .. $#$s2 ) {
772 5381         6269 my $j = 0;
773 5381         14188 $j++ until $s2->[$i]->pairingNumber == $copy[$j]->pairingNumber;
774 5381         7852 $pattern[$i] = $j;
775 5381         9677 splice @copy, $j, 1;
776             }
777 1719         2702 my $value = $pattern[$position];
778 1719         1900 my @nextPattern;
779 1719         4175 @nextPattern[ 0 .. $position ] = @pattern[ 0 .. $position ];
780 1719         4382 @nextPattern[ $position + 1 .. $#pattern ] =
781             (0) x ( $#pattern - $position );
782 1719         3238 for my $digit ( reverse( 0 .. $position ) ) {
783 2730 50       5701 die "${digit}th digit overrun of @pattern \@pattern" if
784             @pattern == $digit;
785 2730         4574 $nextPattern[$digit] = ++$value % ( @pattern - $digit );
786 2730 100       6715 last unless $nextPattern[$digit] == 0;
787             }
788 1481         2729 continue { $value = $pattern[ $digit - 1 ]; }
789 1719 100       2840 return unless grep { $_ } @nextPattern;
  5381         10586  
790 1249         1392 my @permutation;
791 1249         1930 for my $pos (@nextPattern) {
792 4259         7234 push @permutation, splice( @players, $pos, 1 );
793             }
794 1249         5423 return @permutation;
795              
796             #my @selectS2 = $group->c7shuffler($badpair);
797             #my @unselectS2 = @$s2;
798             #for my $position ( 0 .. $#$s2 )
799             #{
800             # my $player = $s2->[$#$s2 - $position];
801             # splice @unselectS2, $#$s2 - $position, 1 if grep{$_ eq $player} @selectS2;
802             #}
803             #my @newS2 = (@selectS2, @unselectS2);
804             }
805              
806              
807             =head2 c7iterator
808              
809             $next = $bracket->c7iterator
810             while ( my @s2 = &$next )
811             {
812             create match cards unless this permutation is incompatible;
813             }
814              
815             DEPRECATED Creates an iterator for the permutation of the second-half players in D1 transposition counting order, as used in C7. Only as many players as are in S1 can be matched, so we get only the permutations of all the p-length combinations of members of S2. Deprecated because if C1 or C6 finds a player in a certain position in S2 should not be paired with the player in the corresponding position in S1, we need to be able to skip ahead to the next permutation where a different player is in that position.
816              
817             =cut
818              
819             sub c7iterator {
820 0     0 1 0 my $self = shift;
821 0         0 my $players = $self->s2;
822 0         0 my $p = $self->p;
823 0         0 my $n = 0;
824             return sub {
825 0     0   0 my @pattern = n_to_pat->( $n, $#$players + 1, $p );
826 0         0 my @result = permGenerator->( \@pattern, $players );
827 0         0 print "transposition $n:\t";
828 0         0 $n++;
829 0         0 return @result;
830 0         0 };
831             my $permGenerator = sub {
832 0     0   0 my $pattern = shift;
833 0         0 my @items = @{ shift() };
  0         0  
834 0         0 my @r;
835 0         0 for my $pos (@$pattern) {
836 0         0 push @r, splice( @items, $pos, 1 );
837             }
838 0         0 return @r;
839 0         0 };
840             my $n_to_pat = sub {
841 0     0   0 my @odometer;
842 0         0 my ( $n, $length, $k ) = @_;
843 0         0 for my $i ( $length - $k + 1 .. $length ) {
844 0         0 unshift @odometer, $n % $i;
845 0         0 $n = int( $n / $i );
846             }
847 0 0       0 return $n ? () : @odometer;
848 0         0 };
849             }
850              
851              
852             =head2 c8iterator
853              
854             $next = $bracket->c8iterator
855             while ( my @members = &$next )
856             {
857             next if grep {$incompat{$s1[$_]}{$s2[$_]}} 0..$p-1);
858             }
859              
860             Creates an iterator for the exchange of @s1 and @s2 players in D2 order, as used in C8. Exchanges are performed in order of the difference between the pairing numbers of the players exchanged. If the difference is equal, the exchange with the lowest player is to be performed first. XXX Only as many players as in S1 can be matched, so does this mean some exchanges don't have an effect? I don't understand the description when there are an odd number of players. There appears to be a bug with only 3 players. 1 and 2 should be swapped, I think. I think the order of exchanges of 2 players each may also have some small inconsistencies with the FIDE order.
861              
862             =cut
863              
864             sub c8iterator {
865 247     247 1 377 my $self = shift;
866 247         416 my $letter = 'a';
867 247         569 my $p = $self->p;
868 247         344 my $oddBracket = @{$self->members} % 2;
  247         533  
869 247         322 my @exchanges;
870 247 100       640 unless ($oddBracket)
    50          
871             {
872             @exchanges = map {
873 162         464 my $i = $_;
  189         251  
874 189         764 map { [ [ $_, $_+$i ] ] }
  230         854  
875             reverse( ( max 1, $p-$i ) .. ( min $p-1, 2*($p-1)-$i ) )
876             } ( 1 .. 2*($p-1)-1 );
877             }
878             elsif ( $oddBracket ) {
879 85         132 my $pPlus = $p+1;
880             @exchanges = map {
881 85         246 my $i = $_;
  109         128  
882 109         476 map { [ [ $_-1, $_+$i-1 ] ] }
  129         538  
883             reverse( (max 1, $pPlus-$i) .. (min $pPlus-1, 2*($pPlus-1)-$i) )
884             } ( 1 .. 2*($pPlus-1)-1 );
885             }
886 247         335 my @exchanges2;
887 247 100       594 unless ($oddBracket)
    50          
888             {
889             my @s1pair = map {
890 162         340 my $i = $_;
  33         44  
891 33         62 map { [ $i - $_, $i ] } 1 .. $i - 1
  37         115  
892             } reverse 2 .. $p - 1;
893             my @s2pair = map {
894 162         349 my $i = $_;
  33         46  
895 33         73 map { [ $i, $i + $_ ] } 1 .. 2 * ( $p - 1 ) - $i
  37         121  
896             } $p .. 2 * ( $p - 1 ) - 1;
897             @exchanges2 = map {
898 162         419 my $i = $_;
  44         55  
899             map {
900 44         211 [
901 73         422 [ $s1pair[$_][0], $s2pair[ $i - $_ ][0] ],
902             [ $s1pair[$_][1], $s2pair[ $i - $_ ][1] ]
903             ]
904             } ( max 0, $i - ( $p - 1 ) * ( $p - 2 ) / 2 + 1 )
905             .. ( min( ( $p - 1 ) * ( $p - 2 ) / 2 - 1, $i ) )
906             } 0 .. ( $p - 1 ) * ( $p - 2 ) - 2;
907             }
908             elsif ($oddBracket)
909             {
910 85         127 my $pPlus = $p+1;
911             my @s1pair = map {
912 85         291 my $i = $_;
  3         4  
913 3         4 map { [ $i - $_-1, $i-1 ] } 1 .. $i-1
  7         18  
914             } reverse 3 .. $pPlus - 1;
915             my @s2pair = map {
916 85         189 my $i = $_;
  12         19  
917 12         23 map { [ $i-1, $i+$_-1 ] } 1 .. 2 * ( $pPlus - 1 ) - $i
  16         50  
918             } $pPlus .. 2 * ( $pPlus - 1 ) - 1;
919             @exchanges2 = map {
920 85         212 my $i = $_;
  14         18  
921             map {
922 14         63 [
923 36         143 [ $s1pair[$_][0], $s2pair[ $i - $_ ][0] ],
924             [ $s1pair[$_][1], $s2pair[ $i - $_ ][1] ]
925             ]
926             } ( max 0, $i - ( $pPlus - 1 ) * ( $pPlus - 2 ) / 2 + 1 )
927             .. ( min( ( $pPlus - 1 ) * ( $pPlus - 2 ) / 2 - 2, $i ) )
928             } 0 .. ( $pPlus - 1 ) * ( $pPlus - 2 ) - 3;
929             }
930 247         383 push @exchanges, @exchanges2;
931             return sub {
932 553     553   315294 my $exchange = shift @exchanges;
933 553 100       1590 return ("last S1,S2 exchange") unless $exchange;
934 376         933 $self->resetS12;
935 376         820 my $s1 = $self->s1;
936 376         768 my $s2 = $self->s2;
937 376         939 my @members = (@$s1, @$s2);
938             # my @members = @{ $self->members };
939             ( $members[ $_->[0] ], $members[ $_->[1] ] ) =
940             ( $members[ $_->[1] ], $members[ $_->[0] ] )
941 376         1809 for @$exchange;
942 376         802 my $number = $letter++;
943             die "undef player in exchange $number of S1, S2" if
944 376 50       1466 any { not defined } @members;
  2329         2918  
945 376         2563 return "exchange $number", @members;
946             }
947 247         1722 }
948              
949              
950             =head2 score
951              
952             $group->score
953              
954             Gets/sets the score of the score group.
955              
956             =cut
957              
958             sub score {
959 380     380 1 520 my $self = shift;
960 380         485 my $score = shift;
961 380 50       1119 if ( defined $score ) { $self->{score} = $score; }
  0 50       0  
962 380         1435 elsif ( exists $self->{score} ) { return $self->{score}; }
963 0         0 return;
964             }
965              
966              
967             =head2 number
968              
969             $group->number
970              
971             Gets/sets the bracket's number, a number from 1 to the number of separate brackets, remainder groups and bye groups in the tournament. Don't use this number for anything important.
972              
973             =cut
974              
975             sub number {
976 9645     9645 1 12219 my $self = shift;
977 9645         11854 my $number = shift;
978 9645 50       27414 if ( defined $number ) { $self->{number} = $number; }
  0 100       0  
979 9494         20788 elsif ( exists $self->{number} ) { return $self->{number}; }
980 151         285 return;
981             }
982              
983              
984             =head2 badpair
985              
986             $group->badpair
987              
988             Gets/sets the badpair, the position, counting from zero, of the first pair in S1 and S2 for which pairing failed in a previous attempt in C6. This is the first position at which the next ordering of S2 will differ from the previous one. All orderings between these two orderings will not result in a criteria-compliant pairing.
989              
990             =cut
991              
992             sub badpair {
993 4315     4315 1 5462 my $self = shift;
994 4315         5201 my $badpair = shift;
995 4315 100       10818 if ( defined $badpair ) { $self->{badpair} = $badpair; }
  1384 100       2341  
996 2801         6046 elsif ( defined $self->{badpair} ) { return $self->{badpair}; }
997 1514         2867 return;
998             }
999              
1000              
1001             =head2 members
1002              
1003             $group->members
1004              
1005             Gets/sets the members of the score group as an anonymous array of player objects. The order of this array is important. The first half is paired with the second half.
1006              
1007             =cut
1008              
1009             sub members {
1010 18456     18456 1 22207 my $self = shift;
1011 18456         21474 my $members = shift;
1012 18456 100       49087 if ( defined $members ) { $self->{members} = $members; }
  2103 100       3409  
1013 16264         37510 elsif ( $self->{members} ) { return $self->{members}; }
1014 2192         4390 return;
1015             }
1016              
1017              
1018             =head2 c8swapper
1019              
1020             $pairing->c8swapper
1021              
1022             Gets/sets an iterator through the different exchanges of players in the two halves of the bracket.
1023              
1024             =cut
1025              
1026             sub c8swapper {
1027 973     973 1 1225 my $self = shift;
1028 973         1288 my $c8swapper = shift;
1029 973 100       2900 if ( defined $c8swapper ) { $self->{c8swapper} = $c8swapper; }
  223 100       1152  
1030 696         1960 elsif ( $self->{c8swapper} ) { return $self->{c8swapper}; }
1031             }
1032              
1033              
1034             =head2 _floatCheck
1035              
1036             %b65TestResults = _floatCheck( \@testee, $checkLevels );
1037              
1038             Takes a list representing the pairing of a bracket (see the description for _getNonPaired), and the various up- and down-float check levels. Returns an anonymous hash with keys: (a) 'badpos', the first element of the list responsible for violation of B6 or 5, if there was a violation of any of the levels, (b) 'passer', an anonymous array of the same form as \@testee, if there was no violation of any of the levels, and (c) 'message', a string noting the reason why the pairing is in violation of B6 or 5, and the id of the player involved. If there are multiple violations, the most important one is/should be returned.
1039              
1040             =cut
1041              
1042             sub _floatCheck {
1043 272     272   364 my $self = shift;
1044 272         364 my $untested = shift;
1045 272         516 my @paired = @$untested;
1046 272         754 my @nopairs = $self->_getNonPaired(@paired);
1047 272         417 my $levels = shift;
1048 272 50 33     1434 die "Float checks are $levels?" unless $levels and ref($levels) eq 'ARRAY';
1049 272         594 my $pprime = $self->pprime;
1050 272         570 my $s1 = $self->s1;
1051 272         359 my ($badpos, %badpos);
1052 272         476 my @pairtestee = @paired;
1053 272         676 my @nopairtestee = @nopairs;
1054 272         591 my @pairlevelpasser;
1055             my @nopairlevelpasser;
1056 0         0 my $message;
1057 272         763 B56: for my $level (@$levels)
1058             {
1059 882         1046 my ($round, $direction, $checkedOne, $id);
1060 882 100       2573 if ( $level =~ m/^B5/i ) { $round = 1; }
  367         534  
1061 515         716 else { $round = 2; }
1062 882 100       3069 if( $level =~ m/Down$/i) { $direction = 'Down'; $checkedOne = 0 }
  315 100       481  
  315         411  
1063 375         525 elsif ( $level =~ m/Up$/i ) { $direction = 'Up'; $checkedOne = 1 }
  375         476  
1064 192         372 else { @pairlevelpasser = @pairtestee; last B56 }
  192         423  
1065 690         1447 for my $pos ( 0 .. $#$s1 ) {
1066 940 100       2046 next unless defined $pairtestee[$pos];
1067 937         1951 my @pair = ( $pairtestee[$pos]->[0], $pairtestee[$pos]->[1] );
1068 937 100       1371 my @score = map { defined $_->score? $_->score: 0 } @pair;
  1874         4864  
1069 937         1892 my @float = map { $_->floats( -$round ) } @pair;
  1874         5084  
1070 937         1377 my $test = 0;
1071 937 50 100     4327 $test = ( $score[0] == $score[1] or $float[$checkedOne] ne
1072             $direction ) unless $direction eq 'None';# XXX check both?
1073 937 100       1601 if ( $test ) { $pairlevelpasser[$pos] = \@pair; }
  869         2856  
1074             else {
1075 68 50       233 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1076 68 50       144 $badpos = defined $badpos? $badpos: $pos;
1077 68   33     297 $id ||= $pair[$checkedOne]->pairingNumber;
1078             }
1079             }
1080 690 100 100     3124 if ($direction ne 'Up' and @nopairtestee and ( not $self->hetero or
      100        
      66        
1081             (grep {defined} @nopairtestee) == 1 ))
1082             {
1083             #my $potentialDownFloaters =
1084             # grep { grep { defined } @$_ } @nopairtestee;
1085 128         290 for my $pos ( 0 .. $#nopairtestee ) {
1086 314 100       699 next unless defined $nopairtestee[$pos];
1087 128 50 33     680 my @pair = @{ $nopairtestee[$pos] } if defined
  128         303  
1088             $nopairtestee[$pos] and ref $nopairtestee[$pos] eq 'ARRAY';
1089 128         188 my $tableTest = 0;
1090 128         142 my $idCheck;
1091 128         222 for my $player ( @pair) {
1092 256   100     875 my $test = ( not defined $player or
1093             ($player->floats(-$round) ne "Down") );
1094 256 100 33     952 $idCheck ||= $player->pairingNumber if $player and
      100        
1095             not $test;
1096 256 100       667 $tableTest++ if $test;
1097             }
1098 128 100       252 if ( $tableTest >= 2 ) { $nopairlevelpasser[$pos] = \@pair; }
  116         340  
1099             else {
1100 12 50       41 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1101 12 50       28 $badpos = defined $badpos? $badpos: $pos;
1102 12 50       44 $id = $idCheck if $idCheck;
1103             }
1104             }
1105             }
1106 690         1128 my @retainables = grep { defined } @pairlevelpasser ;#
  884         2168  
1107             # , grep { defined } @nopairlevelpasser;
1108             # my @nonfloaters = grep { grep { defined } @$_ } @retainables;
1109 690 100 100     3534 if ( @retainables < $pprime or keys %badpos )
1110             # if ( @retainables < $pprime or $badpos )
1111             {
1112 80         103 my $badpos;
1113 80         150 for my $nextLevel ( @$levels )
1114             {
1115 146 100       361 next unless defined $badpos{ $nextLevel };
1116 80         114 $badpos = $badpos{ $nextLevel };
1117 80         117 last;
1118             }
1119 80         121 my $pluspos = $badpos+1;
1120 80         278 $message =
1121             "$level, table $pluspos: $id NOK. Floated $direction $round rounds ago";
1122 80         674 return badpos => $badpos, passer => undef, message => $message;
1123             }
1124             }
1125             continue {
1126 610         1243 @pairtestee = @pairlevelpasser;
1127 610         957 @nopairtestee = @nopairlevelpasser;
1128 610         914 undef @pairlevelpasser;
1129 610         1149 undef @nopairlevelpasser;
1130             }
1131 192         1381 return badpos => undef, passer => \@pairlevelpasser, message => "B56: OK.";
1132             }
1133              
1134              
1135             =head2 _getNonPaired
1136              
1137             $bracket->_getNonPaired([$alekhine,$uwe],undef,[$deepblue,$yournewnike])
1138              
1139             Takes a list representing the pairing of S1 and S2. Each element of the list is either a 2-element anonymous array ref (an accepted pair of players), or undef (a rejected pair.) Returns an array of the same form, but with the accepted player items replaced by undef and the undef items replaced by the pairs rejected. If there are more players in S2 than S1, those players are represented as [undef,$player].
1140              
1141             =cut
1142              
1143             sub _getNonPaired {
1144 464     464   592 my $self = shift;
1145 464         822 my @pairables = @_;
1146 464         999 my $s1 = $self->s1;
1147 464         975 my $s2 = $self->s2;
1148 464         626 my @nopairs;
1149 464         1017 for my $pos ( 0..$#pairables )
1150             {
1151 599 50       1539 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ] unless
1152             defined $pairables[$pos];
1153             }
1154 464         1116 for my $pos ( $#pairables+1 .. $#$s1 )
1155             {
1156 2         8 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ];
1157             }
1158 464         1055 for my $pos ( $#$s1+1 .. $#$s2 )
1159             {
1160 753         2003 $nopairs[$pos] = [ undef, $s2->[$pos] ];
1161             }
1162 464         1554 return @nopairs;
1163             }
1164              
1165              
1166             =head1 AUTHOR
1167              
1168             Dr Bean, C<< >>
1169              
1170             =head1 BUGS
1171              
1172             Please report any bugs or feature requests to
1173             C, or through the web interface at
1174             L.
1175             I will be notified, and then you'll automatically be notified of progress on
1176             your bug as I make changes.
1177              
1178             =head1 SUPPORT
1179              
1180             You can find documentation for this module with the perldoc command.
1181              
1182             perldoc Games::Tournament::Swiss::Bracket
1183              
1184             You can also look for information at:
1185              
1186             =over 4
1187              
1188             =item * AnnoCPAN: Annotated CPAN documentation
1189              
1190             L
1191              
1192             =item * CPAN Ratings
1193              
1194             L
1195              
1196             =item * RT: CPAN's request tracker
1197              
1198             L
1199              
1200             =item * Search CPAN
1201              
1202             L
1203              
1204             =back
1205              
1206             =head1 ACKNOWLEDGEMENTS
1207              
1208             See L for the FIDE's Swiss rules.
1209              
1210             =head1 COPYRIGHT & LICENSE
1211              
1212             Copyright 2006 Dr Bean, all rights reserved.
1213              
1214             This program is free software; you can redistribute it and/or modify it
1215             under the same terms as Perl itself.
1216              
1217             =cut
1218              
1219             1; # End of Games::Tournament::Swiss::Bracket
1220              
1221             # vim: set ts=8 sts=4 sw=4 noet: