10
\$\begingroup\$

The Challenge:

In this question: Name the poker hand you had to take a five card poker hand and identify it. This question is similar, with two twists:

First, the output will be in all lower case. This allows for more golfing, as you don't have to worry about the capitalization of flush and straight

high card
one pair
two pair
three of a kind
straight
flush
full house
four of a kind
straight flush
royal flush

Secondly, with the popularity of Texas Hold'em and 7 card stud, we here at code golf should be able to score a seven card poker hand am I right? When scoring a seven card hand, use the five best cards for your hand and ignore the two you don't need.

Reference:

List of poker hands: http://en.wikipedia.org/wiki/List_of_poker_hands

Input (lifted directly from the previous thread)

7 cards from either stdin or commandline arguments. A card is a two letter string on the form RS, where R is rank and S is suit. The ranks are 2 - 9 (number cards), T (ten), J (Jack), Q (Queen), K (King), A (Ace). The suits are S, D, H, C for spades, diamonds, hearts and clubs respectively.

Example of cards

5H - five of hearts
TS - ten of spades
AD - ace of diamonds

Example of input => desired output

3H 5D JS 3C 7C AH QS => one pair
JH 4C 2C 9S 4H JD 2H => two pair
7H 3S 7S 7D AC QH 7C => four of a kind
8C 3H 8S 8H 3S 2C 5D => full house
AS KC KD KH QH TS JC => straight

Notice in the second example there are actually three pairs, but you can only use five cards, so it's two pair. In the fifth example, there are both a three of a kind and a straight possible, but a straight is better, so output straight.

Scoring

This is , so shortest code wins!

Errata

  1. You may not use external resources.
  2. Ace is both high and low for straights.
\$\endgroup\$
  • \$\begingroup\$ Nice; I secretly hoped that someone would pick up the ball. Just wanted to note that I did not have any restrictions on the capitalization in the original question (clarified in a comment), So you could (and most/all did) output "Straight Flush". IMHO capitalized looks better. \$\endgroup\$ – daniero Mar 11 '14 at 14:38
  • \$\begingroup\$ You say input (lifted directly from the previous thread) 5 cards. I think you meant to change that to 7. \$\endgroup\$ – Level River St Mar 11 '14 at 14:41
  • \$\begingroup\$ @steveverrill You can edit posts yourself on stack exchange. Though I did it for you here \$\endgroup\$ – durron597 Mar 11 '14 at 14:45
  • \$\begingroup\$ Are external resources allowed? There are lookup tables that will allow you to simply look up each card in the hand and get a hand strength. \$\endgroup\$ – Kendall Frey Mar 11 '14 at 14:51
  • \$\begingroup\$ Can ace be low as well as high for straights? \$\endgroup\$ – Nick T Mar 11 '14 at 19:12
4
\$\begingroup\$

Ruby 353

This was based off of Chron's answer from the original question.

This takes input as command line arguments. Basically we just iterate over all combinations of size 5 to get what type of hand it is. Each hand type was modified so that is starts with a number. ("royal flush" -> "0royal 4flush", "high card" -> "9high card"). This allows us to sort strings that were returned. The first string after sorting is the best possible hand. So we print that after removing all numbers from the string.

o,p=%w(4flush 1straight)
f=/1{5}|1{4}0+1$/
puts $*.combination(5).map{|z|s=[0]*13;Hash[*z.map{|c|s['23456789TJQKA'.index c[0]]+=1;c[1]}.uniq[1]?[f,'5'+p,?4,'2four'+a=' of a kind',/3.*2|2.*3/,'3full house',?3,'6three'+a,/2.*2/,'7two pair',?2,'8one pair',0,'9high card']:[/1{5}$/,'0royal '+o,f,p+' '+o,0,o]].find{|r,y|s.join[r]}[1]}.sort[0].gsub(/\d/,'')
\$\endgroup\$
  • \$\begingroup\$ Nice. The gsub at the end can just be a sub right? \$\endgroup\$ – bazzargh Mar 12 '14 at 3:24
  • \$\begingroup\$ @bazzargh no it needs to remove all numbers. The code concatenates 4flush with 1straight or 0royal to get "0royal 4 flush" or "1straight 4flush". If we only use sub the 4 wouldn't be removed. \$\endgroup\$ – FDinoff Mar 12 '14 at 3:30
  • \$\begingroup\$ Gives the wrong result for AS QS JS TS 9S 5H 5D. That's gonna cost you a character! \$\endgroup\$ – user15244 Mar 17 '14 at 23:25
  • \$\begingroup\$ @WumpusQ.Wumbley Hmm this seems to be a bug in the original code. I'll try and figure out what the problem is later. \$\endgroup\$ – FDinoff Mar 18 '14 at 19:25
5
\$\begingroup\$

Haskell 618 603 598 525 512 504 480 464

Cards taken as a line of input. I think I've golfed this to death, but will easily be beaten by ruby etc using the same trick: if you generate all permutations, you get the forward sorts you want to look for straights, plus the reverse sorts you want for testing N of a kind.

import Data.List
m=map
z=take 5
q=m(\x->head[n|(f,n)<-zip"A23456789TJQK"[1..],f==x!!0])
l=m length
v=" of a kind"
w="flush"
y="straight"
c f s p r|f&&r="9royal "++w|f&&s='8':y++" "++w|f='5':w|s||r='4':y|True=case p of 4:_->"7four"++v;3:2:_->"6full house";3:_->"3three"++v;2:2:_->"2two pair";2:_->"1one pair";_->"0high card"
d x=c([5]==l(group$m(!!1)x))(q x==z[head(q x)..])(l$group$q x)$q x==1:[10..13]
k h=tail$maximum$m(d.z)$permutations$words h
main=interact k

Edited to inline "pair" and use number prefixes after seeing @FDinoff's entry, also composed map functions to shave one more char.

\$\endgroup\$
  • \$\begingroup\$ You can save yourself a couple characters (about 5 i think) if you get rid of u. "one pair","two pair" is shorter then u=" pair" ... "one"++u,"two++u \$\endgroup\$ – FDinoff Mar 12 '14 at 3:40
  • \$\begingroup\$ yup, I was just making that change after reading your code. Also the number prefix technique saves me another 5 \$\endgroup\$ – bazzargh Mar 12 '14 at 3:44
2
\$\begingroup\$

C++, 622 553 chars

four unnecessary newlines added below for clarity.

#include"stdafx.h"
#include"string"
std::string c=" flush",d=" of a kind",e="straight",z[10]={"high card","one pair","two pair","three"+d,e,c,"full house","four"+d,e+c,"royal"+c},
x="CDHSA23456789TJQK";char h[99];int main(){__int64 f,p,t,g,u,v,w,l=1,a=78517370881,b=a+19173960,i,j,q=0;gets_s(h,99);for(i=28;i-->7;){f=p=0;
for(j=7;j--;)if(j!=i%7&j!=(i+i/7)%7){f+=l<<x.find(h[j*3+1])*6;p+=l<<x.find(h[j*3])*3-12;}
v=p&b*2;u=v&v-1;w=p&p/2;g=p*64&p*8&p&p/8&p/64;f&=f*4;t=f&&p==a?9:f&&g?8:p&b*4?7:u&&w?6:f?5:g||p==a?4:w?3:u?2:v?1:0;
q=t>q?t:q;}puts(z[q].c_str());}

Things changed in the golfed version:

Rev 1: Changed all numeric variables to __int64 for single declaration.

Rev 1: Golfed increment and condition of for loops

Rev 0: Changed octal constants to decimal.

Rev 0: Changed if statements to assignments with conditional operator. Rev 1: Rearranged further into a single expression for t. This required new variable v for one of the intermediate values

Rev 0: Deleted verbose output. Only outputs the best overall hand.

Rev 0: Gave up on compressing the output text (difficult in C because you can't concatenate strings using the + operator.) Writing "flush" only once saved me 12 characters but cost me 15, making me 3 chars worse off overall. So I just wrote it 3 times instead. Rev 1: used std::string instead of char[] as suggested by FDinoff, making it possible to concatenate with +.

Ungolfed version, 714 noncomment nonwhitespace characters.

Loops through all 21 possible hands that can be made from 7 cards and rejects 2 cards each time. The suit and rank of the five chosen cards are totalised in variables f and p with a different octal digit for each suit/rank. Various bit operations are performed to determine the hand type, which is then stored in t (all 21 possibilities are output in the ungolfed version.) Finally the best possible hand is output.

#include "stdafx.h"
#include "string.h"

char x[] = "CDHSA23456789TJQK", h[99], z[10][99] = 
{ "high card", "one pair", "two pair","three of a kind", "straight","flush","full house","four of a kind","straight","royal" };

int main(void)
{
        int i,j,q=0;                  //i,j:loop counters. q:best possible hand of 7 card   
        scanf_s("%s/n", &h, 99); getchar();
        for (i = 7; i < 28; i++){

          //f,p: count number of cards of each suit (2 octal digits) and rank (1 octal digit.)
          //t: best hand for current 5 cards. g:straight flag. u,w: flags for pairs and 3's.   
          //l: constant 1 (64bit leftshift doesn't work on a literal.) 
          //octal bitmasks: a=ace high straight, b=general purpose

            __int64 f=0,p=0,t=0,g,u,w,l=1,a=01111000000001,b=a+0111111110;

           for (j = 0; j < 7; j++){
               if (j != i %7 & j != (i+i/7) %7){

                   f += l << (strchr(x,h[j*3+1])-x)*6;
                   p += l << (strchr(x,h[j*3])-x-4)*3;

                   printf_s("%c%c ",h[j*3], h[j*3+1]);
               }
           }

           w=p&b*2;                          //if 2nd bit set we have a pair
           if (w) t=1;
           u= w & w-1;                       //if there is only one pair w&w-1 evaluates to 0; +ve for 2 pair.
           if (u) t=2;
           w = p & p/2;                      // if 2nd and 1st bit set we have 3 of kind. 
           if (w) t=3;
           g = p*64 & p*8 & p & p/8 & p/64;  // detects all straights except ace high. pattern for ace high in a.
           if (g||p==a) t=4;
           f&=f*4;                           //for a flush we want 5 cards of the same suit, binary 101
           if (f) t=5;
           if (u&&w) t=6;                    //full house meets conditions of 2 pair and 3 of kind
           if (p & b*4) t=7;                 //four of a kind
           if (f && g) t=8;                  //straight flush
           if (f && p==a) t=9;               //royal flush
           printf_s("%s %s \n",z[t],t>7?z[5]:"");
           q=t>q?t:q;
        }   
        printf_s("%s %s",z[q],q>7?z[5]:"");
        getchar();
}

Ungolfed output

enter image description here

\$\endgroup\$
  • \$\begingroup\$ Since you say you are using c++ you could use <string> which does support + for string concatenation. Which then means you could probably use <iostream> and use cout However I don't actually know if any of those would lead to smaller character count. \$\endgroup\$ – FDinoff Mar 17 '14 at 0:20
  • \$\begingroup\$ @FDinoff what I could save: " pair flush flush straight of a kind"=35 chars. Once you add in #include savings are minimal, then you have to consider extra ",=+ and declarations of constants. Also I'm new to C++ and struggling with IDE and compiler settings (it forces me to use scanf_s and printf_s instead of the old "unsafe" versions and the help about fixing it goes round in circles.) cout might help a little, it's on my to do list, but probably for another program. The thing that kills cout for me is using namespace std I don't know if there's a way to avoid writing all that. \$\endgroup\$ – Level River St Mar 17 '14 at 1:01
  • \$\begingroup\$ You should almost never need printf and scanf since you are using c++. There are other (safer) was to do the same thing. You could use std::cout to get around the using namespace std \$\endgroup\$ – FDinoff Mar 17 '14 at 1:20
  • \$\begingroup\$ @FDinoff thx for the tip. In my latest edit I saved 18 bytes w different string handling: gets_s & puts, plus std::string to concatenate, which means I must convert to char* to output. The golf I posted works with just string or just iostream. Bizarrely I must include both to use <<>> operators with cin/cout & std::strings. Overall, using both #includes works out 5 bytes worse, even though i can declare h as a std::string and avoid a separate char declaration. Predictably, I cant find a list of what's in namespace std in help (or an explanation about the operator.) \$\endgroup\$ – Level River St Mar 22 '14 at 17:44
  • \$\begingroup\$ @FDinoff I agree, I wouldn't normally use scanf and gets, except for golfing, where programs are pretty unsafe anyway. I could shorten by 5 bytes -s,99 if I could use gets instead of gets_s, but I can't get the compiler to let me. What surprises me is how unsafe C/C++ is in general! A few weeks ago it would have shocked me to find that _int64 x=1<<y gives the wrong answer for y greater than 31. But now I'm just mildly annoyed. Having seen things with array subscripts going out of bounds with no error message, I've got used to it. Is there any way of turning better checking on? \$\endgroup\$ – Level River St Mar 22 '14 at 17:54
2
\$\begingroup\$

perl (>=5.14), 411 403 400 397 400

Edit: inlined a sub that was only called once, saving 8 chars.
Edit 2: removed a ."" that was left over from an early attempt
Edit 3: instead of a temp variable that preserves the original $_, use one to make it unnecessary. Net gain 3 chars.
Edit 4: fixed failure to detect over-full full house (2x 3-of-a-kind). cost 3 chars.

Not quite a winner, but the straight detector is an interesting concept, I think.

sub
j{join"",sort@_}sub
o{j(map{{A=>10}->{$_},11+index(j(2..9).TJQKA,$_)}$h=~/(.(?=@_))/g)=~/.*(..)(??{j
map$1+$_.'.*',1..4})/?$1:()}$h=$_=<>;if(j(/(\S)\b/g)=~/(.)\1{4}/){$w=$_==19?royal:straight
for
o$f=$1}$_=j(/\b(\S)/g)=~s/(.)\1*/length$&/rge;$k=" of a kind";print$w?"$w flush":/4/?four.$k:/3.*2|[23].*3/?"full house":$f?flush:(o".")?straight:/3/?three.$k:/2.*2/?"two pair":/2/?"one pair":"high card"

Expanded version:

# We'll be doing a lot of sorting and joining
sub j {
  return join "", sort @_;
}

# r() expects $_ to contain a rank, and converts it to a numeric code. The
# code starts at 10 so the numbers will sort correctly as strings, and a list
# of 2 values is returned because A is both 10 and 23. All other ranks have
# undef as the first value and their proper 11..22 value as the second value.
sub r {
  return ({A=>10}->{$_}, 11+index(j(2..9).TJQKA,$_));
}

# Sequence-detector. Factored into a sub because it's run twice; once over
# the ranks in the flush suit to find a straight flush and once over all the
# ranks to find a straight. On successful match, returns the lowest rank of
# the straight (in the 10..23 representation).
# Required parameter: the suit to search, or "." for all suits.
sub o {
  j(map r,$h=~/(.(?=@_))/g)          # The list of ranks, in increasing order,
                                     # with ace included at both ends...
    =~                               # ...is matched against...
  /.*(..)(??{j map$1+$_.'.*',1..4})/ # ...a pattern requiring 5 consecutive
                                     # numbers.
  ?$1:()
  # A note about this regexp. The string we're matching is a bunch of numbers
  # in the range 10..23 crammed together like "121314151619" so you might
  # worry about a misaligned match starting on the second digit of one of the
  # original numbers. But since that would make every pair of digits in the
  # match end with a 1 or a 2, there's no way 5 of them will be consecutive.
  # There are no false matches.
  # Another note: if we have a royal flush and also have a 9 in the same
  # suit, we need to return the T rank, not the 9, which is why the regexp
  # starts with a .*
}

# Read a line into $_ for immediate matching with /.../ and also save it into
# $h because $_ will be clobbered later and we'll need the original string
# afterwards.
$h = $_ = <>;

if(j(/(\S)\b/g) =~ /(.)\1{4}/) { # flush detector: sorted list of all suits
                                 # contains 5 consecutive identical chars
  # $f=$1 comes first, so $f will be true later if there's a flush.
  # Then o() is called with the flush suit as arg to detect straight flush.
  # If there's no straight flush, o() returns the empty list and for loop
  # runs 0 times, so $w is not set. If there is a straight flush, the return
  # value of o() is compared to 19 to detect royal flush.
  $w = ($_==19 ? "royal" : "straight")
    for o($f=$1);
}

$_ =
  j(/\b(\S)/g)                 # Get the sorted+joined list of ranks...
    =~ s/(.)\1*/length $&/rge; # ... and turn it into a list of sizes of
                               # groups of the same rank. The /r flag
                               # requires perl 5.14 or newer.

print
  $w             ? "$w flush" :
  /4/            ? "four of a kind" :
  /3.*2|[23].*3/ ? "full house" :
  $f             ? "flush" :
  (o".")         ? "straight" :
  /3/            ? "three of a kind" :
  /2.*2/         ? "two pair" :
  /2/            ? "one pair" :
                   "high card"
\$\endgroup\$
1
\$\begingroup\$

JavaScript 600

usage with nodeJS: node code.js "7H 3S 7S 7D AC QH 7C"

function a(o){s="";for(k in o)s+=o[k];return s}
b=process.argv[2]
c={S:0,H:0,D:0,C:0}
v={A:0,K:0,Q:0,J:0,T:0,"9":0,"8":0,"7":0,"6":0,"5":0,"4":0,"3":0,"2":0}
d=b.split(" ")
for(i=d.length;i--;){e=d[i];c[e[1]]++;v[e[0]]++}
c=a(c);v=a(v)
f=g=h=j=k=l=m=false
if((st=c.indexOf(5))!=-1)g=!g
if(v.match(/[1-9]{5}/))h=!h
if(st==0)f=!f
if(v.indexOf(4)!=-1)j=!j
if(v.indexOf(3)!=-1)k=!k
if(n=v.match(/2/g))if(n)if(n.length>=2)m=!m;else l=!l
p=" of a kind"
q="Flush"
r="Straight"
console.log(f&&g?"Royal "+q:h&&g?r+" "+q:j?"Four"+p:k&&(l||m)?"Full House":g?q:h?r:k?"Three"+p:m?"Two pairs":l?"Pair":"High card")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.