File Coverage

blib/lib/Games/Tournament/Contestant.pm
Criterion Covered Total %
statement 81 167 48.5
branch 28 58 48.2
condition 9 25 36.0
subroutine 17 25 68.0
pod 16 16 100.0
total 151 291 51.8


line stmt bran cond sub pod time code
1             package Games::Tournament::Contestant;
2             $Games::Tournament::Contestant::VERSION = '0.20';
3             # Last Edit: 2016 Jan 01, 13:44:39
4             # $Id: $
5              
6 28     28   24526 use warnings;
  28         226  
  28         790  
7 28     28   199 use strict;
  28         45  
  28         557  
8 28     28   124 use Carp;
  28         45  
  28         2205  
9              
10 28     28   140 use base qw/Games::Tournament/;
  28         46  
  28         9961  
11 28     28   1877 use List::Util qw/sum/;
  28         51  
  28         1693  
12 28     28   147 use List::MoreUtils qw/all/;
  28         51  
  28         192  
13 28 100       2908 use constant ROLES => @Games::Tournament::Swiss::Config::roles?
14             @Games::Tournament::Swiss::Config::roles:
15 28     28   11693 Games::Tournament::Swiss::Config->roles;
  28         51  
16 28 100       36568 use constant SCORES => %Games::Tournament::Swiss::Config::scores?
17             %Games::Tournament::Swiss::Config::scores:
18 28     28   141 Games::Tournament::Swiss::Config->scores;
  28         47  
19              
20             # use overload qw/0+/ => 'id', qw/""/ => 'name', fallback => 1;
21              
22             =head1 NAME
23              
24             Games::Tournament::Contestant A competitor matched with others over a series of rounds
25              
26             =cut
27              
28             =head1 SYNOPSIS
29              
30             my $foo = Games::Tournament::Contestant->new( rating => '15', name => 'Your New Knicks' );
31             ...
32              
33             =head1 DESCRIPTION
34              
35             A generic tournament/series player/team contestant object.
36              
37             =head1 METHODS
38              
39             =head2 new
40              
41             $team = Games::Tournament::Contestant->new( id => '15', name => 'Lala Lakers', rating => 0, score => 1000, )
42             $grandmaster = Games::Tournament::Contestant->new( name => 'Jose Raul Capablanca', rating => 1000 )
43              
44             Make sure the ids of all your contestants are unique.
45              
46             =cut
47              
48             sub new {
49 0     0 1 0 my $self = shift;
50 0         0 my %args = @_;
51 0         0 return bless \%args, $self;
52             }
53              
54              
55             =head2 clone
56              
57             $monster = $alekhine->clone( score => 1000, reputation => 'bad' )
58              
59             Creates a similar object to $alekhine, with the same id, name, score, title, and rating fields but with any other changes or additions you want to make.
60              
61             =cut
62              
63             sub clone {
64 0     0 1 0 my $self = shift;
65 0         0 my %args = @_;
66 0   0     0 my $clone = Games::Tournament::Contestant->new(
      0        
      0        
      0        
      0        
67             id => $self->id || undef,
68             name => $self->name || undef,
69             score => $self->score || undef,
70             title => $self->title || undef,
71             rating => $self->rating || undef,
72             );
73 0         0 foreach my $key ( keys %args ) {
74 0         0 $clone->{$key} = $args{$key};
75             }
76 0         0 return $clone;
77             }
78              
79              
80             =head2 findCard
81              
82             @venues = $player->findCard(@games);
83              
84             Returns a/the first game in @games in which $player is a contestant. 'findCard' expects the game objects to have 'contestants' accessors and be 'canonize'able. The players are grepped for stringwise id equality.
85              
86             =cut
87              
88             sub findCard {
89 0     0 1 0 my $self = shift;
90 0         0 my $id = $self->id;
91 0         0 my @games = @_;
92 0         0 my @cards;
93 0         0 foreach my $game (@games) {
94 0         0 $game->canonize;
95 0         0 my $players = $game->contestants;
96             push @cards, $game
97 0 0       0 if grep { $players->{$_}->id eq $id } keys %$players;
  0         0  
98             }
99 0         0 return $cards[0];
100             }
101              
102              
103             =head2 myOpponent
104              
105             $opponent = $player->myOpponent($game);
106              
107             Returns a/the opponent in $game of $player. 'myOpponent' expects the game object to have 'contestants' accessors. The players are grepped for stringwise id equality.
108              
109             =cut
110              
111             sub myOpponent {
112 576     576 1 840 my $self = shift;
113 576         1180 my $id = $self->id;
114 576         879 my $game = shift;
115 576 50 33     3305 croak "Looking for opponent, but no contestants in $game game" unless
116             $game and $game->can('contestants');
117 576         1613 my $contestants = $game->contestants;
118 576         1554 my @contestants = values %$contestants;
119 576         879 my @ids = map { $_->id } @contestants;
  1152         2222  
120 576 50       10484 die "Player $id not in match of @ids" unless grep m/$_/, @ids;
121 576         724 my @opponents;
122              
123 576         967 for my $contestant (@contestants) {
124 1152 100       2391 push @opponents, $contestant if $contestant->id ne $id;
125             }
126 576         3295 return $opponents[0];
127             }
128              
129              
130             =head2 copyCard
131              
132             @result = $player->copyCard(@games);
133              
134             Stores a ref to the @games in which $player has participated and copied the cards for. @games may or may not be a complete list of result for all rounds, and may include games in which $player wasn't a participant. Pushed to an anonymous array stored as the 'play' field. 'copyCard' expects the game objects to have 'round' and 'contestants' accessors and be 'canonize'able.
135              
136             =cut
137              
138             sub copyCard {
139 0     0 1 0 my $self = shift;
140 0         0 my $id = $self->id;
141 0         0 my $play = $self->play;
142 0         0 my @games = @_;
143 0         0 my %result;
144 0         0 foreach my $game (@games) {
145 0         0 $game->canonize;
146 0         0 my $round = $game->round;
147 0         0 my $players = $game->contestants;
148 0         0 my %roles = map { $players->{$_}->id => $_ } keys %$players;
  0         0  
149 0 0       0 next unless exists $roles{$id};
150 0         0 push @$play, $game;
151             }
152 0         0 $self->play($play);
153             }
154              
155             =head2 writeCard (deprecated)
156              
157             @result = $player->writeCard(@games);
158              
159             Updates the contestant's result in the matches played, using no intelligence if records only have only opponents' scores. @games may or may not be a complete list of result for all rounds, and may include games in which $player wasn't a participant. Stored as a 'play' field and keyed on the round, the resultant records have 'opponent' and 'result' subfields. 'writeCard' expects the game objects to have 'round', 'contestants' and 'result' accessors. Returns the new play field.
160             TODO The 'opponent' subfield will be an anonymous array of player objects if it is a multi-player game.
161              
162             =cut
163              
164             sub writeCard {
165 0     0 1 0 my $self = shift;
166 0         0 my $id = $self->id;
167 0         0 my @games = @_;
168 0         0 my %result;
169 0         0 foreach my $game (@games) {
170 0         0 $game->canonize;
171 0         0 my $round = $game->round;
172 0         0 my $players = $game->contestants;
173 0         0 my %roles = map { $players->{$_}->id => $_ } keys %$players;
  0         0  
174 0 0       0 next unless exists $roles{$id};
175 0         0 my $role = $roles{$id};
176 0         0 my $opponent;
177 0         0 foreach my $player ( values %$players ) {
178 0 0       0 $opponent = $player unless $player->id == $self->id;
179             }
180 0         0 $result{$round} = { opponent => $opponent };
181 0         0 $result{$round}{result} = $game->{result}->{$role};
182             }
183 0         0 $self->play( \%result );
184             }
185              
186              
187             =head2 score
188              
189             $rounds = $deepblue->score
190             next if $deepblue->score
191              
192             Gets/sets the total score over the rounds in which $deepblue has a score. Don't forget to tally $deepblue's scorecard with the appropriate games first! We don't check any cards. Internally, this method accumulates the results of all the rounds into a total score, unless no results exist. If they don't exist, a hash key $self->{score} is consulted. You can set the score this way too, but don't do that. It bypasses the elegant code to do it from individual game results stored by the Games::Tournament::Contestant object. It's a hack to allow importing a pairing table. Finally, if none of the above apply, undef is returned, despite FIDE Rule A2. This means that Bracket and FIDE methods using the score method need to handle undef scores.
193              
194             =cut
195              
196             sub score {
197 127798     127798 1 172556 my $self = shift;
198 127798         326326 my %converter = SCORES;
199 127798         162677 my $score = shift;
200 127798 100       253459 if ( defined $score ) { $self->{score} = $score; }
  160         409  
201 127798         244459 my $scores = $self->scores;
202             return $self->{score} unless defined $scores and
203 127798 100 100 146002   655456 all { defined $_ } values %$scores;
  146002         767381  
204 23462         73303 my %lcconverter = map { lc($_) => $converter{$_} } keys %converter;
  126580         298245  
205 23462         69112 my %scores = map { $_ => lc $scores->{$_} } keys %$scores;
  58623         140676  
206 23462         53277 for my $round ( keys %scores ) {
207             die
208             "Round $round $scores->{$round}, $scores{$round} score unconvertible to $lcconverter{$scores{$round}} for player $self->{id}"
209 58623 50 66     261902 unless defined( $scores{$round} and $lcconverter{ $scores{$round} } );
210             }
211 23462         43653 my @values = map { $lcconverter{$_} } values %scores;
  58623         106305  
212 23462         51589 my $sum = sum(@values);
213 23462 50       150984 return $sum if defined $sum;
214 0         0 return undef;
215             }
216              
217              
218             =head2 met
219              
220             $rounds = $deepblue->met(@grandmasters)
221             next if $deepblue->met($capablanca)
222              
223             Returns an anonymous hash, keyed on @grandmasters' ids, either of the gamecards in which $deepblue remembers meeting the members of @grandmasters or of the empty string '' if there is no record of such a meeting. Don't forget to tally $deepblue's scorecard with the appropriate games first (using $deepblue->play?)! We don't check $deepblue's partners' cards. (Assumes players do not meet more than once!) Don't confuse this with Games::Tournament::met!
224              
225             =cut
226              
227             sub met {
228 0     0 1 0 my $self = shift;
229 0         0 my @opponents = @_;
230 0         0 my $games = $self->play;
231 0         0 my @rounds = keys %$games;
232 0         0 my @ids = map { $_->id } @opponents;
  0         0  
233 0         0 my %gameAgainst;
234 0         0 @gameAgainst{@ids} = ('') x @ids;
235 0         0 for my $round ( @rounds )
236             {
237 0         0 my $gameInRound = $games->{$round};
238 0 0       0 next unless UNIVERSAL::isa $gameInRound, 'Games::Tournament::Card';
239 0         0 my $opponent = $self->myOpponent($gameInRound);
240 0         0 my $opponentId = $opponent->id;
241 0         0 $gameAgainst{$opponentId} = $gameInRound;
242             }
243 0 0       0 carp $self->id . " played @ids? Where are the cards?" unless %gameAgainst;
244 0         0 return \%gameAgainst;
245             }
246              
247              
248             =head2 name
249              
250             $member->name('Alexander Alekhine');
251             $member->name
252              
253             Sets or gets the name of the contesting individual or team, a string that may or may not be unique to the tournament member.
254              
255             =cut
256              
257             sub name {
258 2844     2844 1 3722 my $self = shift;
259 2844         3253 my $name = shift;
260 2844 50       7904 if ( defined $name ) { $self->{name} = $name; }
  0 50       0  
261 2844         10039 elsif ( exists $self->{name} ) { return $self->{name}; }
262             }
263              
264              
265             =head2 title
266              
267             $member->title('Grandmaster')
268              
269             Sets/gets the title of the contestant, a courtesy given to the contestant.
270              
271             =cut
272              
273             sub title {
274 1010     1010 1 1199 my $self = shift;
275 1010         1150 my $title = shift;
276 1010 50       3023 if ( defined $title ) { $self->{title} = $title; }
  0 100       0  
277 962         3734 elsif ( exists $self->{title} ) { return $self->{title}; }
278             }
279              
280              
281             =head2 scores
282              
283             $member->scores
284              
285             Sets/gets the scores (actually results, eg 'Draw', 'Win') of the contestant in the different matches of the tournament, an ongoing record of their standing in the competition. These scores may or may not include the current score. To calculate the total score, use 'score', because internally the scores are not stored as number scores.
286              
287             =cut
288              
289             sub scores {
290 129094     129094 1 159027 my $self = shift;
291 129094   66     322226 my $scores = shift() || $self->{scores};
292 129094         186643 $self->{scores} = $scores;
293 129094         210434 return $scores;
294             }
295              
296              
297             =head2 rating
298              
299             $member->rating
300              
301             Sets/gets the rating of the contestant, an estimate of their strength. The constructor assumes if no rating or a non-numeric rating is given, that they don't have a rating, and it is set to 0.
302              
303             =cut
304              
305             sub rating {
306 54818     54818 1 69338 my $self = shift;
307 54818         65062 my $rating = shift;
308 54818 50 33     169901 if ( defined $rating and $rating =~ m/^\d$/ ) { $self->{rating} = $rating; }
  0 100       0  
309 52937         201525 elsif ( exists $self->{rating} ) { return $self->{rating}; }
310 1881         6465 else { return 0; }
311             }
312              
313              
314             =head2 play
315              
316             $games = $member->play;
317             $games = $member->play( { $lastround => $game } );
318              
319             Sets/gets a hash reference to the result of the pairings in each of the rounds played so far. Don't use this to record a player's match result. Use $tourney->collectCards. Implementation: The keys of the hash are the round numbers and the values are the gamecard of the player in that round. Very similar to the play accessor for tournaments, which is what collectCards uses.
320              
321             =cut
322              
323             sub play {
324 0     0 1 0 my $self = shift;
325 0         0 my $play = shift;
326 0 0       0 if ( defined $play ) {
    0          
327 0         0 my @rounds = keys %$play;
328 0         0 for my $round ( @rounds ) {
329 0         0 $self->{play}->{$round} = $play->{$round};
330             }
331             }
332 0         0 elsif ( $self->{play} ) { return $self->{play}; }
333             }
334              
335              
336             =head2 id
337              
338             $member->id
339              
340             Returns/sets the id of the contestant, a number unique to the member. Users must make sure no two players have the same id. Pairing numbers may change with late entries, so the id is important.
341              
342             =cut
343              
344             sub id {
345 197059     197059 1 595899 my $self = shift;
346 197059         223480 my $id = shift;
347 197059 50       558279 if ( defined $id ) { $self->{id} = $id; }
  0 50       0  
348 197059         577411 elsif ( exists $self->{id} ) { return $self->{id}; }
349             }
350              
351             =head2 firstround
352              
353             $member->firstround
354              
355             Returns/sets the firstround of the contestant, the round in which they first played or will play. Necessary for handling late entrants.
356              
357             =cut
358              
359             sub firstround {
360 4210     4210 1 5458 my $self = shift;
361 4210         5184 my $firstround = shift;
362 4210 100       16247 if ( defined $firstround ) { $self->{firstround} = $firstround; }
  1744 100       3743  
363 722         2480 elsif ( exists $self->{firstround} ) { return $self->{firstround}; }
364             }
365              
366              
367             =head2 absent
368              
369             $member->absent(1)
370             puah @absent if $member->absent
371              
372             A flag of convenience telling you whether this player is absent and not to be paired in the tournament. This is not the same as a forfeit. The Games::Tournament::Swiss constructor uses this.
373              
374             =cut
375              
376             sub absent {
377 0     0 1   my $self = shift;
378 0           my $absent = shift;
379 0 0         if ( $absent ) { $self->{absent} = 1; return }
  0 0          
  0            
380 0           elsif ( defined $self->{absent} ) { return $self->{absent}; }
381 0           else { return; }
382             }
383              
384              
385             =head1 AUTHOR
386              
387             Dr Bean, C<< >>
388              
389             =head1 BUGS
390              
391             Please report any bugs or feature requests to
392             C, or through the web interface at
393             L.
394             I will be notified, and then you'll automatically be notified of progress on
395             your bug as I make changes.
396              
397             =head1 SUPPORT
398              
399             You can find documentation for this module with the perldoc command.
400              
401             perldoc Games::Tournament::Contestant
402              
403             You can also look for information at:
404              
405             =over 4
406              
407             =item * AnnoCPAN: Annotated CPAN documentation
408              
409             L
410              
411             =item * CPAN Ratings
412              
413             L
414              
415             =item * RT: CPAN's request tracker
416              
417             L
418              
419             =item * Search CPAN
420              
421             L
422              
423             =back
424              
425             =head1 ACKNOWLEDGEMENTS
426              
427             =head1 COPYRIGHT & LICENSE
428              
429             Copyright 2006 Dr Bean, all rights reserved.
430              
431             This program is free software; you can redistribute it and/or modify it
432             under the same terms as Perl itself.
433              
434             =cut
435              
436             1; # End of Games::Tournament::Contestant
437              
438             # vim: set ts=8 sts=4 sw=4 noet: