File Coverage

blib/lib/Game/Cribbage/Player/Hand.pm
Criterion Covered Total %
statement 30 30 100.0
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 40 40 100.0


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