File Coverage

blib/lib/Game/Cribbage/Player/Hand.pm
Criterion Covered Total %
statement 167 168 99.4
branch 44 50 88.0
condition 7 14 50.0
subroutine 23 23 100.0
pod 0 12 0.0
total 241 267 90.2


line stmt bran cond sub pod time code
1             package Game::Cribbage::Player::Hand;
2              
3 11     11   340145 use strict;
  11         16  
  11         327  
4 11     11   35 use warnings;
  11         14  
  11         516  
5              
6 11     11   1814 use Object::Proto::Sugar -types;
  11         50196  
  11         66  
7 11     11   9086 use List::Util qw/first/;
  11         26  
  11         731  
8 11     11   4227 use Game::Cribbage::Score;
  11         125  
  11         502  
9 11     11   2038 use Game::Cribbage::Deck::Card;
  11         52  
  11         359  
10 11     11   1426 use Game::Cribbage::Error;
  11         44  
  11         320  
11 11     11   42 use ntheory qw/forcomb vecsum/;
  11         12  
  11         39  
12 11     11   4766 use Array::Diff;
  11         102960  
  11         53  
13              
14             has [qw/id/] => (
15             is => 'rw',
16             isa => Int
17             );
18              
19             has [qw/player score crib_score starter/] => (
20             is => 'rw',
21             isa => Object
22             );
23              
24              
25             has [qw/crib cards play_scored/] => (
26             is => 'rw',
27             isa => 'ArrayRef',
28             default => [],
29             );
30              
31             sub get {
32 38     38 0 887 my ($self, $card_index) = @_;
33 38 100       93 my $card = ref $card_index ? $self->match($card_index) : $self->cards->[$card_index];
34 38 100       86 if (!$card) {
35 1         9 die 'NO CARD FOUND FOR CARD_INDEX ' . $card_index;
36             }
37 37         57 return $card;
38             };
39              
40             sub match {
41 3     3 0 6 my ($self, $card) = @_;
42 3         3 for (@{ $self->cards }) {
  3         7  
43 4         8 my $found = $_->match($card);
44 4 100       9 if ($found) {
45 2         5 return $_;
46             }
47             }
48 1         2 return 0;
49             };
50              
51             sub add {
52 473     473 0 514 my ($self, $card) = @_;
53 473         443 push @{$self->cards}, $card;
  473         897  
54             };
55              
56             sub add_by_index {
57 2     2 0 8 my ($self, $index, $card) = @_;
58 2         37 $self->cards->[$index] = $card;
59 2         6 return 1;
60             };
61              
62             sub discard_cards {
63 2     2 0 5 my ($self, $cards, $crib) = @_;
64 2         2 my $count = scalar @{$self->cards};
  2         5  
65 2 100       6 if ($count <= 4) {
66 1         7 die 'CANNOT DISCARD ANY MORE CARDS';
67             }
68 1         2 my %mapped = ();
69 1         2 for (@{$cards}) {
  1         1  
70 2         8 $mapped{$_->suit}{$_->symbol} = 1;
71             }
72 1         2 my @cribbed;
73 1         2 $cards = $self->cards;
74 1         2 for (my $i = 0; $i < scalar @{$cards}; $i++) {
  7         20  
75 6         6 my $card = $cards->[$i];
76 6 100 100     41 if (exists $mapped{$card->suit} && exists $mapped{$card->suit}{$card->symbol}) {
77 2         2 push @{$crib->crib}, splice(@{$cards}, $i, 1);
  2         3  
  2         5  
78 2         3 push @cribbed, $card;
79 2         3 $i--;
80             }
81             }
82 1         4 $self->cards($cards);
83 1         4 return \@cribbed;
84             };
85              
86             sub discard {
87 124     124 0 846 my ($self, $card, $crib) = @_;
88 124         120 my $count = scalar @{$self->cards};
  124         212  
89 124 100       183 if ($count <= 4) {
90 1         6 die 'CANNOT DISCARD ANY MORE CARDS';
91             }
92 123 100       219 $card = $self->cards->[$card] if (!ref $card);
93 123         180 my $str = $card->stringify;
94 123     427   435 my $ind = first { $self->cards->[$_]->stringify eq $str } 0 .. $count - 1;
  427         657  
95 123         260 splice @{$self->cards}, $ind, 1;
  123         229  
96 123         155 $crib->add_crib_card($card);
97             };
98              
99             sub add_crib_card {
100 132     132 0 166 my ($self, $card) = @_;
101 132         136 push @{$self->crib}, $card;
  132         336  
102             };
103              
104             sub calculate_score {
105 14     14 0 52 my ($self) = @_;
106 14 100       44 my $starter = $self->starter ? 1 : 0;
107 14 100       17 my @cards = (@{$self->cards}, ($starter ? $self->starter : ()));
  14         62  
108 14         135 $self->score(Game::Cribbage::Score->new(with_starter => $starter, cards => \@cards));
109 14 100 50     51 if ($self->crib && scalar @{$self->crib}) {
  14         49  
110 4 50       6 @cards = (@{$self->crib}, ($starter ? $self->starter : ()));
  4         18  
111 4         24 $self->crib_score(Game::Cribbage::Score->new(with_starter => $starter, cards => \@cards));
112             }
113 14 100       84 return $self->score->total_score + ($self->crib_score ? $self->crib_score->total_score : 0);
114             }
115              
116             sub card_exists {
117 3     3 0 5 my ($self, $card) = @_;
118              
119 3         2 for (@{ $self->cards }) {
  3         9  
120 3         10 my $found = $_->match($card);
121 3 100       18 if ($found) {
122 1         3 return 1;
123             }
124             }
125            
126 2 50       6 if ($self->crib) {
127 2         3 for (@{ $self->crib }) {
  2         4  
128 2         3 my $found = $_->match($card);
129 2 100       5 if ($found) {
130 1         3 return 1;
131             }
132             }
133             }
134            
135 1         3 return 0;
136             };
137              
138             sub identify_worst_cards {
139 2     2 0 15 my ($self) = @_;
140              
141 2 100       2 if (!(scalar @{$self->cards} == 6)) {
  2         6  
142 1         7 die 'cards do not exists or two have been moved to the crib already';
143             }
144              
145 1         29 my @index = 0 .. 5;
146 1         1 my @cards = @{$self->cards};
  1         3  
147 1         3 my %best = (
148             score => 0,
149             cards => []
150             );
151             forcomb {
152 15     15   24 my @test = @cards[@_];
153 15         54 my $score = Game::Cribbage::Score->new(with_starter => 0, cards => \@test);
154 15 100       57 if (($score->total_score + 0) > $best{score}) {
155 2         5 $best{score} = $score->total_score;
156 2         9 $best{cards} = [@_];
157             }
158 1         8 } @index, 4;
159              
160 1         11 my $diff = Array::Diff->diff( \@index, $best{cards} );
161 1         389 @index = @{ $diff->deleted };
  1         18  
162 1         6 @cards = map { $self->get($_) } @index;
  2         3  
163 1         9 return (\@cards, @index);
164             };
165              
166             sub validate_crib_cards {
167 3     3 0 27 my ($self, $cards) = @_;
168              
169 3         4 my $find_all = 1;
170 3         4 for my $card (@{$cards}) {
  3         7  
171 5         7 my $found = 0;
172 5         6 for (@{ $self->crib }) {
  5         21  
173 13 100       22 if ( $_->match($card) ) {
174 4         5 $found = 1;
175             }
176             }
177 5 100       12 if (! $found) {
178 1         2 $find_all = 0;
179 1         2 last;
180             }
181             }
182              
183 3 100       5 if (!$find_all) {
184 1         4 $self->crib([]);
185 1         1 for (@{$cards}) {
  1         2  
186 2         32 push @{$self->crib}, Game::Cribbage::Deck::Card->new(
  2         12  
187             suit => $_->suit, symbol => $_->symbol
188             );
189             }
190             }
191              
192 3         7 return 1;
193             };
194              
195             sub best_run_play {
196 4     4 0 32 my ($self, $play) = @_;
197              
198 4         7 my ($best, $card);
199              
200 4         6 my @available = grep { ! $_->used } @{$self->cards};
  14         29  
  4         10  
201              
202 4         8 for (@available) {
203 12         115 my $test = $play->test_card($self->player, $_);
204 12 50 33     45 if (! ref $test && (!$best || $best < $test)) {
      33        
205 12         12 $best = $test;
206 12         15 $card = $_;
207             }
208             }
209              
210 4 100       11 if (! defined $best) {
211 1         10 return Game::Cribbage::Error->new(
212             message => 'No valid cards left to play for this run',
213             go => 1
214             );
215             }
216              
217 3 50       6 if ($best == 0) {
218 3         7 my $total = $play->total;
219 3         6 @available = grep { ($total + $_->value) <= 31 } @available;
  12         17  
220 3 50 33     12 @available = grep { $_->value != 5 } @available if scalar @available > 1 && grep { $_->value != 5 } @available;
  12         15  
  12         15  
221 3 50       6 if (! scalar @available) {
222 0         0 return Game::Cribbage::Error->new(
223             message => 'No valid cards left to play for this run',
224             go => 1
225             );
226             }
227 3         52 $card = $available[int(rand(scalar @available - 1))];
228             }
229              
230 3         12 return $card;
231             }
232              
233             1;