Hold'em or Fold'em?

17

1

Your friend invited you to a high-stakes poker game at the last minute and, as a computer scientist, you've decided to use your skills to gain an edge on the game. Your task will be, given 2 cards (your hand) and 0, 3, 4 or 5 cards (the dealt cards), you must decide what the best possible hand you can get will be. If all 7 cards are given as arguments, the answer is pretty clear. If given less, the problem becomes more complex. However, this isn't quite enough to give you the edge you're looking for, you must also calculate the best possible hand from the remaining cards to understand what your opponents could have as well.


Hold'em Refresher

If you don't know about hold'em, each player in the game starts with 2 cards as their 'hand'. Over the course of 3 'turns', additional cards are revealed to be shared between all players. The first turn, 3 cards are revealed. The second, 1 more, and the third time a final card is revealed. The two cards given first represent your hand, while the latter represents 0, 3, 4, or 5 cards given by the successive turns.


Possible Numbers:

[2,3,4,5,6,7,8,9,T(10),J,Q,K,A]

Possible Suits:

[S,C,H,D]

Full Deck:

[2S,3S,4S,5S,6S,7S,8S,9S,TS,JS,QS,KS,AS, # Spades.
 2C,3C,4C,5C,6C,7C,8C,9C,TC,JC,QC,KC,AC, # Clubs.
 2H,3H,4H,5H,6H,7H,8H,9H,TH,JH,QH,KH,AH, # Hearts.
 2D,3D,4D,5D,6D,7D,8D,9D,TD,JD,QD,KD,AD] # Diamonds.

Hand Rankings:

1:Royal Flush    (A-K-Q-J-10, all from the same suit).
2:Straight Flush (Sequential cards, all from the same suit).
3:Four-of-a-Kind (Self explanatory).
4:Full House     (3-of-a-kind and a 2-of-a-kind).
5:Flush          (All cards are from the same suit).
6:Straight       (Sequential Cards, any suits).
7:3-of-a-Kind    (Self explanatory).
8:2-Pair         (Double 2-of-a-Kind).
9:Pair           (2-of-a-Kind).
10:High Card     (You have absolutely nothing except a single card).

Lets take an example or two and walk through them:

The simple example:

[AS, AC],[AH,AD,9S,9C,9H] -> 3 (Four-of-a-Kind), 3 (Four-of-a-Kind)

The best possible hand you'll have in this setup is the four of a kind hand. The best possible hand your opponents could have is also a 4-of-a-Kind, as you cannot have a KQJ10 in their 2-card hands.


[5C,2C],[6C,4C,JH,JD] -> 2 (Straight Flush), 3 (4-of-a-Kind)

You're at risk for a straight flush, but because you have the 2/5C in your hand, nobody else is due to you holding both middle cards. The best they can hope for is having 2 pocket Jacks and getting a Jack on the flop.


[JS,JC],[] -> 1 (Royal Flush), 1 (Royal Flush)

No information you can use against them has been given, all you can tell at the moment is that they can only have a royal flush in diamonds/hearts, but it's just as possible that you get the royal flush as they do. In fact, all inputs where the flop has yet to happen should result in a 1-1 answer.


[2C,4S],[3C,7S,9D,AH,JD] -> 10 (High Card), 7 (3-of-a-Kind)

This is an example of where you're absolutely screwed, and there's no possibility of a straight or a flush given the river. Meaning the best hand here is pocket aces resulting in a 3-of-a-Kind.


I/O Requirements

  • Input must be separated between what is in your hand and what is public knowledge; it'll likely be easier this way regardless of implementation.
    • Cards can be tuples or strings, up to you.
    • The Hand and the Playing Field can be arrays or delimited strings.
  • Output must be two indexes from the list of hands I've provided (E.G. [2,1]).
    • It may be returned as part of a function, printed to console or output in a suitable manner.
    • There must be two distinct values, one for your best hand, another for the best possible.
  • 10 may be represented as a T or as 10, whichever makes sense for you.
  • Standard loopholes are disallowed.

Winning Criteria

  • This is , lowest byte-count with a tie-breaker of post date.

Magic Octopus Urn

Posted 2017-01-31T19:51:55.597

Reputation: 19 422

It's superfluous, and I removed it. It doesn't really add anything, and most people comment below anyway. – Rɪᴋᴇʀ – 2017-01-31T20:20:59.450

2Shouldn't there only be one of A and 1 in the possible allowed cards? Also I don't think there is really a compelling reason to require the face abbreviations over numeric values like 11. – FryAmTheEggman – 2017-02-01T01:34:30.683

9I've never seen a deck with an A and a 1. Everything else looks good. – isaacg – 2017-02-01T03:58:37.250

1For us non-Poker-players, please explain in the question that the second group of cards are shared between you and your opponent, whereas they have their own two cards that you can't see. A quick glossary defining the terms pocket, flop, and river would be helpful. – DLosc – 2017-02-01T04:08:31.897

1Also helpful: explaining the whole sequence of a round. (Each player starts with two cards known only to them, then three cards are dealt face-up, then a fourth, then a fifth, at which point each player forms a "hand" from any five cards out of the seven visible to them.) It might not be clear to someone why there are seven cards but a hand consists of five. – DLosc – 2017-02-01T04:13:09.250

1In your second example (i.e. [10H,10C],[AH,9C,JC]), shouldn't it output 2(Straight Flush), 2(Straight Flush) ? Imagine your opponent has [8C,7C] in his hand, then the flop might be [6C] and the river [5C], which would give him the following straight flush : [5C,6C,7C,8C,9C]. – roberto06 – 2017-02-01T08:43:43.797

Fixed all of those problems, jeez... Thanks sandbox Obama. – Magic Octopus Urn – 2017-02-01T14:16:10.187

1The player can't have a Straight Flush anymore in your 2nd example. (Even with QC at the river, he will still need 8C or KC.) – Arnauld – 2017-02-02T07:54:14.853

@Arnauld man, I can't even do examples right, this problem will be hard to program haha. – Magic Octopus Urn – 2017-02-02T14:11:50.453

[2C,4S],[3C,7S,9D,AH,JD] should be (10,6) since the other player can get a straight with an 8 and a 10 – vroomfondel – 2017-06-21T16:11:39.497

Answers

3

Haskell, 433 430 425 bytes

-5 bytes thanks to @Laikoni

import Data.List
q="23456789TJQKA"
e=elem
l=length
b=map
r p|z,elem 'A'u,elem 'K'u=1|z=2|e 4t=3|v<3=4|w=5|y=6|e 3t=7|v<4=8|v<5=9|1>0=10where u=[n!!0|n<-p];v=l$nub u;t=b(\n->l[x |x<-u,x==n])q;w=all(==(last$p!!0))[last s|s<-p];y=elem""[u\\s|s<-b(take 5.flip drop('A':q))[0..10]];z=y&&w
0%_=[[]]
n%(x:y)=b(x:)((n-1)%y)++n%y
_%_=[]
h#t|let p=h++t;c i=minimum$b r$concat$b(5%)$b(++i)((7-l i)%([n:[s]|n<-q,s<-"SCHD"]\\p))=(c p,c t)

Try it online!

Ungolfed (same idea, slightly different structure):

import Data.List -- for (\\)
numbers = "23456789TJQKA"

e=elem

rank_hand hand
    |royal_flush=1
    |straight_flush=2
    |four_of_a_kind=3
    |full_house=4
    |flush=5
    |straight=6
    |three_kind=7
    |two_pair=8
    |pair=9
    |1>0=10
    where nums = [head n | n<-hand]
          unique = length $ nub nums
          counts = map (\n->length [x | x<-nums, x==n]) numbers
          pair = unique < 5
          two_pair = unique < 4 -- could also be 3 of a kind, but that's ok
          three_kind = e 3 counts
          flush = all (==(last$hand!!0)) [last s|s<-hand]
          straight = elem "" [nums\\s | s <- map (take 5.flip drop ('A':numbers))[0..10]]
          full_house = unique < 3
          four_of_a_kind = e 4 counts
          straight_flush = straight && flush
          royal_flush = straight_flush && elem 'A' nums && elem 'K' nums

-- taken from https://codegolf.stackexchange.com/a/34496/66460
-- k%l finds combinations of size k from a list l
0%_=[[]]
n%(x:y)=map(x:)((n-1)%y)++n%y
_%_=[]

-- find every combination available to each player, and rank each one. 
-- could be golfed a lot more.
h#t=let p=h++t
        a=[n:[s]|n<-numbers,s<-"SCHD"]\\p
        c i=minimum $ map rank_hand $ concat $ map (5%) $ map (++i) ((7-length i)%a)
    in(c p,c t)

Very slow, since there's no special casing at all (e.g. if no cards have been shown, a royal flush is always possible.) Most of my golfing effort went into the rank_hand function; # can be golfed a lot more by combining maps and such.

hand#table calculates the optimal scores for you and your opponent. Does no error checking.

vroomfondel

Posted 2017-01-31T19:51:55.597

Reputation: 2 094

I think you forgot to s/elem/e/g after defining e=elem, so that should save you 9ish bytes. I think you can also remove some spaces, particularly where identifiers follow numbers directly – Julian Wolf – 2017-06-21T17:29:16.270

@JulianWolf I'm still new to Haskell, but for some reason it looks like e=elem inferred type Int->Bool so it wasn't compiling when I used e for non-integer elem calls. I'm trying to figure out why what was. Thanks for the tip on the spaces! – vroomfondel – 2017-06-21T18:22:32.123

Ah, you're right. Haskell has trouble inferring polymorphic types sometimes—not sure off the top of my head whether there's an easy fix, beyond adding the flexible types flag – Julian Wolf – 2017-06-21T18:26:33.023

1h#t=let[...]in[...] can be shortened to h#t|let[...]=[...]. Also head n is n!!0. In a guard && can simply be ,. – Laikoni – 2017-06-21T22:18:07.890