File Coverage

blib/lib/Games/Go/AGA/DataObjects/Round.pm
Criterion Covered Total %
statement 40 179 22.3
branch 2 70 2.8
condition 0 19 0.0
subroutine 12 21 57.1
pod 10 12 83.3
total 64 301 21.2


line stmt bran cond sub pod time code
1             #===============================================================================
2             #
3             # FILE: Games::Go::AGA::DataObjects::Round.pm
4             #
5             # USAGE: use Games::Go::AGA::DataObjects::Round;
6             #
7             # PODNAME: Games::Go::AGA::DataObjects::Round
8             # ABSTRACT: model a round of an AGA tournament
9             #
10             # AUTHOR: Reid Augustin (REID),
11             # COMPANY: LucidPort Technology, Inc.
12             # CREATED: 11/19/2010 03:13:05 PM PST
13             #===============================================================================
14              
15 1     1   1015 use strict;
  1         1  
  1         39  
16 1     1   4 use warnings;
  1         1  
  1         35  
17              
18             package Games::Go::AGA::DataObjects::Round;
19              
20 1     1   4 use Mouse;
  1         1  
  1         5  
21 1     1   276 use Carp;
  1         1  
  1         60  
22 1     1   4 use Scalar::Util qw( refaddr looks_like_number );
  1         1  
  1         44  
23 1     1   5 use Games::Go::AGA::DataObjects::Game;
  1         0  
  1         18  
24 1     1   4 use Games::Go::AGA::DataObjects::Player;
  1         1  
  1         1320  
25              
26             our $VERSION = '0.107'; # VERSION
27              
28             # has 'games' => (
29             # isa => 'ArrayRef[Games::Go::AGA::DataObjects::Game]',
30             # is => 'rw',
31             # default => sub { [] },
32             # );
33             # has 'byes' => (
34             # isa => 'ArrayRef[Games::Go::AGA::DataObjects::Player]',
35             # is => 'rw',
36             # default => sub { [] },
37             # );
38             has 'table_number' => ( # assign table numbers from how many tables used so far
39             isa => 'Int',
40             is => 'ro',
41             default => 0,
42             );
43             has 'change_callback' => (
44             isa => 'Maybe[CodeRef]',
45             is => 'rw',
46             default => sub { sub { carp 'no Round change_callback' } }
47             );
48              
49             sub BUILD {
50 1     1 1 350 my ($self) = @_;
51 1         6 $self->{games} = [];
52 1         3 $self->{byes} = [];
53             }
54              
55             sub changed {
56 3     3 0 4 my ($self) = @_;
57 3 50       7 &{$self->change_callback}($self) if ($self->{change_callback});
  3         12  
58             }
59              
60             sub games {
61 19     19 1 613 my ($self) = @_;
62              
63             return wantarray
64 19 50       148 ? @{$self->{games}}
  0         0  
65             : $self->{games};
66             }
67              
68             sub byes {
69 0     0 1 0 my ($self) = @_;
70              
71             return wantarray
72 0 0       0 ? @{$self->{byes}}
  0         0  
73             : $self->{byes};
74             }
75              
76             sub add_game {
77 3     3 1 897 my ($self, $game) = @_;
78              
79             # TODO: check for duplicate IDs?
80 3         3 push (@{$self->{games}}, $game);
  3         7  
81 3         12 $game->white->add_game($game); # add game to players' lists
82 3         8 $game->black->add_game($game);
83 3         24 $game->table_number(++$self->{table_number});
84 3         8 $self->changed;
85 3         7 return $self;
86             }
87              
88             sub clear_table_number {
89 0     0 1   my ($self) = @_;
90              
91 0           $self->{table_number} = 0;
92             }
93              
94             sub remove_game {
95 0     0 1   my ($self, $game) = @_;
96              
97 0           my $games = $self->{games};
98 0 0         if (ref $game) {
99 0           my $raddr = refaddr($game);
100 0           for (my $idx = 0; $idx < @{$games}; $idx++) {
  0            
101 0 0         if (refaddr($games->[$idx]) == $raddr) {
102 0           $game = $idx;
103 0           last;
104             }
105             }
106 0 0         if (ref $game) {
107 0           croak "Game not found";
108             }
109             }
110 0           $game = splice @{$games}, $game, 1; # remove from our list
  0            
111 0           $game->white->delete_game($game); # remove game from players' lists
112 0           $game->black->delete_game($game);
113 0           $self->add_bye($game->white);
114 0           $self->add_bye($game->black);
115             # $self->changed; # add_bye already calls this
116 0           return $game;
117             }
118              
119             sub add_bye {
120 0     0 1   my ($self, $player) = @_;
121              
122             # check for duplicate IDs
123 0 0         return $self if (grep { $_->id eq $player->id } @{$self->{byes}});
  0            
  0            
124 0           push (@{$self->{byes}}, $player);
  0            
125 0           $self->changed;
126 0           return $self;
127             }
128              
129             sub remove_bye {
130 0     0 1   my ($self, $player) = @_;
131              
132 0           my $idx = $self->_find_bye_idx($player); # convert to index
133 0           my $removed = splice @{$self->{byes}}, $idx, 1;
  0            
134 0           $self->changed;
135 0           return $removed;
136             }
137              
138             sub replace_bye {
139 0     0 1   my ($self, $old_bye, $new_bye) = @_;
140              
141 0           my $idx = $self->_find_bye_idx($old_bye); # convert to index
142 0           my $removed = $self->{byes}[$idx];
143 0           $self->{byes}[$idx] = $new_bye;
144 0           $self->changed;
145 0           return $removed;
146             }
147              
148             sub swap {
149 0     0 1   my ($self, $id_0, $id_1) = @_;
150              
151 0           my ($p0, $p1, $opp0, $opp1, $item0, $item1);
152              
153 0           for my $player (@{$self->{byes}}) {
  0            
154 0 0         if ($player->id eq $id_0) {
155 0           $item0 = $player;
156 0           $p0 = $player;
157             }
158 0 0         if ($player->id eq $id_1) {
159 0           $item1 = $player;
160 0           $p1 = $player;
161             }
162             }
163              
164 0           for my $game (@{$self->{games}}) {
  0            
165 0 0         if ($game->white->id eq $id_0) {
    0          
166 0           $item0 = $game;
167 0           $p0 = $game->white;
168 0           $opp0 = $game->black;
169             }
170             elsif ($game->black->id eq $id_0) {
171 0           $item0 = $game;
172 0           $p0 = $game->black;
173 0           $opp0 = $game->white;
174             }
175 0 0         if ($game->white->id eq $id_1) {
    0          
176 0           $item1 = $game;
177 0           $p1 = $game->white;
178 0           $opp1 = $game->black;
179             }
180             elsif ($game->black->id eq $id_1) {
181 0           $item1 = $game;
182 0           $p1 = $game->black;
183 0           $opp1 = $game->white;
184             }
185 0 0 0       last if (defined $item0 and defined $item1);
186             };
187 0 0         if (not defined $item0) {
188 0           croak "ID $id_0 not found in games or Byes lists\n";
189             }
190 0 0         if (not defined $item1) {
191 0           croak "ID $id_1 not found in games or Byes lists\n";
192             }
193             # no-op if both are Player IDs from Byes list
194 0 0 0       return if ($item0->can('id') and $item1->can('id'));
195 0 0 0       if ($item0->can('white') and $item1->can('white')) {
    0          
    0          
196             # both items are Games.
197 0 0 0       if ($item0->white->id eq $item1->white->id and
198             $item0->black->id eq $item1->black->id) { # same game?
199 0           $item0->swap; # just swap black and white players
200             }
201             else {
202             # remove game from both players
203 0           $p0->delete_game($p0->id, $opp0->id);
204 0           $p1->delete_game($p1->id, $opp1->id);
205             # swap players between two games
206 0 0         if ($p0->id eq $item0->white->id) {
207 0           $item0->white($p1);
208             }
209             else {
210 0           $item0->black($p1);
211             }
212 0 0         if ($p1->id eq $item1->white->id) {
213 0           $item1->white($p0);
214             }
215             else {
216 0           $item1->black($p0);
217             }
218             # add game back to player game lists, but swapped
219 0           $p0->add_game($item1);
220 0           $p1->add_game($item0);
221             }
222             }
223             elsif ($item0->can('id')) {
224             # first item is a Bye Player, second is a Game
225 0           $p1->delete_game($p1->id, $opp1->id);
226             # swap players between game and Byes list
227 0 0         if ($p1->id eq $item1->white->id) {
228 0           $item1->white($p0);
229             }
230             else {
231 0           $item1->black($p0);
232             }
233             # add game back to player list
234 0           $p1->add_game($item1);
235 0           $self->replace_bye($p0, $p1);
236             }
237             elsif ($item1->can('id')) {
238             # first item is a Game, second is a Bye Player
239 0           $p0->delete_game($p0->id, $opp0->id);
240             # swap players between game and Byes list
241 0 0         if ($p0->id eq $item0->white->id) {
242 0           $item0->white($p1);
243             }
244             else {
245 0           $item0->black($p1);
246             }
247             # add game back to player list
248 0           $p1->add_game($item0);
249 0           $self->replace_bye($p1, $p0);
250             }
251 0 0         $item0->handicap if ($item0->can('handicap'));
252 0 0         $item1->handicap if ($item1->can('handicap'));
253             }
254              
255             # find player in BYEs list
256             sub _find_bye_idx {
257 0     0     my ($self, $idx) = @_;
258              
259 0           my $players = $self->{byes};
260 0 0         if (looks_like_number($idx)) {
    0          
261             # already what we need
262             }
263             elsif (ref $idx) { # must be a Player dataobject
264             # find Player object with matching ID
265 0           FIND_REFADDR : {
266 0           my $player = $idx;
267 0           my $id = $player->id;
268 0           for my $ii (0 .. $#{$players}) {
  0            
269 0 0         if ($players->[$ii]->id eq $id) {
270 0           $idx = $ii;
271 0           last FIND_REFADDR;
272             }
273             }
274 0           croak "can't find BYE player with ID $id\n";
275             }
276             }
277             else {
278             # find Player with matching ID
279 0           FIND_ID : {
280 0           my $id = $idx;
281 0           for my $ii (0 .. $#{$players}) {
  0            
282 0 0         if ($players->[$ii]->id eq $id) {
283 0           $idx = $ii;
284 0           last FIND_ID;
285             }
286             }
287 0           croak "can't find player matching ID $id\n";
288             }
289             }
290 0 0 0       if ($idx < 0 or
  0            
291             $idx > $#{$players}) {
292 0           croak "index=$idx is out of bounds\n";
293             }
294 0           return $idx;
295             }
296              
297             sub fprint {
298 0     0 0   my ($self, $fh) = @_;
299              
300 0           foreach my $game (@{$self->{games}}) {
  0            
301 0           my $result = '?';
302 0           my $winner = $game->winner;
303 0 0         if ($winner) {
304 0 0         $result = 'w' if ($winner->id eq $game->white->id);
305 0 0         $result = 'b' if ($winner->id eq $game->black->id);
306             }
307 0   0       $fh->printf("%s %s %s %s %s # %s (%s->%s) vs (%s->%s) %s\n",
      0        
308             $game->white->id,
309             $game->black->id,
310             $result,
311             $game->handi,
312             $game->komi,
313             $game->white->full_name,
314             $game->white->rank,
315             $game->white->adj_rating || '?',
316             $game->black->rank,
317             $game->black->adj_rating || '?',
318             $game->black->full_name,
319             );
320             }
321 0           foreach my $bye (@{$self->{byes}}) {
  0            
322 0           $fh->printf("# BYE: %s %s, %s\n",
323             $bye->id,
324             $bye->last_name,
325             $bye->first_name,
326             );
327             }
328             }
329              
330 1     1   6 no Mouse;
  1         2  
  1         5  
331             __PACKAGE__->meta->make_immutable;
332              
333             1;
334              
335             __END__