File Coverage

blib/lib/Games/Poker/Omaha/Hutchison.pm
Criterion Covered Total %
statement 118 127 92.9
branch 34 38 89.4
condition 20 23 86.9
subroutine 33 35 94.2
pod 2 2 100.0
total 207 225 92.0


line stmt bran cond sub pod time code
1             package Games::Poker::Omaha::Hutchison;
2              
3             our $VERSION = '1.04';
4              
5 1     1   27163 use strict;
  1         2  
  1         36  
6 1     1   4 use warnings;
  1         2  
  1         27  
7              
8 1     1   5 use List::Util 'sum';
  1         5  
  1         145  
9              
10 1         6 use Class::Struct 'Games::Poker::Omaha::Hutchison::Card' =>
11 1     1   1013 [ suit => '$', pips => '$' ];
  1         1933  
12              
13             sub Games::Poker::Omaha::Hutchison::Card::rank {
14 0     0   0 return (qw/ 0 0 l l l l x h h h c c c c a /)[ shift->pips ];
15             }
16              
17             sub new {
18 15     15 1 5861 my $class = shift;
19 15 100 100     175 my @cardes = @_ > 1 ? @_ : split / /, +shift || die "Need a hand";
20 14         142 my @cards = map [ split // ], @cardes;
21 14         67 my %remap = (A => 14, K => 13, Q => 12, J => 11, T => 10);
22 14   66     169 $_->[0] = $remap{ $_->[0] } || $_->[0] foreach @cards;
23 14         474 bless {
24             cards => [
25             map Games::Poker::Omaha::Hutchison::Card->new(
26             pips => $_->[0],
27             suit => lc $_->[1]
28             ),
29             @cards
30             ]
31             } => $class;
32             }
33              
34 42     42   45 sub _cards { @{ shift->{cards} } }
  42         479  
35              
36             sub _by_suit {
37 14     14   21 my $self = shift;
38 14         14 my %suited;
39 62         2137 push @{ $suited{ $_->suit } }, $_->pips
  56         1892  
40 14         31 foreach sort { $b->pips <=> $a->pips } $self->_cards;
41 14         213 return %suited;
42             }
43              
44             sub _by_pips {
45 14     14   16 my $self = shift;
46 14         19 my %pips;
47 14         27 push @{ $pips{ $_->pips } }, $_->suit foreach $self->_cards;
  56         3647  
48 14         221 return %pips;
49             }
50              
51             sub _unique_pips {
52 0     0   0 my $self = shift;
53 0         0 my %seen;
54 0         0 my %part = map { $_ => [] } qw/l x h c a/;
  0         0  
55 0         0 my @uniq = grep !$seen{ $_->pips }++, $self->_cards;
56 0         0 push @{ $part{ $_->rank } }, $_->pips foreach @uniq;
  0         0  
57 0         0 return %part;
58             }
59              
60             sub hand_score {
61 14     14 1 5821 my $self = shift;
62 14         40 sum($self->flush_score, $self->pair_score, $self->straight_score);
63             }
64              
65             use Object::Attribute::Cached
66 1         11 flush_score => \&_flush_score,
67             pair_score => \&_pair_score,
68 1     1   2029 straight_score => \&_straight_score;
  1         248  
69              
70             sub _flush_score {
71 14     14   1375 my $self = shift;
72 14         30 my %suited = $self->_by_suit;
73 14         28 my $score = 0;
74 14         33 foreach my $suit (keys %suited) {
75 43         42 my @cards = @{ $suited{$suit} };
  43         91  
76 43 100       828 next unless @cards > 1;
77 13         30 $score += $self->_flush_pts($cards[0]);
78 13 50       45 $score -= 2 if @cards == 4;
79             }
80 14         70 $score;
81             }
82              
83 12     12   100 sub _pair_pts { (0, 0, 4, 4, 4, 4, 4, 4, 4, 5, 6, 6, 7, 8, 9)[ $_[1] ] }
84 13     13   28 sub _flush_pts { (0, 0, 1, 1, 1, 1, 1, 1, 1, 1.5, 1.5, 2, 2.5, 3, 4)[ $_[1] ] }
85              
86             sub _pair_score {
87 14     14   5283 my $self = shift;
88 14         35 my %pips = $self->_by_pips;
89 14 100       38 (sum map $self->_pair_pts($_), grep @{ $pips{$_} } == 2, keys %pips) || 0;
  44         231  
90             }
91              
92             sub _straight_score {
93 14     14   5446 my $self = shift;
94 14         20 my %seen;
95 14         35 my @run = grep !$seen{$_}++, map $_->pips, $self->_cards;
96 14         431 return Games::Poker::Omaha::Hutchison::StraightScorer->new(@run)->score;
97             }
98              
99             package Games::Poker::Omaha::Hutchison::StraightScorer;
100              
101 1     1   416 use List::Util qw/sum max/;
  1         2  
  1         920  
102              
103             sub new {
104 16     16   41 my ($proto, @cards) = @_;
105 16   66     66 my $class = ref $proto || $proto;
106 16         49 bless { cards => [ sort { $b <=> $a } @cards ] }, $class;
  47         185  
107             }
108              
109 134     134   137 sub cards { @{ shift->{cards} } }
  134         600  
110              
111             sub gap {
112 28     28   36 my $self = shift;
113 28         69 my @pips = sort { $b <=> $a } @_;
  55         110  
114 28         49 my $gap = ($pips[0] - $pips[-1]) - (@pips - 1);
115 28         112 return $gap;
116             }
117              
118             sub gaploss {
119 15     15   39 my ($self, @pips) = @_;
120 15         30 my $gap = $self->gap(@pips);
121 15         62 return (0, 1, 1, 2, (0) x 10)[$gap];
122             }
123              
124 26     26   43 sub ace { grep { $_ == 14 } shift->cards; }
  75         196  
125 9 100   9   19 sub court { grep { $_ > 9 and $_ < 14 } shift->cards; }
  22         104  
126 25 50   25   45 sub twosix { grep { $_ > 1 and $_ < 7 } shift->cards; }
  82         380  
127 7 50   7   13 sub twofive { grep { $_ > 1 and $_ < 6 } shift->cards; }
  21         106  
128 31     31   55 sub sixup { grep { $_ > 5 } shift->cards; }
  96         290  
129 17 100   17   28 sub sixking { grep { $_ > 5 and $_ < 14 } shift->cards; }
  48         234  
130              
131             sub score {
132 16     16   21 my $self = shift;
133 16         27 my @cards = $self->cards;
134              
135 16         45 my $score = $self->_four_high_cards;
136 16 100       129 return $score if $score;
137              
138 15         63 $score += $self->_ace_low;
139 15         41 $score += $self->_two_low_cards;
140              
141 15         46 $score += my $high3 = $self->_three_high_cards;
142 15 100       55 return $score if $high3;
143              
144 10   100     24 $score += $self->_two_high_cards || $self->_ace_court;
145 10         63 return $score;
146             }
147              
148             sub _two_low_cards {
149 15     15   30 my $self = shift;
150 15 100 66     30 return 2 - $self->gaploss($self->twosix)
151             if $self->twosix >= 2
152             and $self->gap($self->twosix) < 4;
153             }
154            
155             sub _two_high_cards {
156 10     10   11 my $self = shift;
157 10 100 100     21 return 4 - $self->gaploss($self->sixking)
158             if $self->sixking == 2
159             and $self->gap($self->sixking) < 4;
160             }
161              
162             sub _four_high_cards {
163 16     16   19 my $self = shift;
164 16 100       28 return 0 unless $self->sixup == 4;
165 2 100       8 return 0 if $self->gap($self->cards) > 3;
166 1         5 return 12 - $self->gaploss($self->cards);
167             }
168              
169             sub _three_high_cards {
170 15     15   19 my $self = shift;
171 15         27 my @cards = $self->sixup;
172 15 100       55 return 0 unless @cards >= 3;
173 5 100       18 return 7 - $self->gaploss(@cards) if @cards == 3;
174             # Want 3 from 4
175 1         3 my @hi = @cards; pop @hi;
  1         2  
176 1         5 my @lo = @cards; shift @lo;
  1         2  
177 1         3 return max($self->new(@hi)->score, $self->new(@lo)->score);
178             }
179              
180             sub _ace_court {
181 7     7   10 my $self = shift;
182 7 100 100     13 return 0 unless $self->ace and $self->court;
183 2 50       6 return 0 if $self->gap($self->ace, $self->court) > 3;
184 2         7 return 2 - $self->gaploss($self->ace, $self->court);
185             }
186              
187             sub _ace_low {
188 15     15   20 my $self = shift;
189 15 100 100     30 return ($self->ace and $self->twofive) ? 1 : 0
190             }
191              
192             __END__