0
/* --------------------------------------------------------------
   Contains method to evaluate the strength of Poker hands

   I made them as STATIC (class) methods, because they 
   are like methods such as "sin(x)", "cos(x)", that
   evaulate the sine, cosine of a value x

   Input of each method:

     Card[] h;  (5 Cards)

   Output of each method:

     An integer value represent the strength
     The higher the integer, the stronger the hand
   -------------------------------------------------------------- */
public class Poker
{
   public static final int STRAIGHT_FLUSH = 8000000; 
                                             // + valueHighCard()
   public static final int FOUR_OF_A_KIND = 7000000; 
                                             // + Quads Card Rank
   public static final int FULL_HOUSE     = 6000000; 
                                             // + SET card rank
   public static final int FLUSH          = 5000000;  
                                             // + valueHighCard()
   public static final int STRAIGHT       = 4000000;   
                                             // + valueHighCard()
   public static final int SET            = 3000000;    
                                             // + Set card value
   public static final int TWO_PAIRS      = 2000000;     
                                             // + High2*14^4+ Low2*14^2 + card
   public static final int ONE_PAIR       = 1000000;      
                                             // + high*14^2 + high2*14^1 + low



   /***********************************************************
     Methods used to determine a certain Poker hand
    ***********************************************************/

   /* --------------------------------------------------------
      valueHand(): return value of a hand
      -------------------------------------------------------- */
   public static int valueHand( Card[] h )
   {
      if ( isFlush(h) && isStraight(h) )
         return valueStraightFlush(h);
      else if ( is4s(h) )
         return valueFourOfAKind(h);
      else if ( isFullHouse(h) )
         return valueFullHouse(h);
      else if ( isFlush(h) )
         return valueFlush(h);
      else if ( isStraight(h) )
         return valueStraight(h);
      else if ( is3s(h) )
         return valueSet(h);
      else if ( is22s(h) )
         return valueTwoPairs(h);
      else if ( is2s(h) )
         return valueOnePair(h);
      else
         return valueHighCard(h);
   }


   /* -----------------------------------------------------
      valueFlush(): return value of a Flush hand

            value = FLUSH + valueHighCard()
      ----------------------------------------------------- */
   public static int valueStraightFlush( Card[] h )
   {
      return STRAIGHT_FLUSH + valueHighCard(h);
   }

   /* -----------------------------------------------------
      valueFlush(): return value of a Flush hand

            value = FLUSH + valueHighCard()
      ----------------------------------------------------- */
   public static int valueFlush( Card[] h )
   {
      return FLUSH + valueHighCard(h);
   }

   /* -----------------------------------------------------
      valueStraight(): return value of a Straight hand

            value = STRAIGHT + valueHighCard()
      ----------------------------------------------------- */
   public static int valueStraight( Card[] h )
   {
      return STRAIGHT + valueHighCard(h);
   }

   /* ---------------------------------------------------------
      valueFourOfAKind(): return value of a 4 of a kind hand

            value = FOUR_OF_A_KIND + 4sCardRank

      Trick: card h[2] is always a card that is part of 
             the 4-of-a-kind hand
         There is ONLY ONE hand with a quads of a given rank.
      --------------------------------------------------------- */
   public static int valueFourOfAKind( Card[] h )
   {
      sortByRank(h);

      return FOUR_OF_A_KIND + h[2].rank();
   }

   /* -----------------------------------------------------------
      valueFullHouse(): return value of a Full House hand

            value = FULL_HOUSE + SetCardRank

      Trick: card h[2] is always a card that is part of
             the 3-of-a-kind in the full house hand
         There is ONLY ONE hand with a FH of a given set.
      ----------------------------------------------------------- */
   public static int valueFullHouse( Card[] h )
   {
      sortByRank(h);

      return FULL_HOUSE + h[2].rank();
   }

   /* ---------------------------------------------------------------
      valueSet(): return value of a Set hand

            value = SET + SetCardRank

      Trick: card h[2] is always a card that is part of the set hand
         There is ONLY ONE hand with a set of a given rank.
      --------------------------------------------------------------- */
   public static int valueSet( Card[] h )
   {
      sortByRank(h);

      return SET + h[2].rank();
   }

   /* -----------------------------------------------------
      valueTwoPairs(): return value of a Two-Pairs hand

            value = TWO_PAIRS
                   + 14*14*HighPairCard
                   + 14*LowPairCard
                   + UnmatchedCard
      ----------------------------------------------------- */
   public static int valueTwoPairs( Card[] h )
   {
      int val = 0;

      sortByRank(h);

      if ( h[0].rank() == h[1].rank() &&
           h[2].rank() == h[3].rank() )
         val = 14*14*h[2].rank() + 14*h[0].rank() + h[4].rank();
      else if ( h[0].rank() == h[1].rank() &&
                h[3].rank() == h[4].rank() )
         val = 14*14*h[3].rank() + 14*h[0].rank() + h[2].rank();
      else 
         val = 14*14*h[3].rank() + 14*h[1].rank() + h[0].rank();

      return TWO_PAIRS + val;
   }

   /* -----------------------------------------------------
      valueOnePair(): return value of a One-Pair hand

            value = ONE_PAIR 
                   + 14^3*PairCard
                   + 14^2*HighestCard
                   + 14*MiddleCard
                   + LowestCard
      ----------------------------------------------------- */
   public static int valueOnePair( Card[] h )
   {
      int val = 0;

      sortByRank(h);

      if ( h[0].rank() == h[1].rank() )
         val = 14*14*14*h[0].rank() +  
                + h[2].rank() + 14*h[3].rank() + 14*14*h[4].rank();
      else if ( h[1].rank() == h[2].rank() )
         val = 14*14*14*h[1].rank() +  
                + h[0].rank() + 14*h[3].rank() + 14*14*h[4].rank();
      else if ( h[2].rank() == h[3].rank() )
         val = 14*14*14*h[2].rank() +  
                + h[0].rank() + 14*h[1].rank() + 14*14*h[4].rank();
      else
         val = 14*14*14*h[3].rank() +  
                + h[0].rank() + 14*h[1].rank() + 14*14*h[2].rank();

      return ONE_PAIR + val;
   }

   /* -----------------------------------------------------
      valueHighCard(): return value of a high card hand

            value =  14^4*highestCard 
                   + 14^3*2ndHighestCard
                   + 14^2*3rdHighestCard
                   + 14^1*4thHighestCard
                   + LowestCard
      ----------------------------------------------------- */
   public static int valueHighCard( Card[] h )
   {
      int val;

      sortByRank(h);

      val = h[0].rank() + 14* h[1].rank() + 14*14* h[2].rank() 
            + 14*14*14* h[3].rank() + 14*14*14*14* h[4].rank();

      return val;
   }


   /***********************************************************
     Methods used to determine a certain Poker hand
    ***********************************************************/


   /* ---------------------------------------------
      is4s(): true if h has 4 of a kind
              false otherwise
      --------------------------------------------- */
   public static boolean is4s( Card[] h )
   {
      boolean a1, a2;

      if ( h.length != 5 )
         return(false);

      sortByRank(h);

      a1 = h[0].rank() == h[1].rank() &&
           h[1].rank() == h[2].rank() &&
           h[2].rank() == h[3].rank() ;

      a2 = h[1].rank() == h[2].rank() &&
           h[2].rank() == h[3].rank() &&
           h[3].rank() == h[4].rank() ;

      return( a1 || a2 );
   }


   /* ----------------------------------------------------
      isFullHouse(): true if h has Full House
                     false otherwise
      ---------------------------------------------------- */
   public static boolean isFullHouse( Card[] h )
   {
      boolean a1, a2;

      if ( h.length != 5 )
         return(false);

      sortByRank(h);

      a1 = h[0].rank() == h[1].rank() &&  //  x x x y y
           h[1].rank() == h[2].rank() &&
           h[3].rank() == h[4].rank();

      a2 = h[0].rank() == h[1].rank() &&  //  x x y y y
           h[2].rank() == h[3].rank() &&
           h[3].rank() == h[4].rank();

      return( a1 || a2 );
   }



   /* ----------------------------------------------------
      is3s(): true if h has 3 of a kind
              false otherwise

      **** Note: use is3s() ONLY if you know the hand
                 does not have 4 of a kind 
      ---------------------------------------------------- */
   public static boolean is3s( Card[] h )
   {
      boolean a1, a2, a3;

      if ( h.length != 5 )
         return(false);

      if ( is4s(h) || isFullHouse(h) )
         return(false);        // The hand is not 3 of a kind (but better)

      /* ----------------------------------------------------------
         Now we know the hand is not 4 of a kind or a full house !
         ---------------------------------------------------------- */
      sortByRank(h);

      a1 = h[0].rank() == h[1].rank() &&
           h[1].rank() == h[2].rank() ;

      a2 = h[1].rank() == h[2].rank() &&
           h[2].rank() == h[3].rank() ;

      a3 = h[2].rank() == h[3].rank() &&
           h[3].rank() == h[4].rank() ;

      return( a1 || a2 || a3 );
   }

   /* -----------------------------------------------------
      is22s(): true if h has 2 pairs
               false otherwise

      **** Note: use is22s() ONLY if you know the hand
                 does not have 3 of a kind or better
      ----------------------------------------------------- */
   public static boolean is22s( Card[] h )
   {
      boolean a1, a2, a3;

      if ( h.length != 5 )
         return(false);

      if ( is4s(h) || isFullHouse(h) || is3s(h) )
         return(false);        // The hand is not 2 pairs (but better)

      sortByRank(h);

      a1 = h[0].rank() == h[1].rank() &&
           h[2].rank() == h[3].rank() ;

      a2 = h[0].rank() == h[1].rank() &&
           h[3].rank() == h[4].rank() ;

      a3 = h[1].rank() == h[2].rank() &&
           h[3].rank() == h[4].rank() ;

      return( a1 || a2 || a3 );
   }


   /* -----------------------------------------------------
      is2s(): true if h has one pair
              false otherwise

      **** Note: use is22s() ONLY if you know the hand
                 does not have 2 pairs or better
      ----------------------------------------------------- */
   public static boolean is2s( Card[] h )
   {
      boolean a1, a2, a3, a4;

      if ( h.length != 5 )
         return(false);

      if ( is4s(h) || isFullHouse(h) || is3s(h) || is22s(h) )
         return(false);        // The hand is not one pair (but better)

      sortByRank(h);

      a1 = h[0].rank() == h[1].rank() ;
      a2 = h[1].rank() == h[2].rank() ;
      a3 = h[2].rank() == h[3].rank() ;
      a4 = h[3].rank() == h[4].rank() ;

      return( a1 || a2 || a3 || a4 );
   }


   /* ---------------------------------------------
      isFlush(): true if h has a flush
                 false otherwise
      --------------------------------------------- */
   public static boolean isFlush( Card[] h )
   {
      if ( h.length != 5 )
         return(false);

      sortBySuit(h);

      return( h[0].suit() == h[4].suit() );   // All cards has same suit
   }


   /* ---------------------------------------------
      isStraight(): true if h is a Straight
                    false otherwise
      --------------------------------------------- */
   public static boolean isStraight( Card[] h )
   {
      int i, testRank;

      if ( h.length != 5 )
         return(false);

      sortByRank(h);

      /* ===========================
         Check if hand has an Ace
         =========================== */
      if ( h[4].rank() == 14 )
      {
         /* =================================
            Check straight using an Ace
            ================================= */
         boolean a = h[0].rank() == 2 && h[1].rank() == 3 &&
                     h[2].rank() == 4 && h[3].rank() == 5 ;
         boolean b = h[0].rank() == 10 && h[1].rank() == 11 &&
                     h[2].rank() == 12 && h[3].rank() == 13 ;

         return ( a || b );
      }
      else
      {
         /* ===========================================
            General case: check for increasing values
            =========================================== */
         testRank = h[0].rank() + 1;

         for ( i = 1; i < 5; i++ )
         {
            if ( h[i].rank() != testRank )
               return(false);        // Straight failed...

            testRank++;
         }

         return(true);        // Straight found !
      }
   }

   /* ===========================================================
      Helper methods
      =========================================================== */

   /* ---------------------------------------------
      Sort hand by rank:

          smallest ranked card first .... 

      (Finding a straight is eaiser that way)
      --------------------------------------------- */
   public static void sortByRank( Card[] h )
   {
      int i, j, min_j;

      /* ---------------------------------------------------
         The selection sort algorithm
         --------------------------------------------------- */
      for ( i = 0 ; i < h.length ; i ++ )
      {
         /* ---------------------------------------------------
            Find array element with min. value among
            h[i], h[i+1], ..., h[n-1]
            --------------------------------------------------- */
         min_j = i;   // Assume elem i (h[i]) is the minimum
 
         for ( j = i+1 ; j < h.length ; j++ )
         {
            if ( h[j].rank() < h[min_j].rank() )
            {
               min_j = j;    // We found a smaller minimum, update min_j     
            }
         }
 
         /* ---------------------------------------------------
            Swap a[i] and a[min_j]
            --------------------------------------------------- */
         Card help = h[i];
         h[i] = h[min_j];
         h[min_j] = help;
      }
   }

   /* ---------------------------------------------
      Sort hand by suit:

          smallest suit card first .... 

      (Finding a flush is eaiser that way)
      --------------------------------------------- */
   public static void sortBySuit( Card[] h )
   {
      int i, j, min_j;

      /* ---------------------------------------------------
         The selection sort algorithm
         --------------------------------------------------- */
      for ( i = 0 ; i < h.length ; i ++ )
      {
         /* ---------------------------------------------------
            Find array element with min. value among
            h[i], h[i+1], ..., h[n-1]
            --------------------------------------------------- */
         min_j = i;   // Assume elem i (h[i]) is the minimum
 
         for ( j = i+1 ; j < h.length ; j++ )
         {
            if ( h[j].suit() < h[min_j].suit() )
            {
               min_j = j;    // We found a smaller minimum, update min_j     
            }
         }
 
         /* ---------------------------------------------------
            Swap a[i] and a[min_j]
            --------------------------------------------------- */
         Card help = h[i];
         h[i] = h[min_j];
         h[min_j] = help;
      }
   }

}

This Java class works if you pick the best 5 cards out of 7, but it doesn't work if you don't pick the best 5 cards out of 7. Aside running the algorithm 21 times, because there are 21 combination possible for 1 player, what's the best way about doing this? I've heard that you can just compare all 7 cards at the same time, but there's the issue that the algorithms would have to be rewritten, and I am not sure how exactly the logic to these algorithms would have to change.

How would you do this given you already the above code in your hand? Is it possible to not use brute force without changing the current code?

Sayaman
  • 1,145
  • 4
  • 14
  • 35
  • 1
    Are there community cards? Because that's going to require another set of changes to extend the tiebreaking rules. – David Eisenstat Mar 04 '21 at 13:13
  • What are community cards? I am trying to create an algorithm for Texas Hold 'em. – Sayaman Mar 04 '21 at 13:22
  • https://en.wikipedia.org/wiki/Community_card_poker . The problem is that it's not enough to compare (e.g.) flushes by their high card, since that high card may be in common between the hands. – David Eisenstat Mar 04 '21 at 13:27
  • Brute force (checking each 5-card subset) is sensible. You can avoid brute force, and recode each of your functions to handle 7 cards, but it's a lot of work and the resulting code is ugly. If you need fast code, the whole structure of the code is suboptimal -- all fast 7-card evaluation code (that I know of) uses lookup tables. I have a repo https://github.com/paulhankin/poker that includes fast 5 and 7 card eval code (in go, but the ideas still work). The repo links to a forum thread where the ideas for the code came from (although the link is unfortunately dead). – Paul Hankin Mar 04 '21 at 14:09
  • https://github.com/paulhankin/poker/blob/master/poker/eval.go#L140 is a fast-ish 5-card evaluation that doesn't use lookup tables. If you care about speed, the bit-twiddling ideas there might help even if you adapt your code to 7 cards. If you don't care about speed, just call your current code 21 times. – Paul Hankin Mar 04 '21 at 14:12
  • @DavidEisenstat is correct (isn't he always!) about your evaluation. The score assigned to a flush must encode all 5 cards in the flush. Even without community cards, ♠A♠Q♠J♠8♠6 beats ♢A♢Q♢J♢8♢5. With community cards, the same goes for quads, full houses, 3-of-a-kind – Paul Hankin Mar 04 '21 at 14:16
  • The value when the hand has just high cards is also wrong. Again, it needs to encode the value of all 5 cards. – Paul Hankin Mar 04 '21 at 14:35
  • @PaulHankin That depends on the exact card game. In Texas Hold'em the suits are all equal. In stud poker they are not. – btilly Mar 04 '21 at 16:58
  • @PaulHankin what score should I assign to each hands? For each value, I would assign a score between 1 to 13, but I have no idea what score I should assign for each hand. – Sayaman Mar 04 '21 at 23:43
  • @Atsushi oddly, you have solved this problem already in some of the other evaluations (for example, in the 2-pair case you score three values -- the higher pair (1-13), the lower pair (1-13) and the spare card (1-13) as 14*14*high-pair + 14*low-pair + spare). You can do a similar thing with all the other hands. For example, the case where there's no pairs or anything, if the rank of the cards in low-to-high order are c0, c1, c2, c3, c4 then you can value them as c0 + 14*c1 + 14*14*c2 + 14*14*14*c3 + 14*14*14*14*c4. The top-level scores for the hand types are spaced 1 million apart ... – Paul Hankin Mar 05 '21 at 11:23
  • ... which is enough that even in the worst case (534744) a no-pair score will never be above ONE_PAIR. – Paul Hankin Mar 05 '21 at 11:24
  • In my own code, I have a score function `evalScore5` that takes a number `v` from 0 (no pair) to 9 (five-of-a-kind) and 5 card values `a` to `e` (with 0 representing no card), and simply encode that as `v*16*16*16*16*16 + a*16*16*16*16 + b*16*16*16 + c*16*16 + d*16 + e`. Then, for example, I score full house as `evalScore5(6, t, p, 0, 0, 0)` where `t` is the value of the trips, and `p` the value of the pair. And one-pair as `evalScore5(1, p, a, b, c, 0)` where `p` is the value of the pair and `a,b,c` the spare cards in decreasing order. I think this scheme makes things a bit more uniform. – Paul Hankin Mar 05 '21 at 11:32

0 Answers0