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              
3             # Last Edit: 2010 9月 01, 13時29分53秒
4             # $Id: $
5              
6 28     28   25065 use warnings;
  28         219  
  28         779  
7 28     28   126 use strict;
  28         43  
  28         610  
8 28     28   124 use Carp;
  28         47  
  28         2090  
9              
10 28     28   132 use base qw/Games::Tournament/;
  28         54  
  28         10084  
11 28     28   658 use List::Util qw/sum/;
  28         52  
  28         2943  
12 28     28   143 use List::MoreUtils qw/all/;
  28         45  
  28         193  
13 28 100       3019 use constant ROLES => @Games::Tournament::Swiss::Config::roles?
14             @Games::Tournament::Swiss::Config::roles:
15 28     28   11872 Games::Tournament::Swiss::Config->roles;
  28         51  
16 28 100       37900 use constant SCORES => %Games::Tournament::Swiss::Config::scores?
17             %Games::Tournament::Swiss::Config::scores:
18 28     28   140 Games::Tournament::Swiss::Config->scores;
  28         46  
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             =head1 VERSION
27              
28             Version 0.03
29              
30             =cut
31              
32             our $VERSION = '0.03';
33              
34             =head1 SYNOPSIS
35              
36             my $foo = Games::Tournament::Contestant->new( rating => '15', name => 'Your New Knicks' );
37             ...
38              
39             =head1 DESCRIPTION
40              
41             A generic tournament/series player/team contestant object.
42              
43             =head1 METHODS
44              
45             =head2 new
46              
47             $team = Games::Tournament::Contestant->new( id => '15', name => 'Lala Lakers', rating => 0, score => 1000, )
48             $grandmaster = Games::Tournament::Contestant->new( name => 'Jose Raul Capablanca', rating => 1000 )
49              
50             Make sure the ids of all your contestants are unique.
51              
52             =cut
53              
54             sub new {
55 0     0 1 0 my $self = shift;
56 0         0 my %args = @_;
57 0         0 return bless \%args, $self;
58             }
59              
60              
61             =head2 clone
62              
63             $monster = $alekhine->clone( score => 1000, reputation => 'bad' )
64              
65             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.
66              
67             =cut
68              
69             sub clone {
70 0     0 1 0 my $self = shift;
71 0         0 my %args = @_;
72 0   0     0 my $clone = Games::Tournament::Contestant->new(
      0        
      0        
      0        
      0        
73             id => $self->id || undef,
74             name => $self->name || undef,
75             score => $self->score || undef,
76             title => $self->title || undef,
77             rating => $self->rating || undef,
78             );
79 0         0 foreach my $key ( keys %args ) {
80 0         0 $clone->{$key} = $args{$key};
81             }
82 0         0 return $clone;
83             }
84              
85              
86             =head2 findCard
87              
88             @venues = $player->findCard(@games);
89              
90             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.
91              
92             =cut
93              
94             sub findCard {
95 0     0 1 0 my $self = shift;
96 0         0 my $id = $self->id;
97 0         0 my @games = @_;
98 0         0 my @cards;
99 0         0 foreach my $game (@games) {
100 0         0 $game->canonize;
101 0         0 my $players = $game->contestants;
102             push @cards, $game
103 0 0       0 if grep { $players->{$_}->id eq $id } keys %$players;
  0         0  
104             }
105 0         0 return $cards[0];
106             }
107              
108              
109             =head2 myOpponent
110              
111             $opponent = $player->myOpponent($game);
112              
113             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.
114              
115             =cut
116              
117             sub myOpponent {
118 576     576 1 698 my $self = shift;
119 576         1009 my $id = $self->id;
120 576         746 my $game = shift;
121 576 50 33     2970 croak "Looking for opponent, but no contestants in $game game" unless
122             $game and $game->can('contestants');
123 576         1402 my $contestants = $game->contestants;
124 576         1285 my @contestants = values %$contestants;
125 576         813 my @ids = map { $_->id } @contestants;
  1152         2148  
126 576 50       8099 die "Player $id not in match of @ids" unless grep m/$_/, @ids;
127 576         660 my @opponents;
128              
129 576         898 for my $contestant (@contestants) {
130 1152 100       2192 push @opponents, $contestant if $contestant->id ne $id;
131             }
132 576         2657 return $opponents[0];
133             }
134              
135              
136             =head2 copyCard
137              
138             @result = $player->copyCard(@games);
139              
140             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.
141              
142             =cut
143              
144             sub copyCard {
145 0     0 1 0 my $self = shift;
146 0         0 my $id = $self->id;
147 0         0 my $play = $self->play;
148 0         0 my @games = @_;
149 0         0 my %result;
150 0         0 foreach my $game (@games) {
151 0         0 $game->canonize;
152 0         0 my $round = $game->round;
153 0         0 my $players = $game->contestants;
154 0         0 my %roles = map { $players->{$_}->id => $_ } keys %$players;
  0         0  
155 0 0       0 next unless exists $roles{$id};
156 0         0 push @$play, $game;
157             }
158 0         0 $self->play($play);
159             }
160              
161             =head2 writeCard (deprecated)
162              
163             @result = $player->writeCard(@games);
164              
165             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.
166             TODO The 'opponent' subfield will be an anonymous array of player objects if it is a multi-player game.
167              
168             =cut
169              
170             sub writeCard {
171 0     0 1 0 my $self = shift;
172 0         0 my $id = $self->id;
173 0         0 my @games = @_;
174 0         0 my %result;
175 0         0 foreach my $game (@games) {
176 0         0 $game->canonize;
177 0         0 my $round = $game->round;
178 0         0 my $players = $game->contestants;
179 0         0 my %roles = map { $players->{$_}->id => $_ } keys %$players;
  0         0  
180 0 0       0 next unless exists $roles{$id};
181 0         0 my $role = $roles{$id};
182 0         0 my $opponent;
183 0         0 foreach my $player ( values %$players ) {
184 0 0       0 $opponent = $player unless $player->id == $self->id;
185             }
186 0         0 $result{$round} = { opponent => $opponent };
187 0         0 $result{$round}{result} = $game->{result}->{$role};
188             }
189 0         0 $self->play( \%result );
190             }
191              
192              
193             =head2 score
194              
195             $rounds = $deepblue->score
196             next if $deepblue->score
197              
198             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.
199              
200             =cut
201              
202             sub score {
203 127833     127833 1 168104 my $self = shift;
204 127833         318914 my %converter = SCORES;
205 127833         157841 my $score = shift;
206 127833 100       249757 if ( defined $score ) { $self->{score} = $score; }
  160         405  
207 127833         247736 my $scores = $self->scores;
208             return $self->{score} unless defined $scores and
209 127833 100 100 146002   655344 all { defined $_ } values %$scores;
  146002         745645  
210 23462         74280 my %lcconverter = map { lc($_) => $converter{$_} } keys %converter;
  126580         294116  
211 23462         69676 my %scores = map { $_ => lc $scores->{$_} } keys %$scores;
  58623         138996  
212 23462         53962 for my $round ( keys %scores ) {
213             die
214             "Round $round $scores->{$round}, $scores{$round} score unconvertible to $lcconverter{$scores{$round}} for player $self->{id}"
215 58623 50 66     267050 unless defined( $scores{$round} and $lcconverter{ $scores{$round} } );
216             }
217 23462         41717 my @values = map { $lcconverter{$_} } values %scores;
  58623         105077  
218 23462         50933 my $sum = sum(@values);
219 23462 50       150023 return $sum if defined $sum;
220 0         0 return undef;
221             }
222              
223              
224             =head2 met
225              
226             $rounds = $deepblue->met(@grandmasters)
227             next if $deepblue->met($capablanca)
228              
229             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!
230              
231             =cut
232              
233             sub met {
234 0     0 1 0 my $self = shift;
235 0         0 my @opponents = @_;
236 0         0 my $games = $self->play;
237 0         0 my @rounds = keys %$games;
238 0         0 my @ids = map { $_->id } @opponents;
  0         0  
239 0         0 my %gameAgainst;
240 0         0 @gameAgainst{@ids} = ('') x @ids;
241 0         0 for my $round ( @rounds )
242             {
243 0         0 my $gameInRound = $games->{$round};
244 0 0       0 next unless UNIVERSAL::isa $gameInRound, 'Games::Tournament::Card';
245 0         0 my $opponent = $self->myOpponent($gameInRound);
246 0         0 my $opponentId = $opponent->id;
247 0         0 $gameAgainst{$opponentId} = $gameInRound;
248             }
249 0 0       0 carp $self->id . " played @ids? Where are the cards?" unless %gameAgainst;
250 0         0 return \%gameAgainst;
251             }
252              
253              
254             =head2 name
255              
256             $member->name('Alexander Alekhine');
257             $member->name
258              
259             Sets or gets the name of the contesting individual or team, a string that may or may not be unique to the tournament member.
260              
261             =cut
262              
263             sub name {
264 2856     2856 1 3557 my $self = shift;
265 2856         3526 my $name = shift;
266 2856 50       8272 if ( defined $name ) { $self->{name} = $name; }
  0 50       0  
267 2856         9729 elsif ( exists $self->{name} ) { return $self->{name}; }
268             }
269              
270              
271             =head2 title
272              
273             $member->title('Grandmaster')
274              
275             Sets/gets the title of the contestant, a courtesy given to the contestant.
276              
277             =cut
278              
279             sub title {
280 1022     1022 1 1212 my $self = shift;
281 1022         1100 my $title = shift;
282 1022 50       3112 if ( defined $title ) { $self->{title} = $title; }
  0 100       0  
283 974         3734 elsif ( exists $self->{title} ) { return $self->{title}; }
284             }
285              
286              
287             =head2 scores
288              
289             $member->scores
290              
291             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.
292              
293             =cut
294              
295             sub scores {
296 129129     129129 1 164781 my $self = shift;
297 129129   66     311406 my $scores = shift() || $self->{scores};
298 129129         182006 $self->{scores} = $scores;
299 129129         210676 return $scores;
300             }
301              
302              
303             =head2 rating
304              
305             $member->rating
306              
307             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.
308              
309             =cut
310              
311             sub rating {
312 54022     54022 1 68711 my $self = shift;
313 54022         66142 my $rating = shift;
314 54022 50 33     171902 if ( defined $rating and $rating =~ m/^\d$/ ) { $self->{rating} = $rating; }
  0 100       0  
315 52141         193259 elsif ( exists $self->{rating} ) { return $self->{rating}; }
316 1881         6262 else { return 0; }
317             }
318              
319              
320             =head2 play
321              
322             $games = $member->play;
323             $games = $member->play( { $lastround => $game } );
324              
325             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.
326              
327             =cut
328              
329             sub play {
330 0     0 1 0 my $self = shift;
331 0         0 my $play = shift;
332 0 0       0 if ( defined $play ) {
    0          
333 0         0 my @rounds = keys %$play;
334 0         0 for my $round ( @rounds ) {
335 0         0 $self->{play}->{$round} = $play->{$round};
336             }
337             }
338 0         0 elsif ( $self->{play} ) { return $self->{play}; }
339             }
340              
341              
342             =head2 id
343              
344             $member->id
345              
346             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.
347              
348             =cut
349              
350             sub id {
351 189806     189806 1 572651 my $self = shift;
352 189806         222061 my $id = shift;
353 189806 50       526154 if ( defined $id ) { $self->{id} = $id; }
  0 50       0  
354 189806         541564 elsif ( exists $self->{id} ) { return $self->{id}; }
355             }
356              
357             =head2 firstround
358              
359             $member->firstround
360              
361             Returns/sets the firstround of the contestant, the round in which they first played or will play. Necessary for handling late entrants.
362              
363             =cut
364              
365             sub firstround {
366 4210     4210 1 5464 my $self = shift;
367 4210         5093 my $firstround = shift;
368 4210 100       15529 if ( defined $firstround ) { $self->{firstround} = $firstround; }
  1744 100       3664  
369 722         2455 elsif ( exists $self->{firstround} ) { return $self->{firstround}; }
370             }
371              
372              
373             =head2 absent
374              
375             $member->absent(1)
376             puah @absent if $member->absent
377              
378             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.
379              
380             =cut
381              
382             sub absent {
383 0     0 1   my $self = shift;
384 0           my $absent = shift;
385 0 0         if ( $absent ) { $self->{absent} = 1; return }
  0 0          
  0            
386 0           elsif ( defined $self->{absent} ) { return $self->{absent}; }
387 0           else { return; }
388             }
389              
390              
391             =head1 AUTHOR
392              
393             Dr Bean, C<< >>
394              
395             =head1 BUGS
396              
397             Please report any bugs or feature requests to
398             C, or through the web interface at
399             L.
400             I will be notified, and then you'll automatically be notified of progress on
401             your bug as I make changes.
402              
403             =head1 SUPPORT
404              
405             You can find documentation for this module with the perldoc command.
406              
407             perldoc Games::Tournament::Contestant
408              
409             You can also look for information at:
410              
411             =over 4
412              
413             =item * AnnoCPAN: Annotated CPAN documentation
414              
415             L
416              
417             =item * CPAN Ratings
418              
419             L
420              
421             =item * RT: CPAN's request tracker
422              
423             L
424              
425             =item * Search CPAN
426              
427             L
428              
429             =back
430              
431             =head1 ACKNOWLEDGEMENTS
432              
433             =head1 COPYRIGHT & LICENSE
434              
435             Copyright 2006 Dr Bean, all rights reserved.
436              
437             This program is free software; you can redistribute it and/or modify it
438             under the same terms as Perl itself.
439              
440             =cut
441              
442             1; # End of Games::Tournament::Contestant
443              
444             # vim: set ts=8 sts=4 sw=4 noet: