18

我喜欢这样的挑战,我希望尽快提交我的答案。

哪位玩家拥有最好的 7 张牌?

给定 9 张牌的无序列表(用空格分隔),找出哪个玩家的牌手最好。 这是扑克手排名列表。示例输入:

2C 5H AS KS 2D 4D QD KH 3S
(ie: [[2C 5H] [AS KS] [2D 4D QD KH 3S]])

数组中的前 2 张牌代表玩家 1 的手牌,数组中的第二张牌代表玩家 2 的手牌。最后 5 张牌代表公共牌,双方玩家共用的牌。实际上,两位玩家都有 7 张牌,您必须确定哪位玩家拥有最好的 5 张牌。

一张牌被定义为一个字符串,第一个字符代表牌面值,第二个值代表花色。总是大写。没有卡片可以出现两次。

该函数将计算这手牌对任一玩家来说是平局还是获胜。它将在输入结束时输出总数。输出格式将在本文后面定义。

例子

2C 5H AS KS 2D 4D QD KH 3S
(ie: [[2C 5H] [AS KS] [2D 4D QD KH 3S]])
Player 2 wins this hand.  Player 1 has a pair of 2's, player 2 has a pair of kings.

5S 6S 8H 9D 7S 8S JH TS 2H
(ie: [[5S 6S] [8H 9D] [7S 8S JH TS 2H]])
Player 1 wins this hand  Player 1 has a flush, player 2 has a straight.

2S 2H AC AS 2C AH 9H TS 2D    
(ie: [[2S 2H] [AC AS] [2C AH 9H TS 2D]])
Player 1 wins this hand.  Player 1 has quads, player 2 has a full house

5S 6S 2D 4D 9S AS KD JC 9D
(ie: [[5S 6S] [2D 4D] [9S AS KD JC 9D]])
A draw.  Both players have Ace high.

更多信息

感谢 mgroves 提供以下指向具有类似问题的 Project Euler 的链接: http ://projecteuler.net/index.php?section=problems&id=54

测试数据

我们将使用 Project Euler 测试数据:

http://projecteuler.net/project/poker.txt

您的解决方案应该接受该文本文件作为输入,并输出总的赢和平局。

示例输出

输出必须采用以下格式:

1: 45
2: 32
D: 12

玩家 1 赢了 45 手,玩家 2 赢了 32 手,共有 12 局平局。(非实际结果)

规则

  • 不必返回获胜的手型,如果有人赢了,只有谁赢了
  • 卡片列表输入没有特定顺序
  • 没有卡片在输入中出现两次
  • 输入总是大写
  • 将 Project Euler 测试数据作为输入
  • 输出一个计数,在上面给定的格式中,哪个玩家赢得最多的手和总平局
4

6 回答 6

15

Perl,414 398 370/458 344/416 字符

换行不重要。

%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/.$/;$M{$`}.$&}

sub B{$s=@p=();
for$m(@_){$m-$_||($s+=2,++$p[$m])for@_}
@_=sort{$p[$b]-$p[$a]||$b-$a}@_;
$s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);
"@_"=~/.$/;$s+=14*(4<grep/$&/,@_);
$s=100*$s+$_ for@_;$s}

++$X{B((@c=map{N}split)[0..4])<=>B(@c[5..9])}for<>;
printf"1: %d\n2: %d\nD: %d\n",@X{1,-1,0}

这解决了“10 张牌”问题(发了 10 张牌,玩家 1 有前 5 张牌,玩家 2 有后 5 张牌)。

第一部分定义了一个子程序N,它可以转换每张卡片,使其具有数值。对于非人脸牌,这是一个简单的映射(5H ==> 5H),但它确实转换了人脸牌(KC => 13C,AD => 14D)。

最后一部分将输入的每一行解析为卡片,将卡片转换为包含数值,将卡片分成两个玩家的不同手牌,然后分析和比较这些手牌。每只手都会增加一个哈希元素%X。解析所有输入后,%X包含玩家 1 赢的手数、玩家 2 赢的手数或平局数。

中间部分是一个子程序,它以一组五张牌作为输入并产生一个 12 位数字,其属性是更强的扑克牌将拥有更高价值的数字。以下是它的工作原理:

    for$m(@_){$m-$_||($s+=2,++$p[$m])for@_}

这是“对”检测器。如果任意两张卡片具有相同的数值,则为其中一张卡片增加一个哈希元素,并将“score”变量$s增加 2。请注意,我们最终会将每张卡与其自身进行比较,因此$s至少为 10,并且$p[$x]每张卡至少为 1 $x。如果手牌包含三张,那么这三张牌将与另外两张牌匹配——这就像这三张牌中有 9 次匹配,“分数”至少为 18。

    @_=sort{$p[$b]-$p[$a]||$b-$a}@_;

按 (1) 该卡是“对”的一部分的次数和 (2) 卡的价值对卡进行排序。因此,在有两个 7 和两个 3 的手牌中,两个 7 将首先出现,然后是两个 3,然后是起脚牌。在有两个 7 和三个 3 的一手牌中,三个 3 将首先出现在两个 7 之后。此排序的目的是区分具有相同分数的两只手 - 一对 8 的手和一对 7 的手都有一对,但我们需要能够分辨出一对 8更好。

    $s=23 if$s<11&&($_[0]-$_[4]<5||$_[0]-$_[1]>8&&push@_,shift);

这条线是“直线”检测器。顺子价值 23 点,当手上没有对子时发生($s<11意味着只找到 5 个“对子”——每张与自己匹配的牌),并且(1)最高牌的价值正好比最低牌的价值 ( $_[0]-$_[4]==4),或 (2) 最高价值的牌是 A,次高的牌是 5 ( $_[0]-$_[1]==9),这意味着这手牌有 A-2-3-4-5 顺子。在后一种情况下,A 现在是手中最不值钱的牌,所以我们操纵@_以反映 ( push@_,shift)

    "@_"=~/.$/;$s+=14*(4<grep/$&/,@_);

这条线是冲洗检测器。当每张牌的最后一个字符相同时,同花就值 14 点以上。第一个表达式 ( ) 具有设置为手中最后一张牌的最后一个字符(花色)的"@_"=~/.$/副作用。当且仅当 的所有元素具有相同的最后一个字符时$&,最终表达式 ( 4<grep/$&/,@_) 才会为真。@_

    $s=100*$s+$_ for@_;$s}

创建并返回一个值,该值以手牌得分开始,然后包含卡片的值,按卡片的重要性排序。不同手牌的分数将是

Hand           Score
----------    ------
High card       10     (each card matches itself for two points)
One pair        14     (2 additional matches)
Two pair        18     (4 additional matches)
Three of a kind 22     (6 additional matches)
Straight        23     (no pair, but 23 points for straight)
Flush           24     (no pair, but 14 additional points for the flush)
Full house      26     (8 additional matches)
4 of a kind     34     (12 additional matches)
Straight flush  37     (23 + 14 points)

这符合扑克规则。相同分数的手牌可以通过手牌的价值来区分,按照对手牌的重要性,一直到手牌中最不值钱的牌。

解决 9 张牌问题(玩家 1 两张牌,玩家 2 两张牌,玩家分享接下来的 5 张牌并建立他们最好的 5 张牌手)需要大约 70 次以上的笔画才能从中选出最好的 5 张牌手。 7张牌可供每位玩家使用:

%M=map{$_,$Z++}0..9,T,J,Q,K,A;sub N{/./;$M{$&}.$'}

sub A{my$I;
for$k(0..41){@d=@_;splice@d,$_,1for$k%7,$k/7;$s=@p=();
for$m(grep$_=N,@d){$m-$_||($s+=2,$p[$m]++)for@d}
@d=sort{$p[$b]-$p[$a]||$b-$a}@d;
$s=23 if$s<11&&($d[0]-$d[4]<5||$d[0]-$d[1]>8&&push@d,shift@d);
"@d"=~/.$/;$s+=14*(4<grep/$&/,@d);
$s=100*$s+$_ for@d;
$I=$s if$s>$I}$I}

++$X{A((@c=split)[0,1,4..8])<=>A(@c[2..8])}for<>;
printf"1: %d\n2: %d\nD: %d\n",@X{1,-1,0}
于 2010-07-31T05:50:21.643 回答
7

哈斯克尔:793 796 806 826 864 904 901 880 863


由于文本文件与 9 张牌不一致,我只是从控制台读取一行并输出谁赢了。


Bug修复:

  • Ace 现在在 ace-low 运行中的计数低于 2。
  • 比较固定的满屋(再次:D)。
  • 保证选择给定手型的最佳版本。例如,如果玩家可以在 2-6 跑和 3-7 跑之间进行选择,则选择 3-7 跑(冲洗一边)。
  • 现在比 PHP 解决方案更短!

打高尔夫球:

import Data.List
(%)=mod
m=map
y=foldr1
t=0<1
z=13
w=[0,1,2,3,12]
n&x|length x<n=[]|t=take n x
b?x|b=x|t=[]
n!k= \c->e(n&m(%k)c)?(n&c)
e[]=1<1
e(x:y)=all(x==)y
k q c|any null[q c,p$c\\q c]=[]|t=q c
f=5!4
s c=(sort(m(%z)c)`elem`w:[[n..n+4]|n<-[0..8]])?c
r=3!z
p=2!z
g x y|c x y<2=x|t=y
q x(_,[])=x
q _ y=y
b h=y q$m($h)$zipWith(\t f->(,)t.y g.m(f.take 5).permutations)[1..][1!1,p,k p,r,s,f,k r,4!z,s.f]
h=reverse.a.m(%z)
a v|w\\v==[]=[-1..3]|t=sort v
c x y=o(h x)$h y
o[](_:_)=2
o[]_=0
o _[]=1
o(a:b)(k:d)|a>k=1|a<k=2|t=o b d
d n(a,k)|a==[]=0|n<1=0|r>s=1|r<s=2|f/=0=f|t=d(n-length o)(a\\o,k\\u)where(r,o)=b a;(s,u)=b k;f=c o u
i x=head.findIndices(x==)
u(n:k)c@[r,s]|n%z==i r"23456789TJQKA"&&n%4==i s"HDSC"=n|t=u k c
l c=(2&c++snd(splitAt 4c),drop 2c)
main=getLine>>=print.d 5.l.m(u[0..]).words

未打高尔夫球:

import Control.Exception (assert)
import Data.List (permutations, sort, intersect, findIndices, (\\))
import Data.Function (on)

(%) = mod

aceLowRun = [0,1,2,3,12]

tryTake n xs
  | length xs < n = []
  | otherwise = take n xs

cond ? xs
  | cond = xs
  | otherwise = []

eqOn n f cards = allEq (tryTake n $ map f cards) ? tryTake n cards

allEq [] = False
allEq (x:xs) = all (== x) xs

combWithPair pokerHand cards
  | any null [picked1, picked2] = []
  | otherwise = pokerHand cards
  where
    picked1 = pokerHand cards
    picked2 = pair $ cards \\ picked1

straightFlush = straight . flush

quads = eqOn 4 (% 13)

fullHouse = combWithPair triples

flush = eqOn 5 (% 4)

straight cards = (sort (map (% 13) cards) `elem` runs) ? cards
  where
    runs = aceLowRun : [[n..n+4] | n <- [0..8]]

triples = eqOn 3 (% 13)

twoPair = combWithPair pair

pair = eqOn 2 (% 13)

single = eqOn 1 id

bestVersionOfHand [] ys = ys
bestVersionOfHand xs [] = xs
bestVersionOfHand xs ys
  | compareSameRankedHands xs ys < 2 = xs
  | otherwise = ys

rate rating pokerHand cards = (rating, handResult)
  where
    handResult = foldr1 bestVersionOfHand
                        (map (pokerHand . take 5) $ permutations cards)

pokerHands = zipWith rate [1..] [
    single
  , pair
  , twoPair
  , triples
  , straight
  , flush
  , fullHouse
  , quads
  , straightFlush
  ]

bestHand hand = foldr1 (\xs ys -> if null (snd ys) then xs else ys)
                       (map ($ hand) pokerHands)

highestVals = reverse . arrangeVals . map (% 13)
  where
    arrangeVals vals = if vals `intersect` aceLowRun == aceLowRun
      then [-1..3]
      else sort vals

compareSameRankedHands = compareSameRankedHands' `on` highestVals

compareSameRankedHands' [] [] = 0
compareSameRankedHands' (card1:cards1) (card2:cards2)
  | card1 > card2 = 1
  | card1 < card2 = 2
  | otherwise = compareSameRankedHands' cards1 cards2

decideWinner n cards1 cards2
  | null cards1 = assert (null cards2) 0
  | n < 1 = 0
  | rating1 > rating2 = 1
  | rating1 < rating2 = 2
  | cmpRes /= 0 = cmpRes
  | otherwise = decideWinner
                  (n - assert (length bests1 == length bests2) (length bests1))
                  (cards1 \\ bests1)
                  (cards2 \\ bests2)
  where
    (rating1, bests1) = bestHand cards1
    (rating2, bests2) = bestHand cards2
    cmpRes = compareSameRankedHands bests1 bests2

indexOf x = head . findIndices (x==)

toNum = toNum' [0..]

toNum' (n:ns) [rank, suit]
  | n % 13 == indexOf rank "23456789TJQKA" && n % 4 == indexOf suit "HDSC" = n
  | otherwise = toNum' ns [rank, suit]

cluster cards = (take 2 cards ++ snd (splitAt 4 cards), drop 2 cards)

main = getLine >>= print
  . uncurry (decideWinner 5)
  . cluster
  . map toNum
  . words
于 2010-07-30T06:50:27.110 回答
7

GolfScript - 151/187 个字符

该程序适用于每行 10 张牌的输入列表,即两张 5 张牌。

n%0.@{3/5/{[zip~;.&,(!15*\[{n),*"TJQKA"+?}/]:|$),-4>=14*+1|{.2\?|@-,5\-.49?@*@+\.+@+\}/.16445=13*@+\]}%.~={0):0;;}{~>.!@+\@+\}if}/"1: "@@n"2: "@n"D: "0

该程序适用于每行 9 张卡片的输入列表,其格式在规范中描述。

n%0.@{3/.4>:D;2/2<{D+.{3/1$^.{3/1$^[zip~;.&,(!15*\[{n),*"TJQKA"+?}/]$:|),-4>=14*+1|{.2\?|@-,5\-.49?@*@+\.+@+\}/.16445=13*@+\]}%\;~}%$-1=\;}%.~={0):0;\(\}*~>.!@+\@+\}/"1: "@@n"2: "@n"D: "0
于 2010-08-03T10:45:18.677 回答
4

GolfScript 258 241 247/341 217/299 字符

解决10卡问题。只有最后几行是重要的:

10:T):J):Q):K):A;0:a;0:b;0:d;"\r\n"%{' '/5/{.{)\;}/4*-+++!:f;{);~}%{$0:z(%{.z-
!99*+:z}%}5*.{+}*99/:P!{..)\(@4+-!2*\;\.2<~9+-!\;+}and:s;[s f*6P=4P=f s P 6$]\;}
%.~={;;d):d;}{~>{a):a;}{b):b;}if}if}/
'1: 'a'
2: 'b'
D: 'd n

9卡问题目前需要大约80多个字符。

10:T):J):Q):K):A;0:a;0:b;0:d;"\r\n"%{' '/);{('Z'%+}2*[0$2>\7<]
{:H;7,{H=:x;H{x=!},:I;6,{I=:x;I{x=!},}/}%{.{)\;}/4*-+++!:f;
{);~}%{$0:z(%{.z-!99*+:z}%}5*.{+}*99/:P!{..)\(@4+-!2*\;\.2<~9+-!\;+}and:s;[
s f*6P=4P=f s P 6$]\;}%{[\].~>{~;}{~\;}if}*}%.~={;;d):d;}{~>{a):a;}{b):b;}if}if}/
'1: 'a'
2: 'b'
D: 'd n

少打高尔夫的版本 10 卡问题。

10:T;11:J;12:Q;13:K;14:A;              # map for face cards
0:a;0:b;0:d;                           # other initialization

"\r\n"%                                # split input on \n
{                                      # on each line of input
    ' '/                               #  divide line into ten cards
    5/                                 #  split into five card hands

    {.                                 #  on each of the two hands

         {)\;}%                        #   chop last character of each card
         .(5*\;\{+}*=                  #   check sum of elem == 5*1st elem
         :f;                           #   this is the flush flag

         {);~}%$                       #   reduce cards to numerical values

         0:z;{.z- 20%{}                
             {;z 20+}if:z}%{-1*}$      #   detect pairs

         .(:h;;                        #   extract value of highest card


         20h>{..)\(@4+-!2*\;\          # detect straight
             .2<~9+-!\;+}and:s;        # s=2 for regular straight, s=1 for A-5 straight

                                       # result of this mapping - 6 elem array
         [ 0$                          #   #6 - cards in the hand
           .{20/}%{+}*:P               #   #5 - number of pairs
           s                           #   #4 - is this a straight?
           f                           #   #3 - is this a flush?
           4P=                         #   #2b - is this a full house?
           h 59>                       #   #2 - is this 4 of a kind?
           s f *                       #   #1 - is this a straight flush?
         ]-1% 

    \;
    }/

    \.@.@                             # put [hand1 hand2 hand1 hand2] on stack

    =                                 # check hand1==hand2

    {;;d):d;}                         # if equal, increment d (draw)
       {>{a):a;}                      # if >, increment a (player 1 wins)
       {b):b;}if                      # if <, increment b (player 2 wins)
     }if
}/

                                      # output results    
'1: 'a'
2: 'b'
D: 'd n
于 2010-08-03T03:43:08.357 回答
3

C,665+379 个字符

这是我的答案,分为两部分。

第一个是完整的 7 卡评估器,包括“AddCard”宏A。它返回一个 32 位数字,对手牌进行排名。高半字节是类型,位 13..25 表示高牌,位 0..12 表示踢牌。比较结果时,更好的牌总是有更大的价值。

#define U unsigned
#define c(a)X=a;i=C=0;while(X){C|=(X&1)<<i++;X/=4;}
#define A(h,c)h[c&7]+=c,h[3]|=c
U C,i,X;
U E(U h[]){
U a=h[0]+h[1]+h[2]+h[4]-(h[3]&-16),t,v,k,e=a&0x55555540,o=a&0xAAAAAA80;
if(v=e&o/2){t=7;k=h[3]^v;i=0;while(k/=4)i++;k=1<<2*i;}
else if(v=o&o-1){t=6;v/=2;k=o/2^v;}
else if(e>1&o>1){t=6;v=o/2;k=(i=e&e-1)?i:e;}
else{a=h[3];
if(t=h[i=1]-(a&1)&4||h[i=2]-(a&2)&8||h[i=4]-(a&4)&16||h[i=0]-(a&8)&32)a=h[i];
a&=-64;v=a|a>>26&16;t*=5;
if(v=v&v<<2&v<<4&v<<6&v<<8){t+=4;a=v&=~(v/2);}
else if(t)for(i=(h[i]&63)/(i?i:8),v=a;i-->5;)a&=a-1;
else if(v=o/2)t=3;
else if (e){o=e&e-1;v=(i=o&o-1)?o:e;t=1+(o>0);}
k=a^v;k&=k-1;k&=k-(i==0);}
c(v);v=C/8;c(k);
return t<<28|v<<13|C/8;}

第二个是输入处理器。它将项目 Euler 文件解析为 2+2+5 张卡片(忽略第 10 张卡片)。它使用 Parse 宏P来创建代表每张卡片的 32 位值。表示是0A0K0Q0J0T090807060504030200shdc。手被存储为 5 个整数的数组。

char*gets(char*);char*strchr(char*,char);
#define P(c)X=strchr(R,*c++)-R;C=1<<strchr(S,*c++)-S|64<<X*2;c++;
#define L(n)for(i=0;i<n;i++)
U g[5],h[5];
char*c,b[32];
char*S="CDHS";
char*R="23456789TJQKA";
int d,r[3]={0};
main(q){while(c=gets(b)){
L(2){P(c)A(g,C);}
L(2){P(c)A(h,C);}
L(5){P(c)A(g,C);A(h,C);}
d=E(g)-E(h);
r[d>0?0:d<0?1:2]++;
L(7)g[i]=h[i]=0;
}L(3)printf("%c:%d\n","12D"[i],r[i]);}

我敢肯定还有一些字符需要修剪掉。我会尽快添加解释。

评估器在我的 3Ghz Core2 Duo 上运行 @1760 万手/秒。这仅比使用至少 56K 查找表的PokerSource评估器慢 3.5 倍。

于 2010-08-02T21:42:11.003 回答
3

PHP,799 个字符

换行不重要。这从链接的 url 获取输入,这与示例输入不同(不处理社区卡)。处理类似于 mobrule 的 perl 答案,但评分方法不同。

<?php
function s($i){$o=array_map('intval',$i);$f=(count(array_unique(str_replace($o,'',$i)))==1);
sort($o);$v=array_count_values($o);arsort($v);$u=array_keys($v);$h=max($u);$m=$u[0];$c=reset($v);
$p=count($v);$e=$c==1&&$o[4]==14&&$o[3]==5;$r=$o==range($o[0],$o[0]+4)||$e;$q=$e?5:$h;
$s=($f&&$r&&($h==12)?2<<11:($f&&$r?(2<<10)+$q:0))+($c==4?(2<<9)+$m:0)+($c==3&&$p==2?(2<<8)+$m:0)+($f?(2<<7)+$h:0)+
($r?(2<<6)+$q:0)+($c==3?(2<<5)+$m:0)+($c==2&&$p==3?(2<<4)+$m:0)+($p==4?(2<<3)+$m:0);$s+=!$s?$h:0;return array($s,$u);}

foreach(file($argv[1]) as $d){
list($y,$z)=array_chunk(explode(' ',trim(strtr($d,array('T'=>10,'J'=>11,'Q'=>12,'K'=>13,'A'=>14)))),5);
$y=s($y);$z=s($z);$w=$y[0]-$z[0];$x=1;while(!$w&&$x<5){$w=$y[1][$x]-$z[1][$x++];}if(!$w)@$t++;elseif($w<0)@$l++;else @$k++;}
@print "1: $k\n2: $l\nD: $t";
于 2010-07-31T23:29:41.770 回答