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.20';
3             # Last Edit: 2016 Jan 01, 13:44:55
4             # $Id: $
5              
6 26     26   60209 use warnings;
  26         45  
  26         886  
7 26     26   135 use strict;
  26         103  
  26         615  
8 26     26   128 use Carp;
  26         55  
  26         1860  
9              
10 26     26   171 use constant ROLES => @Games::Tournament::Swiss::Config::roles;
  26         47  
  26         1749  
11              
12 26     26   131 use base qw/Games::Tournament::Swiss/;
  26         42  
  26         2698  
13 26     26   582 use Games::Tournament::Contestant::Swiss;
  26         56  
  26         592  
14 26     26   11342 use Games::Tournament::Card;
  26         63  
  26         807  
15 26     26   158 use List::Util qw/max min reduce sum/;
  26         51  
  26         2003  
16 26     26   132 use List::MoreUtils qw/any notall/;
  26         51  
  26         125  
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 612 my $self = shift;
53 287         1104 my %args = @_;
54 287         486 my $score = $args{score};
55 287 50       729 die "Bracket has score of: $score?" unless defined $score;
56 287         516 bless \%args, $self;
57 287         594 $args{floatCheck} = "None";
58 287         757 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 8 my $self = shift;
114 6         9 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         25 my $direction = $foreigner->floating;
119 6 50 33     33 return unless $direction eq 'Up' or $direction eq 'Down';
120 6         15 $foreigner->floating('');
121 6         24 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 2 my $self = shift;
152 1         2 my $members = $self->members;
153 1 50 33     6 return () unless @$members and $self->trueHetero;
154 1         2 my %members;
155 1         3 for my $member ( @$members )
156             {
157 3 50       8 my $score = defined $member->score? $member->score: 0;
158 3         12 push @{$members{$score}}, $member;
  3         10  
159             }
160 1         6 my $min = min keys %members;
161 1         3 delete $members{$min};
162 1         3 my @floaters = map { @$_ } values %members;
  1         3  
163 1         5 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 77 my $self = shift;
177 57 50       72 return () unless @{ $self->members };
  57         110  
178 57         129 my @members = $self->residents;
179 57 100       97 grep { $_->floating and $_->floating =~ m/^Up/i } @{ $self->members };
  203         496  
  57         119  
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 3011 my $self = shift;
193 2259         4400 my $members = $self->members;
194 2259         2942 my @residents;
195 2259         4672 my $floated = $self->emigrants;
196 2259         4297 for my $member (@$members) {
197             push @residents, $member
198 8764 50   0   50356 unless any { $member->pairingNumber == $_->pairingNumber } @$floated;
  0         0  
199             }
200 2259         5201 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 2794 my $self = shift;
215 2259         2914 my $floater = shift;
216 2259 50       3886 if ($floater) { push @{ $self->{gone} }, $floater; }
  0         0  
  0         0  
217 2259         4089 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 791 my $self = shift;
231 575         1168 my $members = $self->members;
232 575         809 my $exiter = shift;
233 575         1390 my $myId = $exiter->pairingNumber;
234 575         966 my @stayers = grep { $_->pairingNumber != $myId } @$members;
  1943         4339  
235 575         1208 my $number = $self->number;
236 575 50       1441 croak "Player $myId did not exit Bracket $number" if @stayers == @$members;
237 575         1210 $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         1880 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 828 my $self = shift;
260 575         1217 my $members = $self->residents;
261 575         831 my $enterer = shift;
262 575         1511 my $myId = $enterer->id;
263 575         1263 my $number = $self->number;
264             croak "Player $myId cannot enter Bracket $number. Is already there." if
265 575 50   1268   2390 any { $_->{id} eq $myId } @$members;
  1268         2463  
266 575         1797 unshift @$members, $enterer;
267 575         1276 $self->members(\@$members);
268 575         1734 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 8537 my $self = shift;
311 6941         7982 my $flag = shift;
312 6941 100       12057 if ( defined $flag )
313             {
314 64         151 $self->{dissolved} = $flag;
315 64 50       211 return $flag? 1: 0;
316             }
317             else {
318 6877 100       26201 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 9535 my $self = shift;
335 7091         8818 my $s1 = shift;
336 7091 100       17759 if ( defined $s1 ) {
    100          
337 1984         3223 $self->{s1} = $s1;
338 1984         3936 return $s1;
339             }
340 5099         12370 elsif ( $self->{s1} ) { return $self->{s1}; }
341 8         29 else { $self->resetS12; return $self->{s1}; }
  8         29  
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 15262 my $self = shift;
355 11675         14275 my $s2 = shift;
356 11675 100       27904 if ( defined $s2 ) {
    50          
357 3233         4924 $self->{s2} = $s2;
358 3233         6450 return $s2;
359             }
360 8442         19683 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 1811 my $self = shift;
375 1186         2345 my $number = $self->number;
376 1186         2978 my $members = $self->residents;
377 1186 50       3194 return [] unless $#$members >= 1;
378 1186         1518 my (@s1, @s2);
379 26     26   50531 use Games::Tournament;
  26         57  
  26         125809  
380 1186 100       2781 if ( $self->hetero ) {
381 149         194 my %scorers;
382 149         278 for my $member (@$members)
383             {
384 694 100       1737 my $score = defined $member->score? $member->score: 0;
385 694         1696 push @{ $scorers{$score} }, $member;
  694         2085  
386             }
387 149         565 my @scores = reverse sort { $a <=> $b } keys %scorers;
  160         653  
388             #carp @scores . " different scores in Hetero Bracket $number"
389             # if @scores > 2;
390 149         238 @s2 = @{$scorers{$scores[-1]}};
  149         412  
391 149         246 my %s2 = map { $_->pairingNumber => $_ } @s2;
  509         1242  
392 149         617 @s1 = grep { not exists $s2{$_->pairingNumber} } $self->rank(@$members);
  694         1743  
393             }
394             else {
395 1037         2405 my $p = $self->p;
396 1037         4270 @s1 = ( $self->rank(@$members) )[ 0 .. $p - 1 ];
397 1037         6620 @s2 = ( $self->rank(@$members) )[ $p .. $#$members ];
398             }
399 1186         6780 $self->s1(\@s1);
400 1186         2923 $self->s2(\@s2);
401 1186         2181 my @lastS2ids = reverse map { $_->pairingNumber } @s2;
  3167         7783  
402 1186         2414 $self->{lastS2ids} = \@lastS2ids;
403 1186 50   5684   5848 die "undef player in Bracket $number S1, S2" if any { not defined } @s1, @s2;
  5684         8130  
404 1186         4721 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 101 my $self = shift;
420 60         136 my $members = $self->members;
421 60         137 my $s1 = $self->s1;
422 60         165 my $s2 = $self->s2;
423 60         116 my %s1 = map { $_->pairingNumber => $_ } @$s1;
  65         165  
424 60         140 my %s2 = map { $_->pairingNumber => $_ } @$s2;
  152         344  
425 60         134 my %members = map { $_->pairingNumber => $_ } @$members;
  219         495  
426             # my %tally; @tally{keys %members} = (0) x keys %members;
427 65     65   400 my $memberChangeTest = ( (notall { exists $members{$_} } keys %s1) or
428 60   66 152   339 (notall { exists $members{$_} } keys %s2) or (@$s1 + @$s2 != @$members));
  152         494  
429 60 100       437 $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 4771 my $self = shift;
443 3567         7478 my $members = $self->members;
444 3567         5433 my $n = @$members;
445 3567 50       7878 return 0 unless $n >= 2;
446 3567         3874 my $p;
447 3567 100       7474 if ( $self->hetero ) {
448 378         441 my %scorers;
449 378         666 for my $member ( @$members ) {
450 1926 100       4803 my $score = defined $member->score? $member->score: 0;
451 1926         6586 $scorers{$score}++;
452             }
453 378         1389 my $lowestScore = min keys %scorers;
454 378 50       883 return unless defined $lowestScore;
455 378         601 $p = $n - $scorers{$lowestScore};
456 378 50       1265 $p = int( $n / 2 ) if $p > $n/2;
457             }
458             else {
459 3189         6178 $p = int( $n / 2 );
460             }
461 3567         7949 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 48 my $self = shift;
475 29         77 my $members = $self->members;
476 29         58 my $n = @$members;
477 29 100       121 if ( $self->{remainderof} )
    50          
478             {
479 17         47 my $remaindered = $self->{remainderof}->members;
480 17         30 $n += @$remaindered;
481             }
482             elsif ( $self->{remaindered} ) {
483 0         0 my $heteroMembers = $self->{remainder}->members;
484 0         0 $n += @$heteroMembers;
485             }
486 29 50       86 return 0 unless $n >= 2;
487 29         59 my $p = int( $n / 2 );
488 29         81 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 3318 my ( $self, $p ) = @_;
502 2079         3294 my $pprime = $self->{pprime};
503 2079 100       5297 if ( defined $p ) { $self->{pprime} = $p; }
  192 100       563  
504 1880         4456 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 73 my ( $self, $p ) = @_;
522 37         68 my $bigGroupPprime = $self->{biggrouppprime};
523 37 50       134 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         27 elsif ( defined $bigGroupPprime ) { return $bigGroupPprime; }
534             else {
535 29         96 $self->{biggrouppprime} = $self->bigGroupP;
536 29         80 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 466 my $self = shift;
551 322         662 my $players = $self->members;
552 322 100       1303 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 351 my $self = shift;
566 229         524 my $players = $self->residents;
567             my $numbers = sub {
568 458     458   659 my $n = shift;
569             return scalar grep {
570 458 100       727 $_->preference->role and $_->preference->role eq (ROLES)[$n] }
  1894         4827  
571             @$players;
572 229         807 };
573 229         581 my $w = $numbers->(0);
574 229         510 my $b = $numbers->(1);
575 229         610 my $q = $self->q;
576 229 100       656 my $x = $w >= $b ? $w - $q : $b - $q;
577 229 100       1661 $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 135 my $self = shift;
591 93         223 my $players = $self->members;
592             my $w =
593 93 100       177 grep { $_->preference->role and $_->preference->role eq (ROLES)[0] }
  436         1147  
594             @$players;
595 93         186 my $b = @$players - $w;
596 93         271 my $q = $self->q;
597 93 100       283 my $x = $w >= $b ? $w - $q : $b - $q;
598 93         129 my $bigGroupX = $x;
599 93 50       336 if ( $self->{remainderof} ) { $bigGroupX += $self->{remainderof}->x; }
  0 50       0  
600 0         0 elsif ( $self->{remainder} ) { $bigGroupX += $self->{remainder}->x; }
601 93         164 $self->{biggroupx} = $bigGroupX;
602 93         415 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 390 my $self = shift;
616 244         333 my $x = shift;
617 244         389 my $xprime = $self->{biggroupxprime};
618 244 100       657 if ( defined $x ) {
    100          
619 5         13 $self->{biggroupxprime} = $x;
620 5 50       29 if ( $self->{remainderof} ) {
    50          
621 0         0 $self->{remainderof}->{biggroupxprime} = $x;
622             }
623             elsif ( $self->{remainder} ) {
624 5         12 $self->{remainder}->{biggroupxprime} = $x
625             }
626 5         14 return; }
627 72         234 elsif ( defined $xprime ) { return $xprime; }
628             else {
629 167 100       507 if ( $self->{remainderof} ) {
    100          
630 21         52 my $x = $self->{remainderof}->{biggroupxprime};
631 21 100       92 return $x if defined $x;
632             }
633             elsif ( $self->{remainder} ) {
634 53         97 $x = $self->{remainder}->{biggroupxprime};
635 53 50       305 return $x if defined $x;
636             }
637 93         251 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 2457 my $self = shift;
652 1716         2226 my $x = shift;
653 1716         2711 my $xprime = $self->{xprime};
654 1716 100       4360 if ( defined $x ) { $self->{xprime} = $x; return; }
  77 100       119  
  77         183  
655 1553         3875 elsif ( defined $xprime ) { return $xprime; }
656             else {
657 86         193 $self->{xprime} = $self->x;
658 86         349 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 1635 my $self = shift;
673 1187         2267 my $number = $self->number;
674 1187         1659 my $level = shift;
675 1187 50 66     4033 warn "Unknown float level: $level" if
676             $level and $level !~ m/^(?:None|B6Down|B5Down|B6Up|B5Up|All)$/i;
677 1187         1974 my $oldLevel = $self->{floatCheck};
678 1187 100       2981 if ( defined $level ) {
    50          
679 222 50 66     2522 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         646 $self->{floatCheck} = $level;
692             }
693 965         4500 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 8894 my $self = shift;
708 6964         7971 my @members = @{$self->members};
  6964         13559  
709 6964         9399 my %tally;
710 6964         11150 for my $member ( @members ) {
711 33576 100       85814 my $score = defined $member->score? $member->score: 0;
712 33576         115294 $tally{$score}++ ;
713             }
714 6964         16565 my @range = keys %tally;
715 6964 100       33051 return 0 if @range == 1;
716 1773         6248 my $min = min @range;
717 1773 50       3636 return unless defined $min;
718 1773 100       7257 return 0 if $tally{$min} <= @members/2;
719 1116 50       7539 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         2 my @members = @{$self->members};
  1         2  
735 1         3 my %tally;
736 1         3 for my $member ( @members ) {
737 3 50       8 my $score = defined $member->score? $member->score: 0;
738 3         13 $tally{$score}++;
739             }
740 1         3 my @range = keys %tally;
741 1 50       5 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 3868 my $self = shift;
761 1719         2176 my $position = shift;
762 1719         2227 my $bigLastGroup = shift;
763 1719         3266 my $s2 = $self->s2;
764 1719 50       4235 die "C7 shuffle: pos $position past end of S2" if $position > $#$s2;
765 1719         5353 my @players = $self->rank(@$s2);
766 1719 50       8954 @players = $self->reverseRank(@$s2) if $bigLastGroup;
767             # my @players = @$s2;
768 1719         4061 my $p = $self->p;
769 1719         2407 my @pattern;
770 1719         3481 my @copy = @players;
771 1719         4157 for my $i ( 0 .. $#$s2 ) {
772 5381         6469 my $j = 0;
773 5381         14754 $j++ until $s2->[$i]->pairingNumber == $copy[$j]->pairingNumber;
774 5381         7978 $pattern[$i] = $j;
775 5381         10016 splice @copy, $j, 1;
776             }
777 1719         2605 my $value = $pattern[$position];
778 1719         1960 my @nextPattern;
779 1719         4367 @nextPattern[ 0 .. $position ] = @pattern[ 0 .. $position ];
780 1719         4982 @nextPattern[ $position + 1 .. $#pattern ] =
781             (0) x ( $#pattern - $position );
782 1719         3527 for my $digit ( reverse( 0 .. $position ) ) {
783 2730 50       5791 die "${digit}th digit overrun of @pattern \@pattern" if
784             @pattern == $digit;
785 2730         4868 $nextPattern[$digit] = ++$value % ( @pattern - $digit );
786 2730 100       7076 last unless $nextPattern[$digit] == 0;
787             }
788 1481         3100 continue { $value = $pattern[ $digit - 1 ]; }
789 1719 100       2973 return unless grep { $_ } @nextPattern;
  5381         10977  
790 1249         1460 my @permutation;
791 1249         2085 for my $pos (@nextPattern) {
792 4259         7467 push @permutation, splice( @players, $pos, 1 );
793             }
794 1249         5705 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 386 my $self = shift;
866 247         390 my $letter = 'a';
867 247         545 my $p = $self->p;
868 247         369 my $oddBracket = @{$self->members} % 2;
  247         585  
869 247         371 my @exchanges;
870 247 100       627 unless ($oddBracket)
    50          
871             {
872             @exchanges = map {
873 162         502 my $i = $_;
  189         272  
874 189         814 map { [ [ $_, $_+$i ] ] }
  230         939  
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         142 my $pPlus = $p+1;
880             @exchanges = map {
881 85         217 my $i = $_;
  109         141  
882 109         449 map { [ [ $_-1, $_+$i-1 ] ] }
  129         542  
883             reverse( (max 1, $pPlus-$i) .. (min $pPlus-1, 2*($pPlus-1)-$i) )
884             } ( 1 .. 2*($pPlus-1)-1 );
885             }
886 247         382 my @exchanges2;
887 247 100       586 unless ($oddBracket)
    50          
888             {
889             my @s1pair = map {
890 162         352 my $i = $_;
  33         49  
891 33         69 map { [ $i - $_, $i ] } 1 .. $i - 1
  37         134  
892             } reverse 2 .. $p - 1;
893             my @s2pair = map {
894 162         381 my $i = $_;
  33         53  
895 33         79 map { [ $i, $i + $_ ] } 1 .. 2 * ( $p - 1 ) - $i
  37         149  
896             } $p .. 2 * ( $p - 1 ) - 1;
897             @exchanges2 = map {
898 162         409 my $i = $_;
  44         60  
899             map {
900 44         264 [
901 73         492 [ $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         132 my $pPlus = $p+1;
911             my @s1pair = map {
912 85         194 my $i = $_;
  3         4  
913 3         7 map { [ $i - $_-1, $i-1 ] } 1 .. $i-1
  7         17  
914             } reverse 3 .. $pPlus - 1;
915             my @s2pair = map {
916 85         218 my $i = $_;
  12         22  
917 12         23 map { [ $i-1, $i+$_-1 ] } 1 .. 2 * ( $pPlus - 1 ) - $i
  16         47  
918             } $pPlus .. 2 * ( $pPlus - 1 ) - 1;
919             @exchanges2 = map {
920 85         246 my $i = $_;
  14         16  
921             map {
922 14         49 [
923 36         137 [ $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         437 push @exchanges, @exchanges2;
931             return sub {
932 553     553   321245 my $exchange = shift @exchanges;
933 553 100       1631 return ("last S1,S2 exchange") unless $exchange;
934 376         969 $self->resetS12;
935 376         995 my $s1 = $self->s1;
936 376         832 my $s2 = $self->s2;
937 376         1072 my @members = (@$s1, @$s2);
938             # my @members = @{ $self->members };
939             ( $members[ $_->[0] ], $members[ $_->[1] ] ) =
940             ( $members[ $_->[1] ], $members[ $_->[0] ] )
941 376         2010 for @$exchange;
942 376         968 my $number = $letter++;
943             die "undef player in exchange $number of S1, S2" if
944 376 50       1532 any { not defined } @members;
  2329         3162  
945 376         2683 return "exchange $number", @members;
946             }
947 247         1759 }
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 556 my $self = shift;
960 380         515 my $score = shift;
961 380 50       1123 if ( defined $score ) { $self->{score} = $score; }
  0 50       0  
962 380         1375 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 12690 my $self = shift;
977 9645         11789 my $number = shift;
978 9645 50       27398 if ( defined $number ) { $self->{number} = $number; }
  0 100       0  
979 9494         22244 elsif ( exists $self->{number} ) { return $self->{number}; }
980 151         231 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 5984 my $self = shift;
994 4315         5656 my $badpair = shift;
995 4315 100       11377 if ( defined $badpair ) { $self->{badpair} = $badpair; }
  1384 100       2515  
996 2801         6410 elsif ( defined $self->{badpair} ) { return $self->{badpair}; }
997 1514         3100 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 24251 my $self = shift;
1011 18456         22629 my $members = shift;
1012 18456 100       50538 if ( defined $members ) { $self->{members} = $members; }
  2103 100       3646  
1013 16264         39334 elsif ( $self->{members} ) { return $self->{members}; }
1014 2192         4505 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 1322 my $self = shift;
1028 973         1235 my $c8swapper = shift;
1029 973 100       3091 if ( defined $c8swapper ) { $self->{c8swapper} = $c8swapper; }
  223 100       1183  
1030 696         2109 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   413 my $self = shift;
1044 272         358 my $untested = shift;
1045 272         550 my @paired = @$untested;
1046 272         743 my @nopairs = $self->_getNonPaired(@paired);
1047 272         426 my $levels = shift;
1048 272 50 33     1532 die "Float checks are $levels?" unless $levels and ref($levels) eq 'ARRAY';
1049 272         599 my $pprime = $self->pprime;
1050 272         599 my $s1 = $self->s1;
1051 272         385 my ($badpos, %badpos);
1052 272         514 my @pairtestee = @paired;
1053 272         612 my @nopairtestee = @nopairs;
1054 272         609 my @pairlevelpasser;
1055             my @nopairlevelpasser;
1056 0         0 my $message;
1057 272         785 B56: for my $level (@$levels)
1058             {
1059 882         1000 my ($round, $direction, $checkedOne, $id);
1060 882 100       2659 if ( $level =~ m/^B5/i ) { $round = 1; }
  367         545  
1061 515         712 else { $round = 2; }
1062 882 100       3171 if( $level =~ m/Down$/i) { $direction = 'Down'; $checkedOne = 0 }
  315 100       423  
  315         451  
1063 375         503 elsif ( $level =~ m/Up$/i ) { $direction = 'Up'; $checkedOne = 1 }
  375         486  
1064 192         363 else { @pairlevelpasser = @pairtestee; last B56 }
  192         445  
1065 690         1511 for my $pos ( 0 .. $#$s1 ) {
1066 940 100       2047 next unless defined $pairtestee[$pos];
1067 937         2056 my @pair = ( $pairtestee[$pos]->[0], $pairtestee[$pos]->[1] );
1068 937 100       1379 my @score = map { defined $_->score? $_->score: 0 } @pair;
  1874         4915  
1069 937         1708 my @float = map { $_->floats( -$round ) } @pair;
  1874         5091  
1070 937         1551 my $test = 0;
1071 937 50 100     4437 $test = ( $score[0] == $score[1] or $float[$checkedOne] ne
1072             $direction ) unless $direction eq 'None';# XXX check both?
1073 937 100       1540 if ( $test ) { $pairlevelpasser[$pos] = \@pair; }
  869         3034  
1074             else {
1075 68 50       231 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1076 68 50       137 $badpos = defined $badpos? $badpos: $pos;
1077 68   33     296 $id ||= $pair[$checkedOne]->pairingNumber;
1078             }
1079             }
1080 690 100 100     2969 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         312 for my $pos ( 0 .. $#nopairtestee ) {
1086 314 100       717 next unless defined $nopairtestee[$pos];
1087 128 50 33     726 my @pair = @{ $nopairtestee[$pos] } if defined
  128         325  
1088             $nopairtestee[$pos] and ref $nopairtestee[$pos] eq 'ARRAY';
1089 128         202 my $tableTest = 0;
1090 128         153 my $idCheck;
1091 128         211 for my $player ( @pair) {
1092 256   100     873 my $test = ( not defined $player or
1093             ($player->floats(-$round) ne "Down") );
1094 256 100 33     1016 $idCheck ||= $player->pairingNumber if $player and
      100        
1095             not $test;
1096 256 100       658 $tableTest++ if $test;
1097             }
1098 128 100       266 if ( $tableTest >= 2 ) { $nopairlevelpasser[$pos] = \@pair; }
  116         337  
1099             else {
1100 12 50       59 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1101 12 50       32 $badpos = defined $badpos? $badpos: $pos;
1102 12 50       44 $id = $idCheck if $idCheck;
1103             }
1104             }
1105             }
1106 690         1140 my @retainables = grep { defined } @pairlevelpasser ;#
  884         2253  
1107             # , grep { defined } @nopairlevelpasser;
1108             # my @nonfloaters = grep { grep { defined } @$_ } @retainables;
1109 690 100 100     3673 if ( @retainables < $pprime or keys %badpos )
1110             # if ( @retainables < $pprime or $badpos )
1111             {
1112 80         100 my $badpos;
1113 80         159 for my $nextLevel ( @$levels )
1114             {
1115 146 100       353 next unless defined $badpos{ $nextLevel };
1116 80         126 $badpos = $badpos{ $nextLevel };
1117 80         125 last;
1118             }
1119 80         135 my $pluspos = $badpos+1;
1120 80         293 $message =
1121             "$level, table $pluspos: $id NOK. Floated $direction $round rounds ago";
1122 80         694 return badpos => $badpos, passer => undef, message => $message;
1123             }
1124             }
1125             continue {
1126 610         1256 @pairtestee = @pairlevelpasser;
1127 610         1038 @nopairtestee = @nopairlevelpasser;
1128 610         1040 undef @pairlevelpasser;
1129 610         1185 undef @nopairlevelpasser;
1130             }
1131 192         1388 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   648 my $self = shift;
1145 464         812 my @pairables = @_;
1146 464         949 my $s1 = $self->s1;
1147 464         1021 my $s2 = $self->s2;
1148 464         592 my @nopairs;
1149 464         1003 for my $pos ( 0..$#pairables )
1150             {
1151 599 50       1600 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ] unless
1152             defined $pairables[$pos];
1153             }
1154 464         1162 for my $pos ( $#pairables+1 .. $#$s1 )
1155             {
1156 2         8 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ];
1157             }
1158 464         1048 for my $pos ( $#$s1+1 .. $#$s2 )
1159             {
1160 753         1923 $nopairs[$pos] = [ undef, $s2->[$pos] ];
1161             }
1162 464         1536 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: