Code Golf:谁拥有最好的扑克牌?

时间:2010-07-29 16:34:29

标签: language-agnostic code-golf rosetta-stone

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

哪位牌手拥有最好的7张牌?

给出一张9张牌的无序列表(用空格分隔),找出哪个牌手有最好的扑克牌。 Here is a list of poker hand rankings。示例输入:

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

阵列中的前2张牌代表牌手1的牌,阵列中的第2张牌代表牌手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个平局。 (不是实际结果)

规则

  • 不必返回获胜的手牌类型,只有世界卫生组织赢了,如果有人
  • 卡片列表输入没有特定顺序
  • 输入中没有卡片出现两次
  • 输入始终为大写
  • 将项目Euler测试数据作为输入
  • 输出一个计数,其中哪个玩家赢得了最多的牌局,以及上面给定格式的总牌局

6 个答案:

答案 0 :(得分:15)

Perl, 414 398 370/458 344/416 char

换行并不重要。

%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 ==&gt; 5H),但它确实转换了面部卡(KC =&gt; 13C,AD =&gt; 14D)。

最后一部分将每行输入解析成牌,将牌变换为包含数值,将牌分成两个牌手分开,分析并比较这些牌。每只手递增哈希值%X的一个元素。当解析了所有输入时,%X包含玩家1赢得的牌数,由玩家2赢得的牌数或关系。

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

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

这是“配对”探测器。如果任何两张牌具有相同的数值,则增加其中一张牌的哈希元素,并将“得分”变量$s增加2。请注意,我们最终会将每张卡与自身进行比较,因此$s至少为10,$p[$x]每张卡$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)的价值多4个,或者(2)最高价值卡是Ace,而下一个最高卡是5($_[0]-$_[1]==9),这意味着手牌有A-2-3-4-5直。在后一种情况下,Ace现在是手中的最少有价值的卡片,因此我们操纵@_来反映(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}

答案 1 :(得分:7)

Haskell:793 796 806 826 864 904 901 880 863


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


错误修正:

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

Golfed:

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

Ungolfed:

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

答案 2 :(得分: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

答案 3 :(得分:4)

GolfScript 258 241 247/341 217/299 char

解决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

答案 4 :(得分: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";

答案 5 :(得分: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万手/秒。这只比PokerSource评估器慢3.5倍,它使用至少56K的查找表。