File Coverage

blib/lib/Games/Tournament/Contestant/Swiss/Preference.pm
Criterion Covered Total %
statement 98 107 91.5
branch 41 48 85.4
condition 18 24 75.0
subroutine 19 21 90.4
pod 9 9 100.0
total 185 209 88.5


line stmt bran cond sub pod time code
1             package Games::Tournament::Contestant::Swiss::Preference;
2             $Games::Tournament::Contestant::Swiss::Preference::VERSION = '0.20';
3             # Last Edit: 2016 Jan 01, 13:45:02
4             # $Id: $
5              
6 30     30   130080 use warnings;
  30         52  
  30         1578  
7 30     30   152 use strict;
  30         49  
  30         619  
8 30     30   467 use Carp;
  30         60  
  30         2053  
9              
10 30     30   164 use List::Util qw/first/;
  30         52  
  30         1882  
11 30     30   923 use List::MoreUtils qw/any/;
  30         11846  
  30         183  
12              
13 30     30   13373 use Games::Tournament::Swiss::Config;
  30         54  
  30         1424  
14              
15 30 100       1630 use constant ROLES => @Games::Tournament::Swiss::Config::roles?
16             @Games::Tournament::Swiss::Config::roles:
17 30     30   176 Games::Tournament::Swiss::Config->roles;
  30         65  
18              
19 30     30   144 use base qw/Games::Tournament/;
  30         46  
  30         22799  
20              
21             # use overload qw/0+/ => 'next', qw/""/ => 'value', fallback => 1;
22              
23             =head1 NAME
24              
25             Games::Tournament::Contestant::Swiss::Preference A competitor's right to a role.
26              
27             =cut
28              
29             =head1 SYNOPSIS
30              
31             pray if $preference->role eq 'Black' and $preference->strength eq 'Strong';
32              
33             =head1 DESCRIPTION
34              
35             The preference, or expectation/right/duty one has with reference to a role, eg White, in the next round depends on the difference between the number of games previously played in it and in the alternative roles, and is either Mild, Strong, or Absolute. The more games played in other roles than in this role, the greater the right/duty to play the next game in this role. The FIDE Swiss Rules (C04.1) represent the difference as the number of Games as White minus the number as Black, so a greater number of games as Black is a negative number and of White a positive number. For equal number of games, +0 indicates the last game was as White, and -0 indicates the last game was as Black. So +0 represents a Mild preference for Black and -0 for White. This implementation uses a 'sign' field to perform the same function as the +/- sign.
36             As an API, the strength method returns 'Mild', 'Strong', or 'Absolute' and the role method returns 'Black', 'White', or whatever the preferred role is, respecting the 2 consecutive games in the same role rule. A7
37              
38             =head1 METHODS
39              
40             =head2 new
41              
42             $pref = Games::Tournament::Contestant::Swiss::Preference->new(
43             difference => 0, sign => 'Black', round => 0 );
44              
45             The default difference is 0. The default sign is ''.
46              
47             =cut
48              
49             sub new {
50 1931     1931 1 131903 my $self = shift;
51 1931         2922 my %args = @_;
52 1931 100       5604 $args{sign} = '' unless $args{sign};
53 1931 100       4422 $args{difference} = 0 unless $args{difference};
54 1931         3611 my $pref = bless \%args, $self;
55 1931         5673 return $pref;
56             }
57              
58              
59             =head2 update
60              
61             $pref->update( \@oldRoles )
62              
63             Updates the difference (ie, the internal representation of preference) on the basis of the last role (and second-last role) in \@oldRoles. A minimal sanity check is performed. \@oldRoles is a history of roles in previous rounds, and it is expected only the last role of this history has not yet been used to update the preference. That is, this method must be used round-by-round to keep a players preference up to date. However, the second-last role (in addition to the last role) is also needed to determine the preference in cases when the same role was taken in the last 2 games. So for updates after the second round, make sure the history is at least 2 elements long. Byes and unplayed games have no effect on the preference, so make sure that roles in unplayed games don't make it into oldRoles A5, F2 .
64              
65             =cut
66              
67             sub update {
68 749     749 1 1887 my $self = shift;
69 749         955 my $roles = shift;
70 749         955 my $message = "Preference update: ";
71 749 100 66 1142   4268 return unless $roles->[-1] and any { $roles->[-1] eq $_ } ROLES;
  1142         3613  
72 727         2679 my @reverseRoles = reverse @$roles;
73 727         973 my $lastRole = $reverseRoles[0];
74 727         930 my $before = $reverseRoles[1];
75 727         874 my $oneBeforeThat = $reverseRoles[2];
76 727 100 100     3672 $message .= "3-game run as $lastRole\n" if $before and $oneBeforeThat and
      100        
      100        
77             $oneBeforeThat eq $before and $before eq $lastRole;
78 727         1518 my $difference = $self->difference;
79 727         1532 my $sign = $self->sign;
80 727     1004   3174 my $otherDirection = first { $_ ne $sign } ROLES;
  1004         1728  
81 727 100 66     4386 if ( not $sign or not defined $difference ) {
    100          
    50          
82 109         160 $sign = $lastRole;
83 109         163 $difference = 1;
84             }
85             elsif ( $lastRole eq $otherDirection ) {
86 492 100       1077 if ( $difference > 0 ) {
    50          
87 288         350 $difference--;
88 288 100       697 if ( $difference == 0 ) {
89 243         416 $sign = $otherDirection;
90             }
91             }
92             elsif ( $difference == 0 ) {
93 204         288 $sign = $lastRole;
94 204         325 $difference = 1;
95             }
96             else {
97 0         0 die "$difference games more as $sign after $lastRole role?";
98             }
99             }
100             elsif ( $lastRole eq $sign ) {
101 126         174 $difference++;
102 126 100       314 if ( $difference > 2 ) {
103 32         73 $message .= "$difference games more as $lastRole\n";
104             }
105             }
106             else {
107 0         0 die
108             "$lastRole role update on ${difference}-game difference in $sign role?";
109             }
110 727         1520 $self->sign($sign);
111 727         1431 $self->difference($difference);
112 727 100       1287 if ($before) { $self->lastTwo( [ $before, $lastRole ] ); }
  529         1577  
113 198         575 else { $self->lastTwo( [$lastRole] ); }
114             }
115              
116              
117             =head2 asString
118              
119             $pref->asString
120              
121             The difference as a string, ^[+-][012]$. '0' represents a mild preference, '1' a strong one and '2' an absolute one. '-' represents a preference for White, or the first element of @Games::Tournament::Swiss::Config::roles, and '+' represents a preference for Black or the second element. A player may have an absolute preference even if the difference is 0, because it played the previous 2 rounds in the other color.
122              
123             =cut
124              
125              
126             sub asString {
127 0     0 1 0 my $self = shift;
128 0 0       0 my $string = $self->sign eq (ROLES)[0] ? '+' :
    0          
129             $self->sign eq (ROLES)[1] ? '-' : '';
130 0         0 $string .= $self->difference;
131             }
132              
133              
134             =head2 difference
135              
136             $pref->difference(2)
137              
138             Sets/gets the value of the difference in games played in one role over those played in other alternative roles. Equals either 0,1,2.
139              
140             =cut
141              
142              
143             sub difference {
144 6916     6916 1 8605 my $self = shift;
145 6916         8223 my $difference = shift();
146 6916 100       13926 $self->{difference} = $difference if defined $difference;
147 6916         14685 return $self->{difference};
148             }
149              
150              
151             =head2 sign
152              
153             $pref->sign('Black')
154             $pref->sign('-')
155              
156             Sets/gets the role which the player has taken more often, or more recently, than other alternative roles. The preference is thus for the other role.
157              
158             =cut
159              
160             sub sign {
161 76757     76757 1 92175 my $self = shift;
162 76757   66     192945 my $sign = shift() || $self->{sign};
163 76757         176374 my %abbrev = ( White => '+', Black => '-' );
164 76757         178297 my %expando = reverse %abbrev;
165 76757 50       173341 $sign = $expando{$sign} if $expando{$sign};
166 76757         109173 $self->{sign} = $sign;
167 76757         310309 return $sign;
168             }
169              
170              
171             =head2 strength
172              
173             $pref->strength
174              
175             Gets the strength of the preference, 'Mild,' 'Strong,' or 'Absolute.'
176              
177             =cut
178              
179             sub strength {
180 3954     3954 1 5278 my $self = shift;
181 3954         7410 my @degree = qw/Mild Strong Absolute/;
182 3954         7356 my $diff = $self->difference;
183 3954         5766 my $strength = $degree[$diff];
184 3954 100       7701 $strength = 'Absolute' if $diff > 2 ;
185 3954         4386 my @lastRoles = @{ $self->lastTwo };
  3954         7158  
186 3954 100       9411 if ( @lastRoles == 2 ) {
187 3322 100       7654 $strength = 'Absolute' if $lastRoles[0] eq $lastRoles[1];
188             }
189 3954         21750 return $strength;
190             }
191              
192              
193             =head2 role
194              
195             $pref->role
196              
197             Gets the role which the preference entitles/requires the player to take in the next round. Not defined if sign is ''.
198              
199             =cut
200              
201             sub role {
202 30603     30603 1 41587 my $self = shift;
203 30603         34398 my $role;
204 30603 100   43112   56099 $role = first { $_ ne $self->sign } ROLES if $self->sign;
  43112         88436  
205 30603         76389 my @lastRoles = @{ $self->lastTwo };
  30603         58904  
206 30603 100 100     129271 if ( @lastRoles == 2 and $lastRoles[0] eq $lastRoles[1] )
207             {
208 3909     5903   15616 $role = first { $_ ne $lastRoles[0] } ROLES;
  5903         9828  
209             }
210 30603         157128 return $role;
211             }
212              
213              
214             =head2 round
215              
216             $pref->round
217              
218             Sets/gets the round in this game up to which play is used to calculate the preference . The default is 0.
219              
220             =cut
221              
222             sub round {
223 0     0 1 0 my $self = shift;
224 0   0     0 my $round = shift() || $self->{round};
225 0         0 $self->{round} = $round;
226 0         0 return $round;
227             }
228              
229              
230             =head2 lastTwo
231              
232             $pref->lastTwo
233              
234             Sets/gets a list of the roles in the last 2 games. If the 2 roles are the same, there is an absolute preference for the other role.
235              
236             =cut
237              
238             sub lastTwo {
239 35329     35329 1 45123 my $self = shift;
240 35329         42629 my $lastTwo = shift;
241 35329 100       88684 if ( defined $lastTwo ) { $self->{lastTwo} = $lastTwo; }
  727 100       4125  
242 31130         82003 elsif ( $self->{lastTwo} ) { return $self->{lastTwo}; }
243 3472         7228 else { return []; }
244             }
245              
246             =head1 AUTHOR
247              
248             Dr Bean, C<< >>
249              
250             =head1 BUGS
251              
252             Please report any bugs or feature requests to
253             C, or through the web interface at
254             L.
255             I will be notified, and then you'll automatically be notified of progress on
256             your bug as I make changes.
257              
258             =head1 SUPPORT
259              
260             You can find documentation for this module with the perldoc command.
261              
262             perldoc Games::Tournament::Contestant::Swiss::Preference
263              
264             You can also look for information at:
265              
266             =over 4
267              
268             =item * AnnoCPAN: Annotated CPAN documentation
269              
270             L
271              
272             =item * CPAN Ratings
273              
274             L
275              
276             =item * RT: CPAN's request tracker
277              
278             L
279              
280             =item * Search CPAN
281              
282             L
283              
284             =back
285              
286             =head1 ACKNOWLEDGEMENTS
287              
288             =head1 COPYRIGHT & LICENSE
289              
290             Copyright 2006 Dr Bean, all rights reserved.
291              
292             This program is free software; you can redistribute it and/or modify it
293             under the same terms as Perl itself.
294              
295             =cut
296              
297             1; # End of Games::Tournament::Contestant::Preference
298              
299             # vim: set ts=8 sts=4 sw=4 noet: