22
\$\begingroup\$

Related: Name the poker hand

A straight flush is a poker hand containing five cards of sequential rank, all of the same suit. As part of a straight flush, an ace can rank either above a king or below a two. An ace can rank either high (e.g. A♥ K♥ Q♥ J♥ 10♥ is an ace-high straight flush) or low (e.g. 5♦ 4♦ 3♦ 2♦ A♦ is a five-high straight flush), but cannot rank both high and low in the same hand (e.g. Q♣ K♣ A♣ 2♣ 3♣ is an ace-high flush, not a straight flush).

Challenge

Given N cards (in any reasonable format) output a truthy value if a straight flush is contained in the poker hand.

Input

  • N numbers of cards. (In any reasonable format)

There are four suits; hearts, spades, diamonds, and clubs (H, S, D, C).

Each suit has one card for the numbers 2 to 10, plus 4 'picture' cards, Ace, Jack, Queen, and King (A, J, Q, K)

Note: You can take 10 as T

Output

  • Truthy/Falsy value

Test case

["AS", "2S", "3S", "4S", "5S"] => true

["3D", "9C", "4S", "KH", "AD", "AC"] => false

["5D", "6D", "7D", "8H", "9D", "10D", "JD"] => false

["JC", "7C", "5D", "8C", "AC", "10C", "9C", "5S"] =>true

[] => false

["AS", "2S", "3S"] => false

["JC", "QC", "KC", "AC", "2C"] => false

[ "2H", "3H", "4H", "5H", "6H", "7H"] => true

Standard rules apply.

Winning criteria: Shortest code in each lang

\$\endgroup\$
6
  • 1
    \$\begingroup\$ May we assume that there won't be two of the same card in the hand? \$\endgroup\$
    – Jo King
    Commented Jul 26, 2018 at 23:52
  • \$\begingroup\$ @JoKing yep, you wont have the same card twice or more \$\endgroup\$ Commented Jul 27, 2018 at 0:48
  • 4
    \$\begingroup\$ May we take 10 as T? \$\endgroup\$ Commented Jul 27, 2018 at 7:13
  • \$\begingroup\$ @JoKing I don't think that can happen IRL. ;-) \$\endgroup\$ Commented Jul 27, 2018 at 9:08
  • 4
    \$\begingroup\$ @EriktheOutgolfer I literally have about 5 packs of mixed cards less then a meter away from me \$\endgroup\$
    – Jo King
    Commented Jul 27, 2018 at 10:04

13 Answers 13

16
\$\begingroup\$

Python 2, 95 bytes

lambda a:any(set('A234567891JQKA'[i/4:][:5])<={r['HCSD'[i%4]in r]for r in a}for i in range(40))

Try it online!

There are 40 possible straight flushes, and this simply checks them all. Chas Brown saved 2 bytes; Jo King saved 4 more.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ There are 40, you used A at both ends so I believe changing 36 to 40 should fix it. \$\endgroup\$ Commented Jul 26, 2018 at 21:46
  • \$\begingroup\$ Oops, aren't I good at counting. I fixed it! \$\endgroup\$
    – lynn
    Commented Jul 27, 2018 at 1:27
  • \$\begingroup\$ 99 bytes. \$\endgroup\$
    – Chas Brown
    Commented Jul 28, 2018 at 20:36
  • \$\begingroup\$ Swap the suit-value order and move the if condition to the index? \$\endgroup\$
    – Jo King
    Commented Jul 29, 2018 at 12:48
  • \$\begingroup\$ 95 bytes \$\endgroup\$
    – Jo King
    Commented Jul 29, 2018 at 23:18
9
\$\begingroup\$

R, 128 126 94 91 bytes

function(x,r=rle(outer(y<-chartr("J-X","A2-9TJQKAS",LETTERS),y,paste0)%in%x))any(r$l>4&r$v)

Try it online!

Original logic shortened considerably by @J.Doe.

Makes a 26 by 26 matrix with mostly nonsense but all the cards (with the Aces repeated at the bottom) contained in rows 10 to 23 of columns 3,4,8 and 24. The matrix is created by concatenating all combinations of the upper case alphabet with letters J through X replaced by A,2-9,T,J,Q,K,A,S via chartr. We get C, D, H for free!

The %in% flattens the matrix column-wise into a vector. Then see if run-length encoding is greater than 4 for any run of TRUE matches.

\$\endgroup\$
3
  • \$\begingroup\$ Clever use of rle AND outer ! This saves two bytes \$\endgroup\$
    – JayCe
    Commented Jul 26, 2018 at 21:06
  • \$\begingroup\$ 94 bytes. Two changes: using a symmetric outer call that produces many invalid cards, and using the vector coercion of in to avoid apply. They both have to be in place for this to work! \$\endgroup\$
    – J.Doe
    Commented Sep 3, 2018 at 8:54
  • 3
    \$\begingroup\$ Very nice! Changed the answer and made it a community wiki. \$\endgroup\$
    – ngm
    Commented Sep 4, 2018 at 13:24
6
\$\begingroup\$

JavaScript (ES6), 116 bytes

a=>[...'CDHS'].some(s=>a.map(c=>m|=c.match(s)&&2<<"234567891JQKA".search(c[0]),m=0)|(g=k=>k&&1+g(k&k/2))(m|m>>13)>4)

Try it online!

How?

For each suit \$s\$, we convert all cards \$c\$ of suit \$s\$ into a 14-bit bitmask \$m\$, duplicate bit #13 (Ace) to bit #0 to handle the steel wheel (A,2,3,4,5) and count the number of consecutive bits. If it's greater than 4, we have a straight flush.

\$\endgroup\$
1
  • 6
    \$\begingroup\$ I've gotten so used to your "currying notation" introductory line that I miss it when it's not needed. \$\endgroup\$
    – ngm
    Commented Jul 26, 2018 at 20:56
4
\$\begingroup\$

Brachylog, 31 bytes

tᵍkᵐ²cᵐ{ps₅~s"A23456789TJQKA"}ᵉ

Try it online!

 ᵍ                    Group input by
t                     each element's "tail" (i.e. suit)
kᵐ²                   Knife off the suit character from each element in each array
cᵐ                    Concatenate the elements of each suit array into a string
{               }ᵉ    There exists at least one string in that such that
 p                    it has a permutation
 s₅                   which has a substring of length 5
 ~s                   which is also a substring of
 "A23456789JQKA"
\$\endgroup\$
3
\$\begingroup\$

Retina 0.8.2, 66 bytes

J
11
Q
12
K
13
A
1$%'¶14
\d+(.)
$1$&$*
O`
^
¶
((?(1)\1.|¶.+)){5}\b

Try it online! Explanation:

J
11
Q
12
K
13

Convert the picture cards into their values.

A
1$%'¶14

A can be 1 or 14.

\d+(.)
$1$&$*
O`

Convert the value to unary, and suffix it so that the cards sort properly.

^
¶
((?(1)\1.|¶.+)){5}\b

Match 5 cards that increase by 1 each time, and ensure that the last increase was exactly 1.

\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES6), 106 bytes

h=>h.map(([r,s])=>[..."HSDCA23456789TJQKA"].map(c=>i+=c==s?i*15:c==r?d[i]=1:1,i=0),d=[])|/(,1){5}/.test(d)

Accepts an array of string representations of cards, replacing 10 with T. Try it online!

Explanation

Iterates over each card and sets a flag in an array of booleans using an index computed from the unique combination of its rank and suit. This array is then stringified to permit matching a pattern of 5 consecutive truthy values.

For example, a hand with a straight flush may produce the following as a substring of the full string representation of the boolean array: ,,,,1,1,1,1,1,,,,

Because the first rank value (i.e. A) is offset from the start of the string, there will always be empty values preceding all 1's in the array, ensuring the string representation will begin with a ,

h =>
    h.map(([r, s]) =>                         // destructure card value, e.g. "JH" => ["J", "H"]
        [..."HSDCA23456789TJQKA"].map(c =>    // mapping accounts for both positions of 'A'
            i +=                              // increment index value
            c == s                            // if found index of suit...
                ? i * 15                      // buffer so that cards from different suits cannot be confused
            : c == r                          // if found index of rank...
                ? d[i] = 1                    // set flag to denote card is in hand
            : 1,
            i = 0
        ),
        d = []
    ) |
    /(,1){5}/.test(d)                         // implicitly converts to string joined with a ,
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Nice. This deserves more votes, but folks tend to lose interest in challenges a couple days after the initial posting. \$\endgroup\$ Commented Jul 30, 2018 at 20:22
2
\$\begingroup\$

Java 10, 189 167 165 164 160 157 156 bytes

s->{int i=10;for(;i-->0;)i=s.matches("AKQJT98765432A".substring(i,i+5).replaceAll(".","(?=.*$0\\\\1)").replaceFirst(".1","([HSDC])")+".*")?-2:i;return-1>i;}

Takes the input as a single space-delimited String (i.e. "AS 2S 3S 4S 5S").

-22 bytes thanks to @OlivierGrégoire.
-1 byte thanks to @AlexRacer.

Try it online.

Golfed version of the code that I've used for Project Euler #54, which I primarily did with regexes (for fun and to learn more about regexes). Without regexes it would probably have been better for performance and easier (probably also applies for golfing this answer; will take a look later on).

Explanation:

s->{                    // Method with String parameter and boolean return-type
  int i=10;for(;i-->0;) //  Loop `i` in the range (10,0]:
    i=s.matches(        //   If the input matches the following regex:
        "AKQJT98765432A".substring(i,i+5)
                        .replaceAll(".","(?=.*$0\\\\1)")
                        .replaceFirst(".1","([HSDC])")
                        //    Five adjacent cards
        +".*")?         //    With optionally zero or more other characters
         -2             //     Set `i` to -2, which also stops the loops at the same time
      :i;               //   Else: leave `i` unchanged to continue
  return-1>i;}          //  Return whether `i` is not -2 (so whether the loop has finished)

Additional regex explanation:

  • "AKQJT98765432A".substring(i,i+5) takes five adjacent cards based on i
  • .replaceAll(".","(?=.*$0\\\\1)") replaces each of those cards with "(?=.*c\\1)" (where c is the card-character)
  • .replaceFirst(".1","([HSDC])") will then replace the first \\1 with ([HSDC]).

I.e. the total regex to check the Straight Flush for cards in the value-range [9,5] will become:
^(?=.*9([HSDC]))(?=.*8\\1)(?=.*7\\1)(?=.*6\\1)(?=.*5\\1).*$
(NOTE: String#matches implicitly adds the trailing/leading ^...$ to check the entire String.) This regex will:

^(?=.*9([HSDC]))(?=.*8\\1)(?=.*7\\1)(?=.*6\\1)(?=.*5\\1).*$
^                                                         $ Match the entire string
 (?=           )(?=      )(?=      )(?=      )(?=      )    Do positive lookaheads to check
                                                            each card
    .*             .*        .*        .*        .*         With optional leading characters
                                                            in front of every card
                                                        .*  And any trailing characters at
                                                            the end of the entire hand
      9              8         7         6         5        The five adjacent values
        [HSDC]                                              With a suit
       (      )       \\1       \\1       \\1       \\1     which is the same for all cards
\$\endgroup\$
5
  • 1
    \$\begingroup\$ 172 bytes. I only golfed the regex generation: it's still your algorithm. \$\endgroup\$ Commented Jul 27, 2018 at 14:48
  • 1
    \$\begingroup\$ 167 bytes. I removed the unnecessary ".*"+ prefix. \$\endgroup\$ Commented Jul 27, 2018 at 14:53
  • 1
    \$\begingroup\$ @OlivierGrégoire Thanks! Nice golfs. \$\endgroup\$ Commented Jul 27, 2018 at 15:05
  • 1
    \$\begingroup\$ -1 byte if you break out of loop instead of using f \$\endgroup\$
    – AlexRacer
    Commented Sep 1, 2018 at 16:31
  • 1
    \$\begingroup\$ @AlexRacer Smart, thanks! And been able to golf 2 more bytes by changing the break to i=-2 and the return to return-1>i; using your approach (and 2 more changing (.) to . and $1 to $0). :) \$\endgroup\$ Commented Sep 1, 2018 at 17:30
1
\$\begingroup\$

Clean, 145 135 bytes

import StdEnv,Data.List
?l=or[isInfixOf(map hd h)['A234567891JQKA']\\a<-l,b<-l,c<-l,d<-l,e<-l,h<-[[a,b,c,d,e]]|tl(nub(map last h))==[]]

Try it online!

Simplified:

? l                                             // function ? taking argument l
  = or [                                        // is at least one of these true
        isInfixOf (map hd h) ['A234567891JQKA'] // do the first characters of a hand appear in this string, in order
        \\ a <- l                               // loop level 1, assigns `a`
           , b <- l                             // loop level 2, assigns `b`
             , c <- l                           // loop level 3, assigns `c`
               , d <- l                         // loop level 4, assigns `d`
                 , e <- l                       // loop level 5, assigns `e`
                   , h <- [[a,b,c,d,e]]         // trick to assign `h`, because it's cheaper than let .. in ..
        | tl (nub (map last h)) == []           // only take the loop iterations where all the suits are the same
       ]
\$\endgroup\$
1
\$\begingroup\$

Japt, 37 bytes

Takes input as a 2D array.

"AJQKA"i1Aò2 q)øUñÌòÏ̦XÌÃËmάú5 á5Ãc

Try it


Explanation

"AJQKA"                                   :String literal
       i1                                 :Insert at (0-based) index 1
         Aò2                              :  Range [2,10]
             q                            :  Join
              )                           :End insert
               ø                          :Does that string contain any element in the following array?
                U                         :Input
                 ñ                        :Sort
                  Ì                       : By last element (grouping suits together)
                   òÏ                     :Partition between X & Y where
                     Ì                    :  Last element of Y
                      ¦                   :  Does not equal
                       XÌ                 :  Last element of X
                         Ã                :End partition
                          Ë               :Map
                           m              :  Map
                            Î             :   First elements (card values)
                             ¬            :  Join
                              ú5          :  Right pad with spaces to length 5
                                 á5       :  Permutations of length 5
                                   Ã      :End map
                                    c     :Flatten
\$\endgroup\$
0
\$\begingroup\$

Jelly, 18 bytes

Ṣœc5Uµ13R;1wṪ€ȧEµƇ

Try it online!

Actual input format: list ([..., ...]) of lists of 2 integers each; the first is an integer in \$[1,13]\$, for \$A23456789TJQK\$ respectively, and the second is in \$[1,4]\$, for \$CDHS\$ respectively. The TIO link accepts input in the format of the test cases.

Output format: empty list as falsy, non-empty list as truthy.

\$\endgroup\$
2
  • \$\begingroup\$ I don't see anything in the specs suggesting that integers can be substituted for the suits and picture cards - did I miss something? \$\endgroup\$
    – Shaggy
    Commented Jul 27, 2018 at 11:37
  • \$\begingroup\$ @Shaggy I assume that's within "any reasonable format", I don't think we have defaults regarding inputting playing cards. \$\endgroup\$ Commented Jul 27, 2018 at 14:49
0
\$\begingroup\$

PHP, 264 bytes

It echos 1 if it is a straight flush and 0 or null if not.

If you name the file 1X then you can save 11 bytes as you don't need to change $argv[0]. Not sure at the moment why the filename can break it.

For some reason the strings :;<=> get sorted before the strings 0123456789 by asort in TIO even though :;<=> have ASCII values 58-62 and 0123456789 have ASCII values 48-57. So if you take the code from the TIO link or below and use PHPTester with the following test suite it works.

$argb[0] = [".code.tio", "AS", "2S", "3S", "4S", "5S"]; // => true
$argb[1] = [".code.tio", "3D", "9C", "4S", "KH", "AD", "AC"]; // => false
$argb[2] = [".code.tio", "5D", "6D", "7D", "8H", "9D", "TD", "JD"]; // => false
$argb[3] = [".code.tio", "JC", "7C", "5D", "8C", "AC", "TC", "9C", "5S"]; // => true
$argb[4] = [".code.tio", ]; // => false
$argb[5] = [".code.tio", "AS", "2S", "3S"]; // => false
$argb[6] = [".code.tio", "JC", "QC", "KC", "AC", "2C"]; // => false
$argb[7] = [".code.tio", "TC", "JC", "QC", "KC", "AC", "2C"]; // => true
$argb[8] = [".code.tio", "2H", "3H", "4H", "5H", "6H", "7H"]; // => true

for ($z=0; $z<9;$z++){
    $argv=$argb[$z];
    array_shift($argv);
    unset($a,$b,$c,$d,$e,$f,$g,$h,$i);
    $f=false; // not needed, just removes several notices

    // TIO code here

    echo "<br>";

TIO Code

for($b=count($a=$argv);$b;){$a[0]='1X';$a[--$b]=strtr($a[$b],'ATJQK','1:;<=');$a[]=($a[$b][0]==1?">".$a[$b][1]:1);}asort($a);foreach($a as$c){$d[$c[1]][]=$c[0];}foreach($d as$e){if(4<$g=count($e)){for($h=0;$g>$i=4+$h;){$f|=(ord($e[$i])-ord($e[$h++])==4);}}}echo$f;

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Kotlin, 226 bytes

Used T for 10 so all cards are 2 characters in length.

{h:List<String>->val m=List(4){mutableSetOf<Int>()}
for(c in h)m["CDHS".indexOf(c[1])].add("A23456789TJQK".indexOf(c[0]))
var r=0>1
for(b in m){if(b.contains(0))b.add(13)
for(i in 0..9)r=b.containsAll((i..i+4).toList())||r}
r}

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Pascal (FPC), 223 216 210 209 bytes

var a,b:char;c:set of byte;i:byte;begin repeat readln(a,b);i:=pos(b,'HDC')*14+pos(a,'23456789TJQK');c:=c+[i];if a='A'then c:=c+[i+13]until eof;i:=0;while not([i..i+4]<=c)or(i mod 14>9)do i:=i+1;write(i<52)end.

Try it online!

Uses T for 10. Input contains 1 card per line.

Now I golfed it so much that I don't know how it works anymore...

Explanation:

var a,b:char; //for reading cards
    c:set of byte; //this set is for remembering which cards are present in the input
                   //14 numbers used for each suit
    i:byte;
begin
  repeat
    readln(a,b);             //read rank into a, suit into b and a newline
    i:=pos(b,'HDC')*14+pos(a,'23456789TJQK');
        //temporary use i to calculate corresponding number for the card
        //pos() gives 0 if b is not found
        //1st pos() is for the group of numbers for that suit, 2nd pos() is for offset
    c:=c+[i];                //include i into set
    if a='A'then c:=c+[i+13] //if rank is A, include the number at the end of group as well
  until eof;
  i:=0;
  while not(
    ([i..i+4]<=c) //if NOT 5 cards in a row are present...
    and           //while the check is started from 10 (T)...
    (i mod 14<10) //(otherwise, it is checking across 2 different suits)
  )do i:=i+1;     //increment i, otherwise stop
  write(i<52) //if i<=51, there is a straight flush starting at the card corresponding to i
              //(if there isn't a straight flush, i stops at 252 due to i..i+4, I don't know why)
end.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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