扑克牌型
作者:Andrei Osipov
https://projecteuler.net/problem=54
文件 poker.txt 包含一千手随机发给两名玩家的牌。 文件的每一行包含十张牌(用一个空格隔开):前五张是玩家 1 的牌,后五张是玩家 2 的牌。 您可以假设所有牌局都是有效的(没有无效字符或重复的牌),每个玩家的牌都没有特定的顺序,并且在每手牌中都有一个明确的赢家。
玩家 1 赢了多少手牌?
use v6;
enum Rank <
Two Three Four Five
Six Seven Eight Nine
Ten Jack Queen King Ace
>;
enum Suit <
Hearts Diamonds Clubs Spades
>;
enum Hand <
RoyalFlush StraightFlush FourOfKind
FullHouse Flush Straight ThreeOfKind
TwoPairs OnePair HighCard
>;
multi counts(Positional $h) {
bag($h).invert
}
multi strigify(Hash $x) {
join ' and ', do for $x.kv -> $k, $v {
"$k of $v"
}
}
class Card {
has Rank $.rank;
has Suit $.suit;
method parse-rank(Str $r) returns Rank {
given $r {
when /\d/ { Rank($r.Int - 2) }
when /T/ { Ten }
when /J/ { Jack }
when /Q/ { Queen }
when /K/ { King }
when /A/ { Ace }
}
}
method parse-suit(Str $s) returns Suit {
given $s {
when /H/ { Hearts }
when /D/ { Diamonds }
when /C/ { Clubs }
when /S/ { Spades }
}
}
multi method CALL-ME(Str $c where $c.chars == 2) {
my ($r, $s) = $c.comb;
self.new(rank => Card.parse-rank($r),
suit => Card.parse-suit($s));
}
multi method CALL-ME(Rank $v, Suit $s) {
self.new(rank => $v, suit => $s)
}
}
multi infix:«<=>»(Card $a, Card $b) is export returns Order {
$a.rank <=> $b.rank
}
class Deal {
subset Ranks where -> $r {
$r ~~ Rank || $r ~~ Array[Rank]
};
has Card @.cards;
has Ranks %.score{Hand};
method CALL-ME(Str $h) {
my $x = self.new(
cards => map { Card($_) } , $h.split: /\s/
);
$x.score = $x!best-hand;
$x;
}
method ACCEPTS(Hand $h) {
so %.score{$h};
}
method !best-hand {
self!royal-flush
// self!straight-flush
// self!full-house
// self!flush
// self!straight
// self!four-of-kind
// self!three-of-kind
// self!two-pairs
// self!one-pair
// self!high-card
}
method !straight {
my @v = @.cards».rank.sort;
if @v eq (@v.min ... @v.max).map({Rank($_)}) {
(Straight) => @v.max
}
}
method !flush {
if [~~] @.cards».suit {
(Flush) => Array[Rank].new: |@.cards».rank;
}
}
method !royal-flush {
if self!flush && self!straight && @.cards».rank.max ~~ Ace {
(RoyalFlush) => Ace
}
}
method !straight-flush {
if self!flush && my $s = self!straight {
(StraightFlush) => $s.value
}
};
method !four-of-kind {
# Four cards of the same value.
my @ranks = @.cards».rank;
my @four = @ranks.&counts.grep(*.key == 4);
if so @four {
(FourOfKind) => my $x = @four[0].value,
(HighCard) => max grep { $_ !~~ $x }, @ranks
}
}
method !full-house {
# Three of a kind and a pair.
my Ranks %x{Hand} = flat self!three-of-kind , self!one-pair;
if %x{ThreeOfKind}.defined && %x{OnePair}.defined {
(FullHouse) => Ace
}
}
method !three-of-kind {
my $rank = @.cards».rank.&counts.grep(*.key == 3)[0];
if $rank {
my Ranks %h{Hand} = (ThreeOfKind) => my $x = $rank.value;
if my $one-pair = @.cards».rank.&counts.grep(*.key == 2)[0] {
%h{OnePair} = $one-pair.value;
}
else {
%h{HighCard} = max grep { $_ !~~ $x }, @.cards».rank;
}
%h;
}
}
method !two-pairs {
my @pairs = @.cards»\
.rank.&counts\
.sort(*.key).grep(*.key == 2);
if +@pairs == 2 {
(OnePair) => my $x= @pairs».value.min,
(TwoPairs) => my $y= @pairs».value.max,
(HighCard) => max grep { $_ !~~ $x | $y },@.cards».rank;
}
}
method !one-pair {
my $pair = @.cards»\
.rank.&counts\
.sort(*.key).grep(*.key == 2)[0];
if $pair {
(OnePair) => my $x = $pair.value,
(HighCard) => max grep { $_ !~~ $x}, @.cards».rank;
}
}
method !high-card {
(HighCard) => @.cards».rank.max;
}
}
multi infix:«<=>»(Deal $a, Deal $b) returns Order {
for Hand.enums.sort(*.value).keys.map({Hand($_)}) -> $h {
return More if $a.score{$h}.defined && !$b.score{$h}.defined;
return Less if $b.score{$h}.defined && !$a.score{$h}.defined;
next unless $a.score{$h}.defined & $b.score{$h}.defined;
if $a.score{$h} & $b.score{$h} ~~ List {
my $cmp = max $a.score{$h} Z<=> $b.score{$h};
return Less if $cmp ~~ Less;
return More if $cmp ~~ More;
}
my $cmp = $a.score{$h} <=> $b.score{$h};
next if $cmp ~~ Same;
return $cmp;
}
Same;
}
sub MAIN(Bool :$verbose = False,
Bool :$run-tests = False,
:$file = $*SPEC.catdir($*PROGRAM-NAME.IO.dirname, 'poker.txt'),
:$lines = Inf, # read only X lines from file
) {
die "'$file' is missing" unless $file.IO.e ;
return TEST if $run-tests;
say [+] gather for $file.IO.lines[^$lines] -> $line is copy {
$line ~~ s:nth(5)/\s/;/;
my ($h1,$h2) = $line.split: /';'/;
my $d1 = Deal($h1);
my $d2 = Deal($h2);
if $d1 <=> $d2 ~~ More {
say "player1 wins on $line \n\twith {$d1.score.&strigify} against {$d2.score.&strigify} " if $verbose ;
take 1;
}
}
}
sub TEST {
use Test;
ok Card("TC") <=> Card("TD") ~~ Same, "cards are equal if ranks are equal ";
ok Card("2C") <=> Card("AC") ~~ Less, "2C < AC";
ok (Straight ~~ Deal("5H 6C 7S 8D 9D") ) &&
(Straight !~~ Deal("2H 6C 7S 8D 9D")) , "Detects straight";
ok (Flush ~~ Deal("5H 7H 8H AH TH")) &&
(Flush !~~ Deal("5H 7H 8H AC TH")), "Detects flush ";
ok RoyalFlush ~~ Deal("TH JH QH KH AH") , "Detects royal flush ";
ok Deal("5H 5C 6S 7S KD") <=> Deal("2C 3S 8S 8D TD") ~~ Less,"Player 2 wins [1]";
ok Deal("5D 8C 9S JS AC") <=> Deal("2C 5C 7D 8S QH") ~~ More, "Player 1 wins [2]";
ok Deal("2D 9C AS AH AC") <=> Deal("3D 6D 7D TD QD") ~~ Less, "Player 2 wins [3]";
ok Deal("4D 6S 9H QH QC") <=> Deal("3D 6D 7H QD QS") ~~ More, "Player 1 wins [4]";
ok Deal("2H 2D 4C 4D 4S") <=> Deal("3C 3D 3S 9S 9D") ~~ Same, "Nobody wins [5]";
ok Deal("7C 5H KC QH JD") <=> Deal("AS KH 4C AD 4S") ~~ Less, "Player 2 wins [6]";
ok Deal("KS KC 9S 6D 2C") <=> Deal("QH 9D 9H TS TC") ~~ Less, "Problem [1]";
ok Deal("TS QH 6C 8H TH") <=> Deal("5H 3C 3H 9C 9D") ~~ Less, "Problem [2]";
ok Deal("AH QC JC 4C TC") <=> Deal("8C 2H TS 2C 7D") ~~ Less, "Problem [3]";
ok Deal("7C KS 6S 5S 2S") <=> Deal("2D TC 2H 5H QS") ~~ Less, "Problem [4]";
ok Deal("JC TH 4S 6S JD") <=> Deal("2D 4D 6C 3D 4C") ~~ More, "Problem [5]";
done;
}
Perl 6 示例