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              
3             # Last Edit: 2011 Dec 09, 01:49:18 PM
4             # $Id: $
5              
6 26     26   59560 use warnings;
  26         49  
  26         875  
7 26     26   128 use strict;
  26         45  
  26         546  
8 26     26   130 use Carp;
  26         53  
  26         1860  
9              
10 26     26   138 use constant ROLES => @Games::Tournament::Swiss::Config::roles;
  26         45  
  26         1734  
11              
12 26     26   126 use base qw/Games::Tournament::Swiss/;
  26         46  
  26         2509  
13 26     26   593 use Games::Tournament::Contestant::Swiss;
  26         48  
  26         595  
14 26     26   11333 use Games::Tournament::Card;
  26         62  
  26         827  
15 26     26   154 use List::Util qw/max min reduce sum/;
  26         49  
  26         2224  
16 26     26   136 use List::MoreUtils qw/any notall/;
  26         47  
  26         132  
17              
18             =head1 NAME
19              
20             Games::Tournament::Swiss::Bracket - Players with same/similar scores pairable with each other
21              
22             =head1 VERSION
23              
24             Version 0.06
25              
26             =cut
27              
28             our $VERSION = '0.06';
29              
30             =head1 SYNOPSIS
31              
32             $tourney = Games::Tournament::Swiss>new($rounds, \@entrants);
33             @rankedPlayers = $tourney->assignPairingNumbers;
34             @firstbrackets = $t->formBrackets;
35             ...
36             $tourney->collectCards(@games);
37             @scores = $tourney->updateScores($round);
38             @groups = $tourney->formBrackets;
39              
40             =head1 DESCRIPTION
41              
42             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.
43              
44             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.
45             I pulled back on this metaphor. It was probably overengineering.
46              
47             =head1 METHODS
48              
49             =head2 new
50              
51             $group = Games::Tournament::Swiss::Bracket->new( score => 7.5, members => [ $a, $b, $c ], remainderof => $largergroup )
52              
53             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
54              
55             =cut
56              
57             sub new {
58 287     287 1 678 my $self = shift;
59 287         1073 my %args = @_;
60 287         514 my $score = $args{score};
61 287 50       661 die "Bracket has score of: $score?" unless defined $score;
62 287         534 bless \%args, $self;
63 287         587 $args{floatCheck} = "None";
64 287         753 return \%args;
65             }
66              
67              
68             =head2 natives
69              
70             @floaters = $group->natives
71              
72             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.
73              
74             =cut
75              
76             sub natives {
77 0     0 1 0 my $self = shift;
78 0 0       0 return () unless @{ $self->members };
  0         0  
79 0         0 my $members = $self->members;
80 0         0 my $foreigners = $self->immigrants;
81             my @natives = grep {
82 0         0 my $member = $_->pairingNumber;
  0         0  
83 0         0 not grep { $member == $_->pairingNumber } @$foreigners
  0         0  
84             } @$members;
85 0         0 return \@natives;
86             }
87              
88              
89             =head2 citizens
90              
91             @floaters = $group->citizens
92              
93             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.
94              
95             =cut
96              
97             sub citizens {
98 0     0 1 0 my $self = shift;
99 0 0       0 return () unless @{ $self->members };
  0         0  
100 0         0 my $members = $self->members;
101 0         0 my $foreigners = $self->immigrants;
102             my @natives = grep {
103 0         0 my $member = $_->pairingNumber;
  0         0  
104 0         0 not grep { $member == $_->pairingNumber } @$foreigners
  0         0  
105             } @$members;
106 0         0 return \@natives;
107             }
108              
109              
110             =head2 naturalize
111              
112             $citizen = $group->naturalize($foreigner)
113              
114             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.
115              
116             =cut
117              
118             sub naturalize {
119 6     6 1 8 my $self = shift;
120 6         8 my $foreigner = shift;
121 6         12 my $members = $self->residents;
122             return unless any
123 6 50   12   22 { $_->pairingNumber == $foreigner->pairingNumber } @$members;
  12         29  
124 6         21 my $direction = $foreigner->floating;
125 6 50 33     28 return unless $direction eq 'Up' or $direction eq 'Down';
126 6         18 $foreigner->floating('');
127 6         23 return $foreigner;
128             }
129              
130              
131             =head2 immigrants
132              
133             @floaters = @{$group->immigrants}
134              
135             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.
136              
137             =cut
138              
139             sub immigrants {
140 0     0 1 0 my $self = shift;
141 0 0       0 return () unless @{ $self->members };
  0         0  
142 0         0 my $members = $self->residents;
143 0         0 my @immigrants = grep { $_->floating } @$members;
  0         0  
144 0         0 return \@immigrants;
145             }
146              
147              
148             =head2 downFloaters
149              
150             @floaters = $group->downFloaters
151              
152             Returns those members downfloated here from higher brackets.
153              
154             =cut
155              
156             sub downFloaters {
157 1     1 1 2 my $self = shift;
158 1         3 my $members = $self->members;
159 1 50 33     7 return () unless @$members and $self->trueHetero;
160 1         1 my %members;
161 1         3 for my $member ( @$members )
162             {
163 3 50       8 my $score = defined $member->score? $member->score: 0;
164 3         8 push @{$members{$score}}, $member;
  3         10  
165             }
166 1         5 my $min = min keys %members;
167 1         3 delete $members{$min};
168 1         3 my @floaters = map { @$_ } values %members;
  1         4  
169 1         4 return @floaters;
170             }
171              
172              
173             =head2 upFloaters
174              
175             @s1 = $group->upFloaters
176              
177             Returns those members upfloated from the next bracket.
178              
179             =cut
180              
181             sub upFloaters {
182 57     57 1 79 my $self = shift;
183 57 50       69 return () unless @{ $self->members };
  57         117  
184 57         133 my @members = $self->residents;
185 57 100       87 grep { $_->floating and $_->floating =~ m/^Up/i } @{ $self->members };
  203         488  
  57         125  
186             }
187              
188              
189             =head2 residents
190              
191             $pairables = $bracket->residents
192              
193             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.
194              
195             =cut
196              
197             sub residents {
198 2259     2259 1 3006 my $self = shift;
199 2259         4333 my $members = $self->members;
200 2259         2959 my @residents;
201 2259         4620 my $floated = $self->emigrants;
202 2259         4564 for my $member (@$members) {
203             push @residents, $member
204 8764 50   0   51714 unless any { $member->pairingNumber == $_->pairingNumber } @$floated;
  0         0  
205             }
206 2259         5203 return \@residents;
207             }
208              
209              
210             =head2 emigrants
211              
212             $bracket->emigrants($member)
213             $gone = $bracket->emigrants
214              
215             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'.
216              
217             =cut
218              
219             sub emigrants {
220 2259     2259 1 2760 my $self = shift;
221 2259         2952 my $floater = shift;
222 2259 50       4006 if ($floater) { push @{ $self->{gone} }, $floater; }
  0         0  
  0         0  
223 2259         4215 else { return $self->{gone}; }
224             }
225              
226              
227             =head2 exit
228              
229             $bracket->exit($player)
230              
231             Removes $player from the list of members of the bracket. They are now in the air. So make sure they enter another bracket.
232              
233             =cut
234              
235             sub exit {
236 575     575 1 826 my $self = shift;
237 575         1178 my $members = $self->members;
238 575         842 my $exiter = shift;
239 575         1383 my $myId = $exiter->pairingNumber;
240 575         1034 my @stayers = grep { $_->pairingNumber != $myId } @$members;
  1943         4317  
241 575         1203 my $number = $self->number;
242 575 50       1402 croak "Player $myId did not exit Bracket $number" if @stayers == @$members;
243 575         1229 $self->members(\@stayers);
244             #my $immigrants = $self->immigrants;
245             #if ( grep { $_ == $member } @$immigrants ) {
246             # @{ $self->members } = grep { $_ != $member } @$members;
247             #}
248             #else {
249             # $self->emigrants($member);
250             #}
251 575         2046 return;
252             }
253              
254              
255             =head2 entry
256              
257             $bracket->entry($native)
258             $bracket->entry($foreigner)
259              
260             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).
261              
262             =cut
263              
264             sub entry {
265 575     575 1 824 my $self = shift;
266 575         1224 my $members = $self->residents;
267 575         880 my $enterer = shift;
268 575         1556 my $myId = $enterer->id;
269 575         1300 my $number = $self->number;
270             croak "Player $myId cannot enter Bracket $number. Is already there." if
271 575 50   1268   2502 any { $_->{id} eq $myId } @$members;
  1268         2671  
272 575         1888 unshift @$members, $enterer;
273 575         1290 $self->members(\@$members);
274 575         1799 return;
275             }
276              
277              
278             =head2 reentry
279              
280             $bracket->reentry($member)
281              
282             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.
283              
284             =cut
285              
286             sub reentry {
287 0     0 1 0 my $self = shift;
288 0         0 my $returnee = shift;
289 0         0 my $emigrants = $self->emigrants;
290 0 0   0   0 if ( any { $_->pairingNumber == $returnee->pairingNumber } @$emigrants ) {
  0         0  
291             my @nonreturnees = grep {
292 0         0 $_->pairingNumber != $returnee->pairingNumber } @$emigrants;
  0         0  
293             # @{ $self->{gone} } = @nonreturnees;
294 0         0 $self->{gone} = \@nonreturnees;
295 0         0 return @nonreturnees;
296             }
297             #my @updatedlist = grep { $_->id != $returnee->id } @$emigrants;
298             #$self->emigrants($_) for @updatedlist;
299             #return @updatedlist if grep { $_->id == $returnee->id } @$emigrants;
300 0         0 return;
301              
302             }
303              
304              
305             =head2 dissolved
306              
307             $group->dissolved(1)
308             $s1 = $group->s1($players)
309             $s1 = $group->s1
310              
311             Dissolve a bracket, so it is no longer independent, its affairs being controlled by some other group:
312              
313             =cut
314              
315             sub dissolved {
316 6941     6941 1 8458 my $self = shift;
317 6941         8028 my $flag = shift;
318 6941 100       11483 if ( defined $flag )
319             {
320 64         158 $self->{dissolved} = $flag;
321 64 50       207 return $flag? 1: 0;
322             }
323             else {
324 6877 100       25740 return $self->{dissolved}? 1: 0;
325             }
326             }
327              
328              
329             =head2 s1
330              
331             $group->s1
332             $s1 = $group->s1($players)
333             $s1 = $group->s1
334              
335             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
336              
337             =cut
338              
339             sub s1 {
340 7091     7091 1 9401 my $self = shift;
341 7091         9056 my $s1 = shift;
342 7091 100       18140 if ( defined $s1 ) {
    100          
343 1984         3492 $self->{s1} = $s1;
344 1984         4054 return $s1;
345             }
346 5099         12484 elsif ( $self->{s1} ) { return $self->{s1}; }
347 8         29 else { $self->resetS12; return $self->{s1}; }
  8         34  
348             }
349              
350              
351             =head2 s2
352              
353             $s2 = $group->s2
354              
355             Getter/Setter of the players in a homogeneous or a heterogeneous bracket who aren't in S1. A6
356              
357             =cut
358              
359             sub s2 {
360 11675     11675 1 15736 my $self = shift;
361 11675         14219 my $s2 = shift;
362 11675 100       26813 if ( defined $s2 ) {
    50          
363 3233         5037 $self->{s2} = $s2;
364 3233         6158 return $s2;
365             }
366 8442         19684 elsif ( $self->{s2} ) { return $self->{s2}; }
367 0         0 else { $self->resetS12; return $self->{s2}; }
  0         0  
368             }
369              
370              
371             =head2 resetS12
372              
373             $group->resetS12
374              
375             Resetter of S1 and S2 to the original members, ranked before exchanges in C8. A6
376              
377             =cut
378              
379             sub resetS12 {
380 1186     1186 1 1786 my $self = shift;
381 1186         2398 my $number = $self->number;
382 1186         3144 my $members = $self->residents;
383 1186 50       3101 return [] unless $#$members >= 1;
384 1186         1646 my (@s1, @s2);
385 26     26   51242 use Games::Tournament;
  26         52  
  26         126079  
386 1186 100       2726 if ( $self->hetero ) {
387 149         220 my %scorers;
388 149         289 for my $member (@$members)
389             {
390 694 100       1771 my $score = defined $member->score? $member->score: 0;
391 694         1730 push @{ $scorers{$score} }, $member;
  694         2035  
392             }
393 149         553 my @scores = reverse sort { $a <=> $b } keys %scorers;
  161         730  
394             #carp @scores . " different scores in Hetero Bracket $number"
395             # if @scores > 2;
396 149         258 @s2 = @{$scorers{$scores[-1]}};
  149         420  
397 149         263 my %s2 = map { $_->pairingNumber => $_ } @s2;
  509         1235  
398 149         619 @s1 = grep { not exists $s2{$_->pairingNumber} } $self->rank(@$members);
  694         1640  
399             }
400             else {
401 1037         2521 my $p = $self->p;
402 1037         4349 @s1 = ( $self->rank(@$members) )[ 0 .. $p - 1 ];
403 1037         6719 @s2 = ( $self->rank(@$members) )[ $p .. $#$members ];
404             }
405 1186         6827 $self->s1(\@s1);
406 1186         2897 $self->s2(\@s2);
407 1186         2101 my @lastS2ids = reverse map { $_->pairingNumber } @s2;
  3167         8041  
408 1186         2525 $self->{lastS2ids} = \@lastS2ids;
409 1186 50   5684   5835 die "undef player in Bracket $number S1, S2" if any { not defined } @s1, @s2;
  5684         8279  
410 1186         4836 return;
411             }
412              
413              
414             =head2 resetShuffler
415              
416             $previous->entry($_) for @returnees;
417             $previous->resetShuffler;
418             return C7;
419              
420             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.
421              
422             =cut
423              
424             sub resetShuffler {
425 60     60 1 77 my $self = shift;
426 60         117 my $members = $self->members;
427 60         144 my $s1 = $self->s1;
428 60         135 my $s2 = $self->s2;
429 60         111 my %s1 = map { $_->pairingNumber => $_ } @$s1;
  65         161  
430 60         126 my %s2 = map { $_->pairingNumber => $_ } @$s2;
  152         327  
431 60         136 my %members = map { $_->pairingNumber => $_ } @$members;
  219         510  
432             # my %tally; @tally{keys %members} = (0) x keys %members;
433 65     65   396 my $memberChangeTest = ( (notall { exists $members{$_} } keys %s1) or
434 60   66 152   361 (notall { exists $members{$_} } keys %s2) or (@$s1 + @$s2 != @$members));
  152         476  
435 60 100       441 $self->resetS12 if $memberChangeTest;
436             }
437              
438              
439             =head2 p
440              
441             $tables = $group->p
442              
443             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
444              
445             =cut
446              
447             sub p {
448 3567     3567 1 4959 my $self = shift;
449 3567         7005 my $members = $self->members;
450 3567         5509 my $n = @$members;
451 3567 50       7579 return 0 unless $n >= 2;
452 3567         4221 my $p;
453 3567 100       7350 if ( $self->hetero ) {
454 378         491 my %scorers;
455 378         703 for my $member ( @$members ) {
456 1926 100       4927 my $score = defined $member->score? $member->score: 0;
457 1926         6930 $scorers{$score}++;
458             }
459 378         1472 my $lowestScore = min keys %scorers;
460 378 50       897 return unless defined $lowestScore;
461 378         580 $p = $n - $scorers{$lowestScore};
462 378 50       1303 $p = int( $n / 2 ) if $p > $n/2;
463             }
464             else {
465 3189         6109 $p = int( $n / 2 );
466             }
467 3567         7834 return $p;
468             }
469              
470              
471             =head2 bigGroupP
472              
473             $tables = $group->bigGroupP
474              
475             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
476              
477             =cut
478              
479             sub bigGroupP {
480 29     29 1 53 my $self = shift;
481 29         77 my $members = $self->members;
482 29         64 my $n = @$members;
483 29 100       111 if ( $self->{remainderof} )
    50          
484             {
485 17         50 my $remaindered = $self->{remainderof}->members;
486 17         42 $n += @$remaindered;
487             }
488             elsif ( $self->{remaindered} ) {
489 0         0 my $heteroMembers = $self->{remainder}->members;
490 0         0 $n += @$heteroMembers;
491             }
492 29 50       96 return 0 unless $n >= 2;
493 29         63 my $p = int( $n / 2 );
494 29         91 return $p;
495             }
496              
497              
498             =head2 pprime
499              
500             $tables = $group->pprime
501              
502             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
503              
504             =cut
505              
506             sub pprime {
507 2079     2079 1 3304 my ( $self, $p ) = @_;
508 2079         3480 my $pprime = $self->{pprime};
509 2079 100       5401 if ( defined $p ) { $self->{pprime} = $p; }
  192 100       577  
510 1880         4486 elsif ( defined $pprime ) { return $pprime; }
511             else {
512 7         21 $self->{pprime} = $self->p;
513 7         26 return $self->{pprime};
514             }
515             }
516              
517              
518             =head2 bigGroupPprime
519              
520             $tables = $group->bigGroupPprime
521              
522             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
523              
524             =cut
525              
526             sub bigGroupPprime {
527 37     37 1 75 my ( $self, $p ) = @_;
528 37         71 my $bigGroupPprime = $self->{biggrouppprime};
529 37 50       154 if ( defined $p ) {
    100          
530 0         0 $self->{biggrouppprime} = $p;
531 0 0       0 if ( $self->{remainderof} ) {
    0          
532 0         0 $self->{remainderof}->{biggrouppprime} = $p;
533             }
534             elsif ( $self->{remainder} ) {
535 0         0 $self->{remainder}->{biggrouppprime} = $p;
536             }
537 0         0 return;
538             }
539 8         27 elsif ( defined $bigGroupPprime ) { return $bigGroupPprime; }
540             else {
541 29         101 $self->{biggrouppprime} = $self->bigGroupP;
542 29         93 return $self->{biggrouppprime};
543             }
544             }
545              
546              
547             =head2 q
548              
549             $tables = $group->q
550              
551             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
552              
553             =cut
554              
555             sub q {
556 322     322 1 469 my $self = shift;
557 322         618 my $players = $self->members;
558 322 100       1336 my $q = @$players % 2 ? ( $#$players + 2 ) / 2 : ( $#$players + 1 ) / 2;
559             }
560              
561              
562             =head2 x
563              
564             $tables = $group->x
565              
566             Sets the number, ranging from zero to p, of matches in the score bracket in which players will have their preferences unsatisfied. A8
567              
568             =cut
569              
570             sub x {
571 229     229 1 344 my $self = shift;
572 229         501 my $players = $self->residents;
573             my $numbers = sub {
574 458     458   608 my $n = shift;
575             return scalar grep {
576 458 100       721 $_->preference->role and $_->preference->role eq (ROLES)[$n] }
  1894         4973  
577             @$players;
578 229         854 };
579 229         610 my $w = $numbers->(0);
580 229         521 my $b = $numbers->(1);
581 229         644 my $q = $self->q;
582 229 100       649 my $x = $w >= $b ? $w - $q : $b - $q;
583 229 100       1677 $self->{x} = $x < 0? 0: $x;
584             }
585              
586              
587             =head2 bigGroupX
588              
589             $tables = $group->bigGroupX
590              
591             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
592              
593             =cut
594              
595             sub bigGroupX {
596 93     93 1 126 my $self = shift;
597 93         195 my $players = $self->members;
598             my $w =
599 93 100       183 grep { $_->preference->role and $_->preference->role eq (ROLES)[0] }
  436         1173  
600             @$players;
601 93         178 my $b = @$players - $w;
602 93         282 my $q = $self->q;
603 93 100       267 my $x = $w >= $b ? $w - $q : $b - $q;
604 93         130 my $bigGroupX = $x;
605 93 50       303 if ( $self->{remainderof} ) { $bigGroupX += $self->{remainderof}->x; }
  0 50       0  
606 0         0 elsif ( $self->{remainder} ) { $bigGroupX += $self->{remainder}->x; }
607 93         165 $self->{biggroupx} = $bigGroupX;
608 93         393 return $self->{biggroupx};
609             }
610              
611              
612             =head2 bigGroupXprime
613              
614             $tables = $group->bigGroupXprime
615              
616             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
617              
618             =cut
619              
620             sub bigGroupXprime {
621 244     244 1 371 my $self = shift;
622 244         355 my $x = shift;
623 244         413 my $xprime = $self->{biggroupxprime};
624 244 100       738 if ( defined $x ) {
    100          
625 5         10 $self->{biggroupxprime} = $x;
626 5 50       31 if ( $self->{remainderof} ) {
    50          
627 0         0 $self->{remainderof}->{biggroupxprime} = $x;
628             }
629             elsif ( $self->{remainder} ) {
630 5         12 $self->{remainder}->{biggroupxprime} = $x
631             }
632 5         15 return; }
633 72         305 elsif ( defined $xprime ) { return $xprime; }
634             else {
635 167 100       541 if ( $self->{remainderof} ) {
    100          
636 21         60 my $x = $self->{remainderof}->{biggroupxprime};
637 21 100       100 return $x if defined $x;
638             }
639             elsif ( $self->{remainder} ) {
640 53         103 $x = $self->{remainder}->{biggroupxprime};
641 53 50       307 return $x if defined $x;
642             }
643 93         253 else { return $self->bigGroupX; }
644             }
645             }
646              
647              
648             =head2 xprime
649              
650             $tables = $group->xprime
651              
652             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
653              
654             =cut
655              
656             sub xprime {
657 1716     1716 1 2405 my $self = shift;
658 1716         2165 my $x = shift;
659 1716         2817 my $xprime = $self->{xprime};
660 1716 100       4452 if ( defined $x ) { $self->{xprime} = $x; return; }
  77 100       129  
  77         156  
661 1553         4060 elsif ( defined $xprime ) { return $xprime; }
662             else {
663 86         182 $self->{xprime} = $self->x;
664 86         417 return $self->{xprime};
665             }
666             }
667              
668              
669             =head2 floatCheckWaive
670              
671             $tables = $group->floatCheckWaive
672              
673             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.
674              
675             =cut
676              
677             sub floatCheckWaive {
678 1187     1187 1 1713 my $self = shift;
679 1187         2360 my $number = $self->number;
680 1187         1792 my $level = shift;
681 1187 50 66     4111 warn "Unknown float level: $level" if
682             $level and $level !~ m/^(?:None|B6Down|B5Down|B6Up|B5Up|All)$/i;
683 1187         1844 my $oldLevel = $self->{floatCheck};
684 1187 100       2987 if ( defined $level ) {
    50          
685 222 50 66     2598 warn
      66        
      100        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      66        
      33        
      66        
      33        
      33        
686             "Bracket [$number]'s old float check waive level, $oldLevel is now $level."
687             unless $level eq 'None' or
688             $oldLevel eq 'None' and $level eq 'B6Down' or
689             $oldLevel eq 'B6Down' and $level eq 'B5Down' or
690             $oldLevel eq 'B6Down' and $level eq 'B6Up' or
691             $oldLevel eq 'B5Down' and $level eq 'B6Up' or
692             $oldLevel eq 'B6Up' and $level eq 'B5Up' or
693             $oldLevel eq 'B5Up' and $level eq 'All' or
694             # $oldLevel eq 'B5Down' and $level eq 'All' or
695             $oldLevel eq 'All' and $level eq 'None' or
696             $oldLevel eq 'All' and $level eq 'B6Down';
697 222         666 $self->{floatCheck} = $level;
698             }
699 965         4596 elsif ( defined $self->{floatCheck} ) { return $self->{floatCheck}; }
700 0         0 else { return; }
701             }
702              
703              
704             =head2 hetero
705              
706             $group->hetero
707              
708             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.
709              
710             =cut
711              
712             sub hetero {
713 6964     6964 1 9207 my $self = shift;
714 6964         8113 my @members = @{$self->members};
  6964         13110  
715 6964         9524 my %tally;
716 6964         11401 for my $member ( @members ) {
717 33576 100       88254 my $score = defined $member->score? $member->score: 0;
718 33576         113731 $tally{$score}++ ;
719             }
720 6964         17038 my @range = keys %tally;
721 6964 100       33020 return 0 if @range == 1;
722 1773         6269 my $min = min @range;
723 1773 50       3695 return unless defined $min;
724 1773 100       7247 return 0 if $tally{$min} <= @members/2;
725 1116 50       7626 return 1 if $tally{$min} > @members/2;
726 0         0 return;
727             }
728              
729              
730             =head2 trueHetero
731              
732             $group->trueHetero
733              
734             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.
735              
736             =cut
737              
738             sub trueHetero {
739 1     1 1 2 my $self = shift;
740 1         1 my @members = @{$self->members};
  1         3  
741 1         2 my %tally;
742 1         3 for my $member ( @members ) {
743 3 50       7 my $score = defined $member->score? $member->score: 0;
744 3         12 $tally{$score}++;
745             }
746 1         4 my @range = keys %tally;
747 1 50       4 return unless @range;
748 1 50       4 return 0 if @range == 1;
749 1         6 return 1;
750             }
751              
752              
753             =head2 c7shuffler
754              
755             $nextS2 = $bracket->c7shuffler($firstmismatch)
756             if ( @nextS2 compatible )
757             {
758             create match cards;
759             }
760              
761             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.
762              
763             =cut
764              
765             sub c7shuffler {
766 1719     1719 1 3902 my $self = shift;
767 1719         2148 my $position = shift;
768 1719         2224 my $bigLastGroup = shift;
769 1719         3538 my $s2 = $self->s2;
770 1719 50       4195 die "C7 shuffle: pos $position past end of S2" if $position > $#$s2;
771 1719         5059 my @players = $self->rank(@$s2);
772 1719 50       8859 @players = $self->reverseRank(@$s2) if $bigLastGroup;
773             # my @players = @$s2;
774 1719         4041 my $p = $self->p;
775 1719         2348 my @pattern;
776 1719         3480 my @copy = @players;
777 1719         4094 for my $i ( 0 .. $#$s2 ) {
778 5381         6401 my $j = 0;
779 5381         15060 $j++ until $s2->[$i]->pairingNumber == $copy[$j]->pairingNumber;
780 5381         8132 $pattern[$i] = $j;
781 5381         9950 splice @copy, $j, 1;
782             }
783 1719         2773 my $value = $pattern[$position];
784 1719         2028 my @nextPattern;
785 1719         4812 @nextPattern[ 0 .. $position ] = @pattern[ 0 .. $position ];
786 1719         5101 @nextPattern[ $position + 1 .. $#pattern ] =
787             (0) x ( $#pattern - $position );
788 1719         3652 for my $digit ( reverse( 0 .. $position ) ) {
789 2730 50       5692 die "${digit}th digit overrun of @pattern \@pattern" if
790             @pattern == $digit;
791 2730         4891 $nextPattern[$digit] = ++$value % ( @pattern - $digit );
792 2730 100       6932 last unless $nextPattern[$digit] == 0;
793             }
794 1481         2876 continue { $value = $pattern[ $digit - 1 ]; }
795 1719 100       3033 return unless grep { $_ } @nextPattern;
  5381         10800  
796 1249         1445 my @permutation;
797 1249         1975 for my $pos (@nextPattern) {
798 4259         7531 push @permutation, splice( @players, $pos, 1 );
799             }
800 1249         5635 return @permutation;
801              
802             #my @selectS2 = $group->c7shuffler($badpair);
803             #my @unselectS2 = @$s2;
804             #for my $position ( 0 .. $#$s2 )
805             #{
806             # my $player = $s2->[$#$s2 - $position];
807             # splice @unselectS2, $#$s2 - $position, 1 if grep{$_ eq $player} @selectS2;
808             #}
809             #my @newS2 = (@selectS2, @unselectS2);
810             }
811              
812              
813             =head2 c7iterator
814              
815             $next = $bracket->c7iterator
816             while ( my @s2 = &$next )
817             {
818             create match cards unless this permutation is incompatible;
819             }
820              
821             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.
822              
823             =cut
824              
825             sub c7iterator {
826 0     0 1 0 my $self = shift;
827 0         0 my $players = $self->s2;
828 0         0 my $p = $self->p;
829 0         0 my $n = 0;
830             return sub {
831 0     0   0 my @pattern = n_to_pat->( $n, $#$players + 1, $p );
832 0         0 my @result = permGenerator->( \@pattern, $players );
833 0         0 print "transposition $n:\t";
834 0         0 $n++;
835 0         0 return @result;
836 0         0 };
837             my $permGenerator = sub {
838 0     0   0 my $pattern = shift;
839 0         0 my @items = @{ shift() };
  0         0  
840 0         0 my @r;
841 0         0 for my $pos (@$pattern) {
842 0         0 push @r, splice( @items, $pos, 1 );
843             }
844 0         0 return @r;
845 0         0 };
846             my $n_to_pat = sub {
847 0     0   0 my @odometer;
848 0         0 my ( $n, $length, $k ) = @_;
849 0         0 for my $i ( $length - $k + 1 .. $length ) {
850 0         0 unshift @odometer, $n % $i;
851 0         0 $n = int( $n / $i );
852             }
853 0 0       0 return $n ? () : @odometer;
854 0         0 };
855             }
856              
857              
858             =head2 c8iterator
859              
860             $next = $bracket->c8iterator
861             while ( my @members = &$next )
862             {
863             next if grep {$incompat{$s1[$_]}{$s2[$_]}} 0..$p-1);
864             }
865              
866             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.
867              
868             =cut
869              
870             sub c8iterator {
871 247     247 1 412 my $self = shift;
872 247         402 my $letter = 'a';
873 247         534 my $p = $self->p;
874 247         387 my $oddBracket = @{$self->members} % 2;
  247         614  
875 247         373 my @exchanges;
876 247 100       688 unless ($oddBracket)
    50          
877             {
878             @exchanges = map {
879 162         525 my $i = $_;
  189         290  
880 189         914 map { [ [ $_, $_+$i ] ] }
  230         992  
881             reverse( ( max 1, $p-$i ) .. ( min $p-1, 2*($p-1)-$i ) )
882             } ( 1 .. 2*($p-1)-1 );
883             }
884             elsif ( $oddBracket ) {
885 85         149 my $pPlus = $p+1;
886             @exchanges = map {
887 85         245 my $i = $_;
  109         164  
888 109         479 map { [ [ $_-1, $_+$i-1 ] ] }
  129         559  
889             reverse( (max 1, $pPlus-$i) .. (min $pPlus-1, 2*($pPlus-1)-$i) )
890             } ( 1 .. 2*($pPlus-1)-1 );
891             }
892 247         383 my @exchanges2;
893 247 100       600 unless ($oddBracket)
    50          
894             {
895             my @s1pair = map {
896 162         363 my $i = $_;
  33         50  
897 33         72 map { [ $i - $_, $i ] } 1 .. $i - 1
  37         126  
898             } reverse 2 .. $p - 1;
899             my @s2pair = map {
900 162         387 my $i = $_;
  33         48  
901 33         75 map { [ $i, $i + $_ ] } 1 .. 2 * ( $p - 1 ) - $i
  37         111  
902             } $p .. 2 * ( $p - 1 ) - 1;
903             @exchanges2 = map {
904 162         449 my $i = $_;
  44         59  
905             map {
906 44         236 [
907 73         396 [ $s1pair[$_][0], $s2pair[ $i - $_ ][0] ],
908             [ $s1pair[$_][1], $s2pair[ $i - $_ ][1] ]
909             ]
910             } ( max 0, $i - ( $p - 1 ) * ( $p - 2 ) / 2 + 1 )
911             .. ( min( ( $p - 1 ) * ( $p - 2 ) / 2 - 1, $i ) )
912             } 0 .. ( $p - 1 ) * ( $p - 2 ) - 2;
913             }
914             elsif ($oddBracket)
915             {
916 85         148 my $pPlus = $p+1;
917             my @s1pair = map {
918 85         208 my $i = $_;
  3         5  
919 3         6 map { [ $i - $_-1, $i-1 ] } 1 .. $i-1
  7         21  
920             } reverse 3 .. $pPlus - 1;
921             my @s2pair = map {
922 85         224 my $i = $_;
  12         14  
923 12         25 map { [ $i-1, $i+$_-1 ] } 1 .. 2 * ( $pPlus - 1 ) - $i
  16         46  
924             } $pPlus .. 2 * ( $pPlus - 1 ) - 1;
925             @exchanges2 = map {
926 85         217 my $i = $_;
  14         18  
927             map {
928 14         51 [
929 36         139 [ $s1pair[$_][0], $s2pair[ $i - $_ ][0] ],
930             [ $s1pair[$_][1], $s2pair[ $i - $_ ][1] ]
931             ]
932             } ( max 0, $i - ( $pPlus - 1 ) * ( $pPlus - 2 ) / 2 + 1 )
933             .. ( min( ( $pPlus - 1 ) * ( $pPlus - 2 ) / 2 - 2, $i ) )
934             } 0 .. ( $pPlus - 1 ) * ( $pPlus - 2 ) - 3;
935             }
936 247         422 push @exchanges, @exchanges2;
937             return sub {
938 553     553   320796 my $exchange = shift @exchanges;
939 553 100       1725 return ("last S1,S2 exchange") unless $exchange;
940 376         1031 $self->resetS12;
941 376         996 my $s1 = $self->s1;
942 376         822 my $s2 = $self->s2;
943 376         1085 my @members = (@$s1, @$s2);
944             # my @members = @{ $self->members };
945             ( $members[ $_->[0] ], $members[ $_->[1] ] ) =
946             ( $members[ $_->[1] ], $members[ $_->[0] ] )
947 376         2041 for @$exchange;
948 376         913 my $number = $letter++;
949             die "undef player in exchange $number of S1, S2" if
950 376 50       1601 any { not defined } @members;
  2329         3112  
951 376         2783 return "exchange $number", @members;
952             }
953 247         1803 }
954              
955              
956             =head2 score
957              
958             $group->score
959              
960             Gets/sets the score of the score group.
961              
962             =cut
963              
964             sub score {
965 380     380 1 547 my $self = shift;
966 380         519 my $score = shift;
967 380 50       1147 if ( defined $score ) { $self->{score} = $score; }
  0 50       0  
968 380         1428 elsif ( exists $self->{score} ) { return $self->{score}; }
969 0         0 return;
970             }
971              
972              
973             =head2 number
974              
975             $group->number
976              
977             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.
978              
979             =cut
980              
981             sub number {
982 9645     9645 1 12445 my $self = shift;
983 9645         11317 my $number = shift;
984 9645 50       27033 if ( defined $number ) { $self->{number} = $number; }
  0 100       0  
985 9494         22064 elsif ( exists $self->{number} ) { return $self->{number}; }
986 151         299 return;
987             }
988              
989              
990             =head2 badpair
991              
992             $group->badpair
993              
994             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.
995              
996             =cut
997              
998             sub badpair {
999 4315     4315 1 5657 my $self = shift;
1000 4315         5542 my $badpair = shift;
1001 4315 100       11349 if ( defined $badpair ) { $self->{badpair} = $badpair; }
  1384 100       2485  
1002 2801         6379 elsif ( defined $self->{badpair} ) { return $self->{badpair}; }
1003 1514         2925 return;
1004             }
1005              
1006              
1007             =head2 members
1008              
1009             $group->members
1010              
1011             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.
1012              
1013             =cut
1014              
1015             sub members {
1016 18456     18456 1 23465 my $self = shift;
1017 18456         22707 my $members = shift;
1018 18456 100       50658 if ( defined $members ) { $self->{members} = $members; }
  2103 100       3523  
1019 16264         38533 elsif ( $self->{members} ) { return $self->{members}; }
1020 2192         4314 return;
1021             }
1022              
1023              
1024             =head2 c8swapper
1025              
1026             $pairing->c8swapper
1027              
1028             Gets/sets an iterator through the different exchanges of players in the two halves of the bracket.
1029              
1030             =cut
1031              
1032             sub c8swapper {
1033 973     973 1 1333 my $self = shift;
1034 973         1211 my $c8swapper = shift;
1035 973 100       3140 if ( defined $c8swapper ) { $self->{c8swapper} = $c8swapper; }
  223 100       1262  
1036 696         2132 elsif ( $self->{c8swapper} ) { return $self->{c8swapper}; }
1037             }
1038              
1039              
1040             =head2 _floatCheck
1041              
1042             %b65TestResults = _floatCheck( \@testee, $checkLevels );
1043              
1044             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.
1045              
1046             =cut
1047              
1048             sub _floatCheck {
1049 272     272   432 my $self = shift;
1050 272         349 my $untested = shift;
1051 272         567 my @paired = @$untested;
1052 272         803 my @nopairs = $self->_getNonPaired(@paired);
1053 272         460 my $levels = shift;
1054 272 50 33     1485 die "Float checks are $levels?" unless $levels and ref($levels) eq 'ARRAY';
1055 272         616 my $pprime = $self->pprime;
1056 272         586 my $s1 = $self->s1;
1057 272         405 my ($badpos, %badpos);
1058 272         535 my @pairtestee = @paired;
1059 272         634 my @nopairtestee = @nopairs;
1060 272         656 my @pairlevelpasser;
1061             my @nopairlevelpasser;
1062 0         0 my $message;
1063 272         835 B56: for my $level (@$levels)
1064             {
1065 882         1108 my ($round, $direction, $checkedOne, $id);
1066 882 100       2669 if ( $level =~ m/^B5/i ) { $round = 1; }
  367         575  
1067 515         723 else { $round = 2; }
1068 882 100       3211 if( $level =~ m/Down$/i) { $direction = 'Down'; $checkedOne = 0 }
  315 100       439  
  315         430  
1069 375         512 elsif ( $level =~ m/Up$/i ) { $direction = 'Up'; $checkedOne = 1 }
  375         518  
1070 192         366 else { @pairlevelpasser = @pairtestee; last B56 }
  192         484  
1071 690         1525 for my $pos ( 0 .. $#$s1 ) {
1072 940 100       2061 next unless defined $pairtestee[$pos];
1073 937         2087 my @pair = ( $pairtestee[$pos]->[0], $pairtestee[$pos]->[1] );
1074 937 100       1348 my @score = map { defined $_->score? $_->score: 0 } @pair;
  1874         5021  
1075 937         1645 my @float = map { $_->floats( -$round ) } @pair;
  1874         5270  
1076 937         1417 my $test = 0;
1077 937 50 100     4437 $test = ( $score[0] == $score[1] or $float[$checkedOne] ne
1078             $direction ) unless $direction eq 'None';# XXX check both?
1079 937 100       1615 if ( $test ) { $pairlevelpasser[$pos] = \@pair; }
  869         3008  
1080             else {
1081 68 50       249 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1082 68 50       148 $badpos = defined $badpos? $badpos: $pos;
1083 68   33     315 $id ||= $pair[$checkedOne]->pairingNumber;
1084             }
1085             }
1086 690 100 100     2969 if ($direction ne 'Up' and @nopairtestee and ( not $self->hetero or
      100        
      66        
1087             (grep {defined} @nopairtestee) == 1 ))
1088             {
1089             #my $potentialDownFloaters =
1090             # grep { grep { defined } @$_ } @nopairtestee;
1091 128         289 for my $pos ( 0 .. $#nopairtestee ) {
1092 314 100       709 next unless defined $nopairtestee[$pos];
1093 128 50 33     676 my @pair = @{ $nopairtestee[$pos] } if defined
  128         319  
1094             $nopairtestee[$pos] and ref $nopairtestee[$pos] eq 'ARRAY';
1095 128         184 my $tableTest = 0;
1096 128         161 my $idCheck;
1097 128         261 for my $player ( @pair) {
1098 256   100     860 my $test = ( not defined $player or
1099             ($player->floats(-$round) ne "Down") );
1100 256 100 33     958 $idCheck ||= $player->pairingNumber if $player and
      100        
1101             not $test;
1102 256 100       656 $tableTest++ if $test;
1103             }
1104 128 100       263 if ( $tableTest >= 2 ) { $nopairlevelpasser[$pos] = \@pair; }
  116         345  
1105             else {
1106 12 50       47 $badpos{$level} = defined $badpos{$level}? $badpos{$level}: $pos;
1107 12 50       26 $badpos = defined $badpos? $badpos: $pos;
1108 12 50       46 $id = $idCheck if $idCheck;
1109             }
1110             }
1111             }
1112 690         1206 my @retainables = grep { defined } @pairlevelpasser ;#
  884         2238  
1113             # , grep { defined } @nopairlevelpasser;
1114             # my @nonfloaters = grep { grep { defined } @$_ } @retainables;
1115 690 100 100     3678 if ( @retainables < $pprime or keys %badpos )
1116             # if ( @retainables < $pprime or $badpos )
1117             {
1118 80         123 my $badpos;
1119 80         158 for my $nextLevel ( @$levels )
1120             {
1121 146 100       363 next unless defined $badpos{ $nextLevel };
1122 80         129 $badpos = $badpos{ $nextLevel };
1123 80         140 last;
1124             }
1125 80         126 my $pluspos = $badpos+1;
1126 80         295 $message =
1127             "$level, table $pluspos: $id NOK. Floated $direction $round rounds ago";
1128 80         699 return badpos => $badpos, passer => undef, message => $message;
1129             }
1130             }
1131             continue {
1132 610         1316 @pairtestee = @pairlevelpasser;
1133 610         966 @nopairtestee = @nopairlevelpasser;
1134 610         934 undef @pairlevelpasser;
1135 610         1142 undef @nopairlevelpasser;
1136             }
1137 192         1396 return badpos => undef, passer => \@pairlevelpasser, message => "B56: OK.";
1138             }
1139              
1140              
1141             =head2 _getNonPaired
1142              
1143             $bracket->_getNonPaired([$alekhine,$uwe],undef,[$deepblue,$yournewnike])
1144              
1145             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].
1146              
1147             =cut
1148              
1149             sub _getNonPaired {
1150 464     464   660 my $self = shift;
1151 464         837 my @pairables = @_;
1152 464         1006 my $s1 = $self->s1;
1153 464         1032 my $s2 = $self->s2;
1154 464         627 my @nopairs;
1155 464         1023 for my $pos ( 0..$#pairables )
1156             {
1157 599 50       1554 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ] unless
1158             defined $pairables[$pos];
1159             }
1160 464         1221 for my $pos ( $#pairables+1 .. $#$s1 )
1161             {
1162 2         7 $nopairs[$pos] = [ $s1->[$pos], $s2->[$pos] ];
1163             }
1164 464         1064 for my $pos ( $#$s1+1 .. $#$s2 )
1165             {
1166 753         1939 $nopairs[$pos] = [ undef, $s2->[$pos] ];
1167             }
1168 464         1567 return @nopairs;
1169             }
1170              
1171              
1172             =head1 AUTHOR
1173              
1174             Dr Bean, C<< >>
1175              
1176             =head1 BUGS
1177              
1178             Please report any bugs or feature requests to
1179             C, or through the web interface at
1180             L.
1181             I will be notified, and then you'll automatically be notified of progress on
1182             your bug as I make changes.
1183              
1184             =head1 SUPPORT
1185              
1186             You can find documentation for this module with the perldoc command.
1187              
1188             perldoc Games::Tournament::Swiss::Bracket
1189              
1190             You can also look for information at:
1191              
1192             =over 4
1193              
1194             =item * AnnoCPAN: Annotated CPAN documentation
1195              
1196             L
1197              
1198             =item * CPAN Ratings
1199              
1200             L
1201              
1202             =item * RT: CPAN's request tracker
1203              
1204             L
1205              
1206             =item * Search CPAN
1207              
1208             L
1209              
1210             =back
1211              
1212             =head1 ACKNOWLEDGEMENTS
1213              
1214             See L for the FIDE's Swiss rules.
1215              
1216             =head1 COPYRIGHT & LICENSE
1217              
1218             Copyright 2006 Dr Bean, all rights reserved.
1219              
1220             This program is free software; you can redistribute it and/or modify it
1221             under the same terms as Perl itself.
1222              
1223             =cut
1224              
1225             1; # End of Games::Tournament::Swiss::Bracket
1226              
1227             # vim: set ts=8 sts=4 sw=4 noet: