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 12 12 100.0
total 253 267 94.7


line stmt bran cond sub pod time code
1             package Game::Cribbage::Player::Hand;
2              
3 11     11   349667 use strict;
  11         18  
  11         339  
4 11     11   35 use warnings;
  11         16  
  11         506  
5              
6 11     11   1719 use Object::Proto::Sugar -types;
  11         47286  
  11         55  
7 11     11   8879 use List::Util qw/first/;
  11         23  
  11         751  
8 11     11   4625 use Game::Cribbage::Score;
  11         110  
  11         489  
9 11     11   2009 use Game::Cribbage::Deck::Card;
  11         51  
  11         318  
10 11     11   1490 use Game::Cribbage::Error;
  11         44  
  11         348  
11 11     11   47 use ntheory qw/forcomb vecsum/;
  11         14  
  11         41  
12 11     11   4803 use Array::Diff;
  11         107044  
  11         56  
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 1 962 my ($self, $card_index) = @_;
33 38 100       101 my $card = ref $card_index ? $self->match($card_index) : $self->cards->[$card_index];
34 38 100       99 if (!$card) {
35 1         10 die 'NO CARD FOUND FOR CARD_INDEX ' . $card_index;
36             }
37 37         57 return $card;
38             };
39              
40             sub match {
41 3     3 1 4 my ($self, $card) = @_;
42 3         4 for (@{ $self->cards }) {
  3         7  
43 4         7 my $found = $_->match($card);
44 4 100       7 if ($found) {
45 2         5 return $_;
46             }
47             }
48 1         2 return 0;
49             };
50              
51             sub add {
52 473     473 1 573 my ($self, $card) = @_;
53 473         424 push @{$self->cards}, $card;
  473         972  
54             };
55              
56             sub add_by_index {
57 2     2 1 7 my ($self, $index, $card) = @_;
58 2         7 $self->cards->[$index] = $card;
59 2         5 return 1;
60             };
61              
62             sub discard_cards {
63 2     2 1 5 my ($self, $cards, $crib) = @_;
64 2         3 my $count = scalar @{$self->cards};
  2         4  
65 2 100       7 if ($count <= 4) {
66 1         8 die 'CANNOT DISCARD ANY MORE CARDS';
67             }
68 1         1 my %mapped = ();
69 1         2 for (@{$cards}) {
  1         2  
70 2         7 $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         11  
75 6         16 my $card = $cards->[$i];
76 6 100 100     21 if (exists $mapped{$card->suit} && exists $mapped{$card->suit}{$card->symbol}) {
77 2         3 push @{$crib->crib}, splice(@{$cards}, $i, 1);
  2         4  
  2         4  
78 2         2 push @cribbed, $card;
79 2         3 $i--;
80             }
81             }
82 1         4 $self->cards($cards);
83 1         3 return \@cribbed;
84             };
85              
86             sub discard {
87 124     124 1 826 my ($self, $card, $crib) = @_;
88 124         114 my $count = scalar @{$self->cards};
  124         164  
89 124 100       188 if ($count <= 4) {
90 1         7 die 'CANNOT DISCARD ANY MORE CARDS';
91             }
92 123 100       236 $card = $self->cards->[$card] if (!ref $card);
93 123         180 my $str = $card->stringify;
94 123     427   448 my $ind = first { $self->cards->[$_]->stringify eq $str } 0 .. $count - 1;
  427         713  
95 123         260 splice @{$self->cards}, $ind, 1;
  123         177  
96 123         156 $crib->add_crib_card($card);
97             };
98              
99             sub add_crib_card {
100 132     132 1 159 my ($self, $card) = @_;
101 132         148 push @{$self->crib}, $card;
  132         324  
102             };
103              
104             sub calculate_score {
105 14     14 1 27 my ($self) = @_;
106 14 100       42 my $starter = $self->starter ? 1 : 0;
107 14 100       19 my @cards = (@{$self->cards}, ($starter ? $self->starter : ()));
  14         74  
108 14         152 $self->score(Game::Cribbage::Score->new(with_starter => $starter, cards => \@cards));
109 14 100 50     46 if ($self->crib && scalar @{$self->crib}) {
  14         45  
110 4 50       6 @cards = (@{$self->crib}, ($starter ? $self->starter : ()));
  4         20  
111 4         41 $self->crib_score(Game::Cribbage::Score->new(with_starter => $starter, cards => \@cards));
112             }
113 14 100       83 return $self->score->total_score + ($self->crib_score ? $self->crib_score->total_score : 0);
114             }
115              
116             sub card_exists {
117 3     3 1 5 my ($self, $card) = @_;
118              
119 3         3 for (@{ $self->cards }) {
  3         13  
120 3         8 my $found = $_->match($card);
121 3 100       7 if ($found) {
122 1         4 return 1;
123             }
124             }
125            
126 2 50       7 if ($self->crib) {
127 2         3 for (@{ $self->crib }) {
  2         3  
128 2         3 my $found = $_->match($card);
129 2 100       5 if ($found) {
130 1         6 return 1;
131             }
132             }
133             }
134            
135 1         3 return 0;
136             };
137              
138             sub identify_worst_cards {
139 2     2 1 14 my ($self) = @_;
140              
141 2 100       4 if (!(scalar @{$self->cards} == 6)) {
  2         6  
142 1         9 die 'cards do not exists or two have been moved to the crib already';
143             }
144              
145 1         39 my @index = 0 .. 5;
146 1         2 my @cards = @{$self->cards};
  1         3  
147 1         4 my %best = (
148             score => 0,
149             cards => []
150             );
151             forcomb {
152 15     15   23 my @test = @cards[@_];
153 15         76 my $score = Game::Cribbage::Score->new(with_starter => 0, cards => \@test);
154 15 100       59 if (($score->total_score + 0) > $best{score}) {
155 2         3 $best{score} = $score->total_score;
156 2         10 $best{cards} = [@_];
157             }
158 1         9 } @index, 4;
159              
160 1         11 my $diff = Array::Diff->diff( \@index, $best{cards} );
161 1         420 @index = @{ $diff->deleted };
  1         21  
162 1         6 @cards = map { $self->get($_) } @index;
  2         5  
163 1         10 return (\@cards, @index);
164             };
165              
166             sub validate_crib_cards {
167 3     3 1 7 my ($self, $cards) = @_;
168              
169 3         5 my $find_all = 1;
170 3         3 for my $card (@{$cards}) {
  3         8  
171 5         5 my $found = 0;
172 5         7 for (@{ $self->crib }) {
  5         9  
173 13 100       21 if ( $_->match($card) ) {
174 4         8 $found = 1;
175             }
176             }
177 5 100       13 if (! $found) {
178 1         1 $find_all = 0;
179 1         2 last;
180             }
181             }
182              
183 3 100       8 if (!$find_all) {
184 1         4 $self->crib([]);
185 1         31 for (@{$cards}) {
  1         3  
186 2         1 push @{$self->crib}, Game::Cribbage::Deck::Card->new(
  2         10  
187             suit => $_->suit, symbol => $_->symbol
188             );
189             }
190             }
191              
192 3         8 return 1;
193             };
194              
195             sub best_run_play {
196 4     4 1 35 my ($self, $play) = @_;
197              
198 4         6 my ($best, $card);
199              
200 4         7 my @available = grep { ! $_->used } @{$self->cards};
  14         29  
  4         10  
201              
202 4         8 for (@available) {
203 12         32 my $test = $play->test_card($self->player, $_);
204 12 50 33     37 if (! ref $test && (!$best || $best < $test)) {
      33        
205 12         13 $best = $test;
206 12         17 $card = $_;
207             }
208             }
209              
210 4 100       11 if (! defined $best) {
211 1         12 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       8 if ($best == 0) {
218 3         8 my $total = $play->total;
219 3         6 @available = grep { ($total + $_->value) <= 31 } @available;
  12         19  
220 3 50 33     11 @available = grep { $_->value != 5 } @available if scalar @available > 1 && grep { $_->value != 5 } @available;
  12         17  
  12         18  
221 3 50       13 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         53 $card = $available[int(rand(scalar @available - 1))];
228             }
229              
230 3         9 return $card;
231             }
232              
233             1;
234              
235             __END__