|
17 | 17 |
|
18 | 18 | How many hands does Player 1 win?
|
19 | 19 |
|
20 |
| -Expected result: 376 |
21 |
| -
|
22 | 20 | =end pod
|
23 | 21 |
|
24 | 22 | enum Rank <
|
25 |
| - Two Three Four Five |
26 |
| - Six Seven Eight Nine |
27 |
| - Ten Jack Queen King Ace |
| 23 | + Two Three Four |
| 24 | + Six Seven Eight Nine |
| 25 | + Ten Jack Queen King Ace |
28 | 26 | >;
|
29 |
| - |
| 27 | + |
30 | 28 | enum Suit <
|
31 |
| - Hearts Diamonds Clubs Spades |
| 29 | + Hearts Diamonds Clubs Spades |
32 | 30 | >;
|
33 | 31 |
|
34 | 32 | enum Hand <
|
35 |
| - RoyalFlush StraightFlush FourOfKind |
36 |
| - FullHouse Flush Straight ThreeOfKind |
37 |
| - TwoPairs OnePair HighCard |
| 33 | + RoyalFlush StraightFlush FourOfKind |
| 34 | + FullHouse Flush Straight ThreeOfKind |
| 35 | + TwoPairs OnePair HighCard |
38 | 36 | >;
|
39 | 37 |
|
40 | 38 | multi counts(Positional $h) {
|
41 |
| - $h.Bag.Hash.invert |
| 39 | + bag($h).invert |
42 | 40 | }
|
43 | 41 |
|
44 | 42 | multi strigify(Hash $x) {
|
45 | 43 | join ' and ', do for $x.kv -> $k, $v {
|
46 |
| - "$k of $v" |
| 44 | + "$k of $v" |
47 | 45 | }
|
48 | 46 | }
|
49 | 47 |
|
|
52 | 50 | has Suit $.suit;
|
53 | 51 |
|
54 | 52 | method parse-rank(Str $r) returns Rank {
|
55 |
| - given $r { |
56 |
| - when /\d/ { Rank($r.Int - 2) } |
57 |
| - when /T/ { Ten } |
58 |
| - when /J/ { Jack } |
59 |
| - when /Q/ { Queen } |
60 |
| - when /K/ { King } |
61 |
| - when /A/ { Ace } |
62 |
| - } |
| 53 | + given $r { |
| 54 | + when /\d/ { Rank($r.Int - 2) } |
| 55 | + when /T/ { Ten } |
| 56 | + when /J/ { Jack } |
| 57 | + when /Q/ { Queen } |
| 58 | + when /K/ { King } |
| 59 | + when /A/ { Ace } |
| 60 | + } |
63 | 61 | }
|
64 | 62 | method parse-suit(Str $s) returns Suit {
|
65 |
| - given $s { |
66 |
| - when /H/ { Hearts } |
67 |
| - when /D/ { Diamonds } |
68 |
| - when /C/ { Clubs } |
69 |
| - when /S/ { Spades } |
70 |
| - } |
| 63 | + given $s { |
| 64 | + when /H/ { Hearts } |
| 65 | + when /D/ { Diamonds } |
| 66 | + when /C/ { Clubs } |
| 67 | + when /S/ { Spades } |
| 68 | + } |
71 | 69 | }
|
72 | 70 | multi method CALL-ME(Str $c where $c.chars == 2) {
|
73 |
| - my ($r, $s) = $c.comb; |
74 |
| - self.new(rank => Card.parse-rank($r), |
75 |
| - suit => Card.parse-suit($s)); |
| 71 | + my ($r, $s) = $c.comb; |
| 72 | + self.new(rank => Card.parse-rank($r), |
| 73 | + suit => Card.parse-suit($s)); |
76 | 74 | }
|
77 | 75 | multi method CALL-ME(Rank $v, Suit $s) {
|
78 |
| - self.new(rank => $v, suit => $s) |
| 76 | + self.new(rank => $v, suit => $s) |
79 | 77 | }
|
80 |
| - |
81 |
| - |
82 | 78 | }
|
83 | 79 |
|
84 |
| - |
85 |
| -multi infix:«<=>»(Card $a, Card $b) returns Order { |
| 80 | +multi infix:«<=>»(Card $a, Card $b) is export returns Order { |
86 | 81 | $a.rank <=> $b.rank
|
87 | 82 | }
|
88 | 83 |
|
89 | 84 |
|
90 | 85 | class Deal {
|
91 | 86 | subset Ranks where -> $r {
|
92 |
| - $r ~~ Rank || $r ~~ Array[Rank] |
| 87 | + $r ~~ Rank || $r ~~ Array[Rank] |
93 | 88 | };
|
94 | 89 |
|
95 | 90 | has Card @.cards;
|
96 |
| - has Ranks %.score; |
| 91 | + has Ranks %.score{Hand}; |
97 | 92 |
|
98 | 93 | method CALL-ME(Str $h) {
|
99 |
| - my $x = self.new( |
100 |
| - cards => map { Card($_) } , $h.split: /\s/ |
101 |
| - ); |
102 |
| - #warn $x!best-hand.perl; |
103 |
| - $x.score = $x!best-hand; |
104 |
| - $x; |
| 94 | + my $x = self.new( |
| 95 | + cards => map { Card($_) } , $h.split: /\s/ |
| 96 | + ); |
| 97 | + $x.score = $x!best-hand; |
| 98 | + $x; |
| 99 | + } |
| 100 | + method ACCEPTS(Hand $h) { |
| 101 | + so %.score{$h}; |
105 | 102 | }
|
106 |
| - |
107 | 103 | method !best-hand {
|
108 |
| - self!royal-flush // |
109 |
| - self!straight-flush // |
110 |
| - self!full-house // |
111 |
| - self!flush // |
112 |
| - self!straight // |
113 |
| - self!four-of-kind // |
114 |
| - self!three-of-kind // |
115 |
| - self!two-pairs // |
116 |
| - self!one-pair // |
117 |
| - self!high-card |
| 104 | + self!royal-flush |
| 105 | + // self!straight-flush |
| 106 | + // self!full-house |
| 107 | + // self!flush |
| 108 | + // self!straight |
| 109 | + // self!four-of-kind |
| 110 | + // self!three-of-kind |
| 111 | + // self!two-pairs |
| 112 | + // self!one-pair |
| 113 | + // self!high-card |
118 | 114 | }
|
119 | 115 |
|
120 |
| - |
121 | 116 | method !straight {
|
122 |
| - my @v = @.cards».rank.sort; |
123 |
| - if @v.join eq (@v.min ... @v.max).map({Rank($_)}).join { |
124 |
| - Straight => @v.max |
125 |
| - } |
| 117 | + my @v = @.cards».rank.sort; |
| 118 | + if @v eq (@v.min ... @v.max).map({Rank($_)}) { |
| 119 | + (Straight) => @v.max |
| 120 | + } |
126 | 121 | }
|
127 | 122 |
|
128 | 123 | method !flush {
|
129 |
| - if [~~] @.cards».suit { |
130 |
| - Flush => Array[Rank].new(|@.cards».rank); |
131 |
| - } |
| 124 | + if [~~] @.cards».suit { |
| 125 | + (Flush) => Array[Rank].new: @.cards».rank; |
| 126 | + } |
132 | 127 | }
|
133 | 128 |
|
134 | 129 | method !royal-flush {
|
135 |
| - if self!flush && self!straight && @.cards».rank.max ~~ Ace { |
136 |
| - RoyalFlush => Ace |
137 |
| - } |
| 130 | + if self!flush && self!straight && @.cards».rank.max ~~ Ace { |
| 131 | + (RoyalFlush) => Ace |
| 132 | + } |
138 | 133 | }
|
139 | 134 |
|
140 | 135 | method !straight-flush {
|
141 |
| - if self!straight && self!flush { |
142 |
| - StraightFlush => self!straight.value |
143 |
| - } |
| 136 | + if self!flush && my $s = self!straight { |
| 137 | + (StraightFlush) => $s.value |
| 138 | + } |
144 | 139 | };
|
145 | 140 | method !four-of-kind {
|
146 |
| - # Four cards of the same value. |
147 |
| - my @ranks = @.cards».rank; |
148 |
| - my @four = @ranks.&counts.grep(*.key == 4); |
149 |
| - if so @four { |
150 |
| - FourOfKind => my $x = EVAL(@four[0].value), |
151 |
| - HighCard => max grep { $_ !~~ $x }, @ranks |
152 |
| - } |
| 141 | + # Four cards of the same value. |
| 142 | + my @ranks = @.cards».rank; |
| 143 | + my @four = @ranks.&counts.grep(*.key == 4); |
| 144 | + if so @four { |
| 145 | + (FourOfKind) => my $x = @four[0].value, |
| 146 | + (HighCard) => max grep { $_ !~~ $x }, @ranks |
| 147 | + } |
153 | 148 | }
|
154 | 149 | method !full-house {
|
155 | 150 | # Three of a kind and a pair.
|
156 |
| - my %x = self!three-of-kind , self!one-pair; |
157 |
| - if %x<ThreeOfKind>.defined && %x<OnePair>.defined { |
158 |
| - FullHouse => Ace |
159 |
| - } |
| 151 | + my Ranks %x{Hand} = self!three-of-kind , self!one-pair; |
| 152 | + if %x{ThreeOfKind}.defined && %x{OnePair}.defined { |
| 153 | + (FullHouse) => Ace |
| 154 | + } |
160 | 155 | }
|
161 |
| - |
| 156 | + |
162 | 157 | method !three-of-kind {
|
163 |
| - my $rank = @.cards».rank.&counts.grep(*.key == 3)[0]; |
164 |
| - |
165 |
| - if $rank { |
166 |
| - my %h = ThreeOfKind => my $x = EVAL($rank.value); |
167 |
| - |
168 |
| - if my $one-pair = @.cards».rank.&counts.grep(*.key == 2)[0] { |
169 |
| - %h<OnePair> = EVAL($one-pair.value); |
170 |
| - } else { |
171 |
| - %h<HighCard> = max grep { $_ !~~ $x }, @.cards».rank; |
172 |
| - } |
173 |
| - %h; |
174 |
| - } |
| 158 | + my $rank = @.cards».rank.&counts.grep(*.key == 3)[0]; |
| 159 | + |
| 160 | + if $rank { |
| 161 | + my Ranks %h{Hand} = (ThreeOfKind) => my $x = $rank.value; |
| 162 | + |
| 163 | + if my $one-pair = @.cards».rank.&counts.grep(*.key == 2)[0] { |
| 164 | + %h{OnePair} = $one-pair.value; |
| 165 | + } |
| 166 | + else { |
| 167 | + %h{HighCard} = max grep { $_ !~~ $x }, @.cards».rank; |
| 168 | + } |
| 169 | + %h; |
| 170 | + } |
175 | 171 | }
|
176 | 172 |
|
177 | 173 | method !two-pairs {
|
178 |
| - my @pairs = @.cards»\ |
179 |
| - .rank.&counts\ |
180 |
| - .sort(*.key).grep(*.key == 2); |
181 |
| - if +@pairs == 2 { |
182 |
| - OnePair => my $x=@pairs.map({ EVAL $_.value }).min, |
183 |
| - TwoPairs => my $y=@pairs.map({ EVAL $_.value }).max, |
184 |
| - HighCard => max grep { $_ !~~ $x | $y },@.cards».rank; |
185 |
| - } |
| 174 | + my @pairs = @.cards»\ |
| 175 | + .rank.&counts\ |
| 176 | + .sort(*.key).grep(*.key == 2); |
| 177 | + if +@pairs == 2 { |
| 178 | + (OnePair) => my $x= @pairs».value.min, |
| 179 | + (TwoPairs) => my $y= @pairs».value.max, |
| 180 | + (HighCard) => max grep { $_ !~~ $x | $y },@.cards».rank; |
| 181 | + } |
186 | 182 |
|
187 | 183 | }
|
188 | 184 |
|
189 | 185 | method !one-pair {
|
190 |
| - my @pairs = @.cards»\ |
191 |
| - .rank.&counts\ |
192 |
| - .sort(*.key).grep(*.key == 2); |
193 |
| - if so @pairs { |
194 |
| - OnePair => my $x = EVAL(@pairs[0].value), |
195 |
| - HighCard => max grep { $_ !~~ $x}, @.cards».rank; |
196 |
| - } |
| 186 | + my $pair = @.cards»\ |
| 187 | + .rank.&counts\ |
| 188 | + .sort(*.key).grep(*.key == 2)[0]; |
| 189 | + if $pair { |
| 190 | + (OnePair) => my $x = $pair.value, |
| 191 | + (HighCard) => max grep { $_ !~~ $x}, @.cards».rank; |
| 192 | + } |
197 | 193 | }
|
198 | 194 |
|
199 | 195 | method !high-card {
|
200 |
| - HighCard => max @.cards».rank ; |
| 196 | + (HighCard) => @.cards».rank.max; |
201 | 197 | }
|
202 | 198 | }
|
203 | 199 |
|
204 |
| - |
205 |
| -multi infix:«~~~»(Deal $a, Hand $h) { |
206 |
| - so $a.score{$h}; |
207 |
| -} |
208 |
| - |
209 | 200 | multi infix:«<=>»(Deal $a, Deal $b) returns Order {
|
210 | 201 | for Hand.enums.sort(*.value).keys.map({Hand($_)}) -> $h {
|
| 202 | + return More if $a.score{$h}.defined && !$b.score{$h}.defined; |
| 203 | + return Less if $b.score{$h}.defined && !$a.score{$h}.defined; |
| 204 | + next unless $a.score{$h} & $b.score{$h}; |
211 | 205 |
|
212 |
| - return More if $a.score{$h}.defined && !$b.score{$h}.defined; |
213 |
| - return Less if $b.score{$h}.defined && !$a.score{$h}.defined; |
214 |
| - next unless $a.score{$h}.defined && $b.score{$h}.defined; |
215 |
| - |
216 |
| - if $a.score{$h} & $b.score{$h} ~~ List { |
217 |
| - my $cmp = max $a.score{$h} Z<=> $b.score{$h}; |
218 |
| - return Less if $cmp ~~ Same | Less; |
219 |
| - return More if $cmp ~~ More; |
220 |
| - } |
| 206 | + if $a.score{$h} & $b.score{$h} ~~ List { |
| 207 | + my $cmp = max $a.score{$h} Z<=> $b.score{$h}; |
| 208 | + return Less if $cmp ~~ Less; |
| 209 | + return More if $cmp ~~ More; |
| 210 | + } |
221 | 211 |
|
222 |
| - my $cmp = $a.score{$h} <=> $b.score{$h}; |
| 212 | + my $cmp = $a.score{$h} <=> $b.score{$h}; |
223 | 213 |
|
224 |
| - next if $cmp ~~ Same; |
225 |
| - return $cmp; |
| 214 | + next if $cmp ~~ Same; |
| 215 | + return $cmp; |
226 | 216 | }
|
| 217 | + Same; |
227 | 218 | }
|
228 | 219 |
|
229 | 220 | sub MAIN(Bool :$verbose = False,
|
230 |
| - Bool :$run-tests = False, |
231 |
| - :$file = $*SPEC.catdir($*PROGRAM_NAME.IO.dirname, 'poker.txt'), |
232 |
| - :$lines = Inf, # read only X lines from file |
233 |
| - ) { |
| 221 | + Bool :$run-tests = False, |
| 222 | + :$file = $*SPEC.catdir($*PROGRAM_NAME.IO.dirname, 'poker.txt'), |
| 223 | + :$lines = Inf, # read only X lines from file |
| 224 | + ) { |
234 | 225 | die "'$file' is missing" unless $file.IO.e ;
|
235 |
| - return tests if $run-tests; |
236 |
| - |
237 |
| - say [+] gather for $file.IO.lines[^$lines] { |
238 |
| - my $line = $_; # for mutability |
239 |
| - $line ~~ s:nth(5)/\s/;/; |
240 |
| - my ($h1,$h2) = $line.split: /';'/; |
241 |
| - my $d1 = Deal($h1); |
242 |
| - my $d2 = Deal($h2); |
243 |
| - if $d1 <=> $d2 ~~ More { |
244 |
| - say "player1 wins on $line \n\twith {$d1.score.&strigify} against {$d2.score.&strigify} " if $verbose ; |
245 |
| - take 1; |
246 |
| - } |
| 226 | + return TEST if $run-tests; |
| 227 | + |
| 228 | + say [+] gather for $file.IO.lines[^$lines] <-> $line { |
| 229 | + $line ~~ s:nth(5)/\s/;/; |
| 230 | + my ($h1,$h2) = $line.split: /';'/; |
| 231 | + my $d1 = Deal($h1); |
| 232 | + my $d2 = Deal($h2); |
| 233 | + if $d1 <=> $d2 ~~ More { |
| 234 | + say "player1 wins on $line \n\twith {$d1.score.&strigify} against {$d2.score.&strigify} " if $verbose ; |
| 235 | + take 1; |
| 236 | + } |
247 | 237 | }
|
248 |
| - |
| 238 | + |
249 | 239 | }
|
250 | 240 |
|
251 |
| -sub tests { |
| 241 | +sub TEST { |
252 | 242 | use Test;
|
253 | 243 | ok Card("TC") <=> Card("TD") ~~ Same, "cards are equal if ranks are equal ";
|
254 | 244 | ok Card("2C") <=> Card("AC") ~~ Less, "2C < AC";
|
255 |
| - ok (Deal("5H 6C 7S 8D 9D") ~~~ Straight) && |
256 |
| - (Deal("2H 6C 7S 8D 9D") !~~~ Straight) , "Detects straight"; |
257 |
| - ok (Deal("5H 7H 8H AH TH") ~~~ Flush) && |
258 |
| - (Deal("5H 7H 8H AC TH") !~~~ Flush), "Detects flush "; |
259 |
| - ok Deal("TH JH QH KH AH") ~~~ RoyalFlush, "Detects royal flush "; |
| 245 | + ok (Straight ~~ Deal("5H 6C 7S 8D 9D") ) && |
| 246 | + (Straight !~~ Deal("2H 6C 7S 8D 9D")) , "Detects straight"; |
| 247 | + ok (Flush ~~ Deal("5H 7H 8H AH TH")) && |
| 248 | + (Flush !~~ Deal("5H 7H 8H AC TH")), "Detects flush "; |
| 249 | + ok RoyalFlush ~~ Deal("TH JH QH KH AH") , "Detects royal flush "; |
260 | 250 | ok Deal("5H 5C 6S 7S KD") <=> Deal("2C 3S 8S 8D TD") ~~ Less,"Player 2 wins [1]";
|
261 | 251 | ok Deal("5D 8C 9S JS AC") <=> Deal("2C 5C 7D 8S QH") ~~ More, "Player 1 wins [2]";
|
262 | 252 | ok Deal("2D 9C AS AH AC") <=> Deal("3D 6D 7D TD QD") ~~ Less, "Player 2 wins [3]";
|
263 | 253 | ok Deal("4D 6S 9H QH QC") <=> Deal("3D 6D 7H QD QS") ~~ More, "Player 1 wins [4]";
|
264 |
| - ok Deal("2H 2D 4C 4D 4S") <=> Deal("3C 3D 3S 9S 9D") ~~ More, "Player 1 wins [5]"; |
| 254 | + ok Deal("2H 2D 4C 4D 4S") <=> Deal("3C 3D 3S 9S 9D") ~~ Same, "Nobody wins [5]"; |
265 | 255 | ok Deal("7C 5H KC QH JD") <=> Deal("AS KH 4C AD 4S") ~~ Less, "Player 2 wins [6]";
|
266 | 256 | ok Deal("KS KC 9S 6D 2C") <=> Deal("QH 9D 9H TS TC") ~~ Less, "Problem [1]";
|
267 | 257 | ok Deal("TS QH 6C 8H TH") <=> Deal("5H 3C 3H 9C 9D") ~~ Less, "Problem [2]";
|
|
0 commit comments