A Lightweight Five-Card Poker Library Using Python

One morning before work, I decided to implement a lightweight five-card poker library using Python. My library has a Card class, a Hand class, and a SingleDeck class. The three main functions are: 1.) classify a hand (like “FullHouse”), 2.) compare two hands to determine which hand is better, 3.) deal a hand from a deck of 52 cards.

I didn’t implement my poker library starting from nothing — I refactored my existing C# poker library.

There are two ways to create a Card object:

  c1 = Card((14,3))  # Ace of spades
  print(c1.to_string())

  c2 = Card("Td")  # Ten of diamonds
  print(c2.to_string())

The first constructor accepts a (rank, suit) tuple. The rank values are 2 = Two, 3 = Three, . . 10 = Ten, 11 = Jack, 12 = Queen, 13 = King, 14 = Ace. Rank values of 0 and 1 are not used. The suit values are 0 = clubs, 1 = diamonds, 2 = hearts, 3 = spades.

There are three main ways to create a Hand object:

  h1 = Hand("7cTsJc8d9h")
  print(h1.to_string())

  h2 = Hand(Card("6s"), Card("Ah"), Card("6h"),
    Card("Ac"), Card("6d"))
  print(h2.to_string()) 

  lst = []
  lst.append(Card("5c")); lst.append(Card("5d"))
  lst.append(Card("9c")); lst.append(Card("9d"))
  lst.append(Card("Qh"))
  h3 = Hand(lst)
  print(h3.to_string())

The first constuctor accepts an easy-to-interpret string such as “7cTsJc8d9h”. The second constructor accepts five individual Card objects. The third constructor accepts a List of five Card objects.

Hand objects are sorted from low card (“2c”) to high card (“As”). The sorting makes a hand easier to interpret, and easier to classify and compare.

There are two methods to classify a Hand object. The get_handtype_str() method returns one of ten strings: “HighCard”, “OnePair”, “TwoPair” , “ThreeKind” , “Straight”, “Flush” , “FullHouse”, “FourKind”, “StraightFlush”, “RoyalFlush”. The get_handtype_int() method returns integer 0 (high card) through 9 (royal flush).

  print(h1.get_handtype_str() + " = ", end="")  # Straight
  print(h1.get_handtype_int())  # 4

  print(h2.get_handtype_str() + " = ", end="")  # FullHouse
  print(h2.get_handtype_int())  # 6

  print(h3.get_handtype_str() + " = ", end="")  # TwoPair
  print(h3.get_handtype_int())  # 2

There is a static Hand.compare(h1, h2) function. It returns -1 if h1 is less than h2, returns +1 if h1 is greater than h2, returns 0 if h1 equals h2.

  cmp = Hand.compare(h1, h2)  # -1 Straight less FullHouse
  cmp = Hand.compare(h2, h3)  # 1 FullHouse greater TwoPair

The SingleDeck class has a deal_hand() method and a deal_list_cards() method. The deal_hand() method returns a Hand object containing five Card objects. The deal_list_cards() method return a List of n Card objects.

  d1 = SingleDeck(seed=0)
  d1.shuffle()
  d1.show()

  h4 = d1.deal_hand()
  print(h4.to_string())

  list_cards = d1.deal_list_cards(38) # remove 38 cards
  d1.show()  # 9 cards left in deck

The poker library can be used in several ways. You can compute the probabilities of different hands using a simulation. You can find the best five-card hand from seven cards. And so on. I’ll post some examples at some point.



I love old mechanical and electro-mechanical coin operated devices. Left: This Sittman-Pitt machine from 1891 is arguably the world’s first slot machine. It didn’t have automatic payout, so most resources state that the “Liberty Bell” machine from 1895, which did have automatic payout, by the Chas Fey company, was the first true slot machine. Right: The “Pok-O-Reel” machine was manufactured about 1932 by the Groetchen Tool and Manufacturing Company.


Demo code. Replace “lt” (less-than), “gt”, “lte”, “gte” with Boolean operator symbols.

# poker.py

# a library of five-card poker functions
# Card, Hand, Deck classes

import numpy as np

# -----------------------------------------------------------

class Card:
  def __init__(self, *args):
    # like string "Jc" or tuple (11,3)
    # rank: 2=Two, 3=Three . . 14=Ace
    # suit: 0=clubs, 1=diamonds, 2=hearts, 3=spades

    if isinstance(args[0], str):
      rnk = args[0][0]; sut = args[0][1]
      if rnk == 'T': self.rank = 10
      elif rnk == 'J': self.rank = 11
      elif rnk == 'Q': self.rank = 12
      elif rnk == 'K': self.rank = 13
      elif rnk == 'A': self.rank = 14
      else: self.rank = int(rnk) 

      if sut == 'c': self.suit = 0
      elif sut == 'd': self.suit = 1
      elif sut == 'h': self.suit = 2
      elif sut == 's': self.suit = 3
    elif isinstance(args[0], tuple):
      self.rank = int(args[0][0])
      self.suit = int(args[0][1])

  def to_string(self):
    rnk = ""; sut = ""
    if self.rank == 10: rnk = "T"
    elif self.rank == 11: rnk = "J"
    elif self.rank == 12: rnk = "Q"
    elif self.rank == 13: rnk = "K"
    elif self.rank == 14: rnk = "A"
    else: rnk = str(self.rank)

    if self.suit == 0: sut = "c"
    elif self.suit == 1: sut = "d"
    elif self.suit == 2: sut = "h"
    elif self.suit == 3: sut = "s"

    return rnk + sut

  # ---------------------------------------------------------
# ----- end Card --------------------------------------------

class Hand:
  # 5-card poker hand. sorted List of Card objects
  # Hand types: "RoyalFlush", "StraightFlush",
  # "FourKind", "FullHouse", "Flush" , "Straight",
  # "ThreeKind", "TwoPair", "OnePair", "HighCard"

  def __init__(self, *args):
    self.cards = []
    if isinstance(args[0], list):
      for i in range(5):
        # c = Card(args[0][i])
        self.cards.append(args[0][i])
    elif len(args) == 5:
      self.cards.append(args[0])
      self.cards.append(args[1])
      self.cards.append(args[2])
      self.cards.append(args[3])
      self.cards.append(args[4])
    elif isinstance(args[0], str):
      for i in range(0,10,2):
        cs = args[0][i:i+2]
        c = Card(cs)
        self.cards.append(c)

    self.cards.sort(key=lambda x: (x.rank, x.suit))
  
  def to_string(self):
    s = ""
    for i in range(5):
      s += self.cards[i].to_string()
    return s

  # ---------------------------------------------------------

  # Hand Type methods:
  # get_handtype_str(), get_handtype_int() call:
  # 
  # is_royal_flush(), is_straight_flush(), 
  # is_four_kind(), is_full_house(), is_flush(),
  # is_straight(), is_three_kind(), is_two_pair(),
  # is_one_pair(), is_high_card()
  #
  # Helpers: has_straight(), has_flush()

  # ---------------------------------------------------------

  def get_handtype_str(self):
    if self.is_royal_flush() == True:
      return "RoyalFlush"
    elif self.is_straight_flush() == True:
      return "StraightFlush"
    elif self.is_four_kind() == True:
      return "FourKind"
    elif self.is_full_house() == True:
      return "FullHouse"
    elif self.is_flush() == True:
      return "Flush"
    elif self.is_straight() == True:
      return "Straight"
    elif self.is_three_kind() == True:
      return "ThreeKind"
    elif self.is_two_pair() == True:
      return "TwoPair"
    elif self.is_one_pair() == True:
      return "OnePair"
    elif self.is_high_card() == True:
      return "HighCard"
    else:
      return "Unknown"

  # ---------------------------------------------------------

  def get_handtype_int(self):
    if self.is_royal_flush() == True:
      return 9
    elif self.is_straight_flush() == True:
      return 8
    elif self.is_four_kind() == True:
      return 7
    elif self.is_full_house() == True:
      return 6
    elif self.is_flush() == True:
      return 5
    elif self.is_straight() == True:
      return 4
    elif self.is_three_kind() == True:
      return 3
    elif self.is_two_pair() == True:
      return 2
    elif self.is_one_pair() == True:
      return 1
    elif self.is_high_card() == True:
      return 0
    else:
      return -1

  # ---------------------------------------------------------

  def has_flush(self):
    if (self.cards[0].suit == self.cards[1].suit) and \
       (self.cards[1].suit == self.cards[2].suit) and \
       (self.cards[2].suit == self.cards[3].suit) and \
       (self.cards[3].suit == self.cards[4].suit):
      return True

    return False

  # ---------------------------------------------------------

  def has_straight(self):
    # check special case of Ace-low straight
    # 2, 3, 4, 5, A when sorted
    if self.cards[0].rank == 2 and \
      self.cards[1].rank == 3 and \
      self.cards[2].rank == 4 and \
      self.cards[3].rank == 5 and \
      self.cards[4].rank == 14:
        return True
    # otherwise, check for 5 consecutive card ranks
    if (self.cards[0].rank == self.cards[1].rank - 1) and \
      (self.cards[1].rank == self.cards[2].rank - 1) and \
      (self.cards[2].rank == self.cards[3].rank - 1) and \
      (self.cards[3].rank == self.cards[4].rank - 1):
      return True

    return False

  # ---------------------------------------------------------

  def is_royal_flush(self):
    if self.has_straight() == True and \
      self.has_flush() == True and \
        self.cards[0].rank == 10:  # assumes hand is sorted
      return True
    else:
      return False

  # ---------------------------------------------------------

  def is_straight_flush(self):
    if self.has_straight() == True and \
      self.has_flush() == True and \
        self.cards[0].rank != 10:  # no Royal Flush
      return True
    else:
      return False

  # ---------------------------------------------------------

  def is_four_kind(self):
    # AAAA B or B AAAA if sorted
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True

    if (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[0].rank != self.cards[1].rank):
      return True

    return False

  # ---------------------------------------------------------

  def is_full_house(self):
    # AAA BB or BB AAA if sorted
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[2].rank != self.cards[3].rank):
      return True

    # BB AAA
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[1].rank != self.cards[2].rank):
      return True

    return False

  # ---------------------------------------------------------

  def is_flush(self):
    if self.has_flush() == True and \
      self.has_straight() == False:
      return True  # no StraightFlush or RoyalFlush
    else:
      return False

  # ---------------------------------------------------------

  def is_straight(self):
    if self.has_straight() == True and \
      self.has_flush() == False:
      return True
    else:
      return False

  # ---------------------------------------------------------

  def is_three_kind(self):
    # AAA B C or B AAA C or B C AAA if sorted
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[2].rank != self.cards[3].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True

    if (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True

    if (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[1].rank != self.cards[2].rank):
      return True

    return False

  # ---------------------------------------------------------

  def is_two_pair(self):
    # AA BB C or AA C BB or C AA BB if sorted
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[1].rank != self.cards[2].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True  # AA BB C

    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[1].rank != self.cards[2].rank) and \
      (self.cards[2].rank != self.cards[3].rank):
      return True  # AA C BB

    if (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[2].rank != self.cards[3].rank):
      return True  # C AA BB

    return False

  # ---------------------------------------------------------

  def is_one_pair(self):
    # AA B C D or B AA C D or B C AA D or B C D AA
    if (self.cards[0].rank == self.cards[1].rank) and \
      (self.cards[1].rank != self.cards[2].rank) and \
      (self.cards[2].rank != self.cards[3].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True  # AA B C D

    if (self.cards[1].rank == self.cards[2].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[2].rank != self.cards[3].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True  # B AA C D

    if (self.cards[2].rank == self.cards[3].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[1].rank != self.cards[2].rank) and \
      (self.cards[3].rank != self.cards[4].rank):
      return True  # B C AA D

    if (self.cards[3].rank == self.cards[4].rank) and \
      (self.cards[0].rank != self.cards[1].rank) and \
      (self.cards[1].rank != self.cards[2].rank) and \
      (self.cards[2].rank != self.cards[3].rank):
      return True  # B C D AA

    return False

  # ---------------------------------------------------------

  def is_high_card(self):
    if self.has_flush() == True:
      return False
    elif self.has_straight() == True:
      return False
    else:
      # all remaining have at least one pair
      if (self.cards[0].rank == self.cards[1].rank) or \
        (self.cards[1].rank == self.cards[2].rank) or \
        (self.cards[2].rank == self.cards[3].rank) or \
        (self.cards[3].rank == self.cards[4].rank):
        return False

    return True

  # ---------------------------------------------------------

  # Hand comparison methods
  # Hand.compare() calls:
  # break_tie_straight_flush(), break_tie_four_kind(),
  # break_tie_full_house(), break_tie_flush(),
  # break_tie_straight(), break_tie_three_kind(),
  # break_tie_two_pair(), break_tie_one_pair(),
  # break_tie_high_card()

  # ---------------------------------------------------------

  @staticmethod
  def compare(h1, h2):
    # -1 if h1 "lt" h2, +1 if h1 "gt" h2, 0 if h1 == h2

    h1_idx = h1.get_handtype_int()  # like 6
    h2_idx = h2.get_handtype_int()

    # different hand types - easy
    if h1_idx "lt" h2_idx:
      return -1
    elif h1_idx "gt" h2_idx:
      return 1
    else: # same hand types so break tie
      h1_handtype = h1.get_handtype_str()
      h2_handtype = h2.get_handtype_str()

      if h1_handtype != h2_handtype:
        print("Fatal Logic Error in compare()")

      if h1_handtype == "RoyalFlush":
        return 0  # two Royal Flush always tie
      elif h1_handtype == "StraightFlush":
        return Hand.break_tie_straight_flush(h1, h2)
      elif h1_handtype == "FourKind":
        return Hand.break_tie_four_kind(h1, h2)
      elif h1_handtype == "FullHouse":
        return Hand.break_tie_full_house(h1, h2)
      elif h1_handtype == "Flush":
        return Hand.break_tie_flush(h1, h2)
      elif h1_handtype == "Straight":
        return Hand.break_tie_straight(h1, h2)
      elif h1_handtype == "ThreeKind":
        return Hand.break_tie_three_kind(h1, h2)
      elif h1_handtype == "TwoPair":
        return Hand.break_tie_two_pair(h1, h2)
      elif h1_handtype == "OnePair":
        return Hand.break_tie_one_pair(h1, h2)
      elif h1_handtype == "HighCard":
        return Hand.break_tie_high_card(h1, h2)

    return -2  # error

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_straight_flush(h1, h2):
    # check one or two Ace-low hands
    # h1 is Ace-low, h2 not Ace-low.h1, is less
    if (h1.cards[0].rank == 2 and \
        h1.cards[4].rank == 14) and \
       not (h2.cards[0].rank == 2 and
       h2.cards[4].rank == 14):
      return -1
 
    # h1 not Ace-low, h2 is Ace-low, h1 is better
    if not (h1.cards[0].rank == 2 and \
         h1.cards[4].rank == 14) and \
       (h2.cards[0].rank == 2 and \
        h2.cards[4].rank == 14):
      return 1
  
    # two Ace-low hands
    if (h1.cards[0].rank == 2 and \
        h1.cards[4].rank == 14) and \
       (h2.cards[0].rank == 2 and \
        h2.cards[4].rank == 14):
      return 0

    # no Ace-low straight flush so check high cards
    if h1.cards[4].rank "lt" h2.cards[4].rank:
      return -1
    elif h1.cards[4].rank "gt" h2.cards[4].rank:
      return 1
    else:
      return 0
  
  # ---------------------------------------------------------

  @staticmethod
  def break_tie_four_kind(h1, h2):
    # AAAA B or B AAAA
    # the off-card is at [0] or at [4]
    # find h1 four-card and off-card ranks
    h1_four_rank = -1; h1_off_rank = -1
    if h1.cards[0].rank == h1.cards[1].rank:
      # 1st two cards same so off-rank at [4]
      h1_four_rank = h1.cards[0].rank
      h1_off_rank = h1.cards[4].rank
    else:
      # 1st two cards diff so off-rank at [0]
      h1_four_rank = h1.cards[4].rank
      h1_off_rank = h1.cards[0].rank

    h2_four_rank = -1; h2_off_rank = -1
    if h2.cards[0].rank == h2.cards[1].rank:
      h2_four_rank = h2.cards[0].rank
      h2_off_rank = h2.cards[4].rank
    else:
      h2_four_rank = h2.cards[4].rank
      h2_off_rank = h2.cards[0].rank

    if h1_four_rank "lt" h2_four_rank: # like 4K, 4A
      return -1
    elif h1_four_rank "gt" h2_four_rank:
      return 1
    else: # both hands have same four-kind (mult. decks)
      if h1_off_rank "lt" h2_off_rank:
        return -1  # like 3c 9c9d9h9s "lt" Qd 9c9d9h9s
      elif h1_off_rank "gt" h2_off_rank:
        return 1  # like Jc 4c4d4h4s "gt" 9s 4c4d4h4s
      elif h1_off_rank == h2_off_rank:
        return 0

    print("Fatal logic break_tie_four_kind")

  # ---------------------------------------------------------
  
  @staticmethod
  def break_tie_full_house(h1, h2):
    # determine high rank (3 kind) and low rank (2 kind)
    # JJJ 55 or 33 KKK
    # if [1] == [2] 3 kind at [0][1][2]
    # if [1] != [2] 3 kind at [2][3][4]

    h1_three_rank = -1; h1_two_rank = -1
    if h1.cards[1].rank == h1.cards[2].rank:
      # if [1] == [2] 3 kind at [0][1][2]
      h1_three_rank = h1.cards[0].rank
      h1_two_rank = h1.cards[4].rank
    else:
      # if [1] != [2] 3 kind at [2][3][4]
      h1_three_rank = h1.cards[4].rank
      h1_two_rank = h1.cards[0].rank

    h2_three_rank = -1; h2_two_rank = -1
    if h2.cards[1].rank == h2.cards[2].rank:
      # if [1] == [2] 3 kind at [0][1][2]
      h2_three_rank = h2.cards[0].rank
      h2_two_rank = h2.cards[4].rank
    else:
      # if [1] != [2] 3 kind at [2][3][4]
      h2_three_rank = h2.cards[4].rank
      h2_two_rank = h2.cards[0].rank

    if h1_three_rank "lt" h2_three_rank:
      return -1
    elif h1_three_rank "gt" h2_three_rank:
      return 1
    else:  # both hands same three-kind (mult. decks)
      if h1_two_rank "lt" h2_two_rank:
        return -1  # like 3c3d 9c9d9h "lt" QdQs 9c9d9h
      elif h1_two_rank "gt" h2_two_rank:
        return 1  # like 3c3d 9c9d9h "gt" 2d2s 9c9d9h
      elif h1_two_rank == h2_two_rank:
        return 0

    print("Fatal logic break_tie_full_house")

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_flush(h1, h2):
    # compare rank of high cards
    if h1.cards[4].rank "lt" h2.cards[4].rank:
      return -1
    elif h1.cards[4].rank "gt" h2.cards[4].rank:
      return 1
    # high cards ranks equal so check at [3]
    elif h1.cards[3].rank "lt" h2.cards[3].rank:
      return -1
    elif h1.cards[3].rank "gt" h2.cards[3].rank:
      return 1
    # and so on
    elif h1.cards[2].rank "lt" h2.cards[2].rank:
      return -1
    elif h1.cards[2].rank "gt" h2.cards[2].rank:
      return 1
    #
    elif h1.cards[1].rank "lt" h2.cards[1].rank:
      return -1
    elif h1.cards[1].rank "gt" h2.cards[1].rank:
      return 1
    #
    elif h1.cards[0].rank "lt" h2.cards[0].rank:
      return -1
    elif h1.cards[0].rank "gt" h2.cards[0].rank:
      return 1
    #
    else:
      return 0  # all ranks the same

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_straight(h1, h2):
    # both hands are straights but one could be Ace-low
    # check special case of one or two Ace-low hands
    # h1 is Ace-low, h2 not Ace-low. h1 is less
    if (h1.cards[0].rank == 2 and \
      h1.cards[4].rank == 14) and \
      not (h2.cards[0].rank == 2 and \
      h2.cards[4].rank == 14):
      return -1
    # h1 not Ace-low, h2 is Ace-low, h1 is better
    elif not (h1.cards[0].rank == 2 and \
      h1.cards[4].rank == 14) and \
      (h2.cards[0].rank == 2 and \
      h2.cards[4].rank == 14):
      return 1
    # two Ace-low hands
    elif (h1.cards[0].rank == 2 and \
      h1.cards[4].rank == 14) and \
      (h2.cards[0].rank == 2 and \
      h2.cards[4].rank == 14):
      return 0

    # no Ace-low hands so just check high card
    if h1.cards[4].rank "lt" h2.cards[4].rank:
      return -1
    elif h1.cards[4].rank "gt" h2.cards[4].rank:
      return +1
    elif h1.cards[4].rank == h2.cards[4].rank:
      return 0
    else:
      print("Fatal logic break_tie_straight")

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_three_kind(h1, h2):
    # assumes multiple decks possible
    # TTT L H or L TTT H or L H TTT
    h1_three_rank = 0; h1_low_rank = 0
    h1_high_rank = 0
    if h1.cards[0].rank == h1.cards[1].rank and \
      h1.cards[1].rank == h1.cards[2].rank:
      h1_three_rank = h1.cards[0].rank
      h1_low_rank = h1.cards[3].rank
      h1_high_rank = h1.cards[4].rank
    elif h1.cards[1].rank == h1.cards[2].rank and \
      h1.cards[2].rank == h1.cards[3].rank:
      h1_low_rank = h1.cards[0].rank
      h1_three_rank = h1.cards[1].rank
      h1_high_rank = h1.cards[4].rank
    elif h1.cards[2].rank == h1.cards[3].rank and \
     h1.cards[3].rank == h1.cards[4].rank:
     h1_low_rank = h1.cards[0].rank
     h1_high_rank = h1.cards[1].rank
     h1_three_rank = h1.cards[4].rank
  
    h2_three_rank = 0; h2_low_rank = 0
    h2_high_rank = 0
    if h2.cards[0].rank == h2.cards[1].rank and \
      h2.cards[1].rank == h2.cards[2].rank:
      h2_three_rank = h2.cards[0].rank
      h2_low_rank = h2.cards[3].rank
      h2_high_rank = h2.cards[4].rank
    elif h2.cards[1].rank == h2.cards[2].rank and \
      h2.cards[2].rank == h2.cards[3].rank:
      h2_low_rank = h2.cards[0].rank
      h2_three_rank = h2.cards[1].rank
      h2_high_rank = h2.cards[4].rank
    elif h2.cards[2].rank == h2.cards[3].rank and \
      h2.cards[3].rank == h2.cards[4].rank:
      h2_low_rank = h2.cards[0].rank
      h2_high_rank = h2.cards[1].rank
      h2_three_rank = h2.cards[4].rank

    if h1_three_rank "lt" h2_three_rank:
      return -1
    elif h1_three_rank "gt" h2_three_rank:
      return 1
    # both hands three-kind same mult. decks
    elif h1_high_rank "lt" h2_high_rank:
      return -1
    elif h1_high_rank "gt" h2_high_rank:
      return 1
    elif h1_low_rank "lt" h2_low_rank:
      return -1
    elif h1_low_rank "gt" h2_low_rank:
      return 1
    #
    else:
      return 0

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_two_pair(h1, h2):
    # LL X HH or LL HH X or X LL HH
    h1_low_rank = 0;  h1_high_rank = 0
    h1_off_rank = 0
    if h1.cards[0].rank == h1.cards[1].rank and \
      h1.cards[3].rank == h1.cards[4].rank:
      # LL X HH
      h1_low_rank = h1.cards[0].rank
      h1_high_rank = h1.cards[4].rank
      h1_off_rank = h1.cards[2].rank
    elif h1.cards[0].rank == h1.cards[1].rank and \
      h1.cards[2].rank == h1.cards[3].rank:
      # LL HH X
      h1_low_rank = h1.cards[0].rank
      h1_high_rank = h1.cards[2].rank
      h1_off_rank = h1.cards[4].rank
    elif h1.cards[1].rank == h1.cards[2].rank and \
      h1.cards[3].rank == h1.cards[4].rank:
      # X LL HH
      h1_low_rank = h1.cards[1].rank
      h1_high_rank = h1.cards[3].rank
      h1_off_rank = h1.cards[0].rank

    h2_low_rank = 0;  h2_high_rank = 0
    h2_off_rank = 0
    if h2.cards[0].rank == h2.cards[1].rank and \
      h2.cards[3].rank == h2.cards[4].rank:
      # LL X HH
      h2_low_rank = h2.cards[0].rank
      h2_high_rank = h2.cards[4].rank
      h2_off_rank = h2.cards[2].rank
    elif h2.cards[0].rank == h2.cards[1].rank and \
      h2.cards[2].rank == h2.cards[3].rank:
      # LL HH X
      h2_low_rank = h2.cards[0].rank
      h2_high_rank = h2.cards[2].rank
      h2_off_rank = h2.cards[4].rank
    elif h2.cards[1].rank == h2.cards[2].rank and \
      h2.cards[3].rank == h2.cards[4].rank:
      # X LL HH
      h2_low_rank = h2.cards[1].rank
      h2_high_rank = h2.cards[3].rank
      h2_off_rank = h2.cards[0].rank
 
    if h1_high_rank "lt" h2_high_rank:
      return -1
    elif h1_high_rank "gt" h2_high_rank:
      return 1
    elif h1_low_rank "lt" h2_low_rank:
      return -1
    elif h1_low_rank "gt" h2_low_rank:
      return 1
    elif h1_off_rank "lt" h2_off_rank:
      return -1
    elif h1_off_rank "gt" h2_off_rank:
      return 1
    else:
      return 0

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_one_pair(h1, h2):
    # PP L M H or L PP M H
    # or L M PP H or L M H PP
    h1_pair_rank = 0; h1_low_rank = 0
    h1_medium_rank = 0; h1_high_rank = 0
    if h1.cards[0].rank == h1.cards[1].rank:
      # PP L M H
      h1_pair_rank = h1.cards[0].rank
      h1_low_rank = h1.cards[2].rank
      h1_medium_rank = h1.cards[3].rank
      h1_high_rank = h1.cards[4].rank
    elif h1.cards[1].rank == h1.cards[2].rank:
      # L PP M H
      h1_pair_rank = h1.cards[1].rank
      h1_low_rank = h1.cards[0].rank
      h1_medium_rank = h1.cards[3].rank
      h1_high_rank = h1.cards[4].rank
    elif h1.cards[2].rank == h1.cards[3].rank:
      # L M PP H
      h1_pair_rank = h1.cards[2].rank
      h1_low_rank = h1.cards[0].rank
      h1_medium_rank = h1.cards[1].rank
      h1_high_rank = h1.cards[4].rank
    elif h1.cards[3].rank == h1.cards[4].rank:
      # L M H PP
      h1_pair_rank = h1.cards[4].rank
      h1_low_rank = h1.cards[0].rank
      h1_medium_rank = h1.cards[1].rank
      h1_high_rank = h1.cards[2].rank
 
    h2_pair_rank = 0; h2_low_rank = 0
    h2_medium_rank = 0; h2_high_rank = 0
    if h2.cards[0].rank == h2.cards[1].rank:
      # PP L M H
      h2_pair_rank = h2.cards[0].rank
      h2_low_rank = h2.cards[2].rank
      h2_medium_rank = h2.cards[3].rank
      h2_high_rank = h2.cards[4].rank
    elif h2.cards[1].rank == h2.cards[2].rank:
      # L PP M H
      h2_pair_rank = h2.cards[1].rank
      h2_low_rank = h2.cards[0].rank
      h2_medium_rank = h2.cards[3].rank
      h2_high_rank = h2.cards[4].rank
    elif h2.cards[2].rank == h2.cards[3].rank:
      # L M PP H
      h2_pair_rank = h2.cards[2].rank
      h2_low_rank = h2.cards[0].rank
      h2_medium_rank = h2.cards[1].rank
      h2_high_rank = h2.cards[4].rank
    elif h2.cards[3].rank == h2.cards[4].rank:
      # L M H PP
      h2_pair_rank = h2.cards[4].rank
      h2_low_rank = h2.cards[0].rank
      h2_medium_rank = h2.cards[1].rank
      h2_high_rank = h2.cards[2].rank

    if h1_pair_rank "lt" h2_pair_rank:
      return -1
    elif h1_pair_rank "gt" h2_pair_rank:
      return 1
    #
    elif h1_high_rank "lt" h2_high_rank:
      return -1
    elif h1_high_rank "gt" h2_high_rank:
      return 1
    #
    elif h1_medium_rank "lt" h2_medium_rank:
      return -1
    elif h1_medium_rank "gt" h2_medium_rank:
      return 1
    #
    elif h1_low_rank "lt" h2_low_rank:
      return -1
    elif h1_low_rank "gt" h2_low_rank:
      return 1
    #
    else:
      return 0

  # ---------------------------------------------------------

  @staticmethod
  def break_tie_high_card(h1, h2):
    if h1.cards[4].rank "lt" h2.cards[4].rank:
      return -1
    elif h1.cards[4].rank "gt" h2.cards[4].rank:
      return 1
    #
    elif h1.cards[3].rank "lt" h2.cards[3].rank:
      return -1
    elif h1.cards[3].rank "gt" h2.cards[3].rank:
      return 1
    #
    elif h1.cards[2].rank "lt" h2.cards[2].rank:
      return -1
    elif h1.cards[2].rank "gt" h2.cards[2].rank:
      return 1
    #
    elif h1.cards[1].rank "lt" h2.cards[1].rank:
      return -1
    elif h1.cards[1].rank "gt" h2.cards[1].rank:
      return 1
    #
    elif h1.cards[0].rank "lt" h2.cards[0].rank:
      return -1
    elif h1.cards[0].rank "gt" h2.cards[0].rank:
      return 1
    #
    else:
      return 0

  # ---------------------------------------------------------
# ----- end Hand --------------------------------------------

class SingleDeck:
  def __init__(self, seed):
    self.rnd = np.random.RandomState(seed)
    self.deck = []
    self.curr_idx = 0
    for r in range(2,15):  # rank
      for s in range(0,4):  # suit
        self.deck.append(Card((r,s)))  # 2c 2d . . Ah As

  def shuffle(self):
    self.rnd.shuffle(self.deck)
    self.curr_idx = 0

  def deal_hand(self):
    if self.curr_idx "gt" 47:
      print("Not enough cards in deck to deal five ")
    lst = []
    for i in range(5):
      c = self.deck[self.curr_idx]
      lst.append(c)
      self.curr_idx += 1
    h = Hand(lst)
    return h
    
  def deal_list_cards(self, n_cards):
    # return a List of Card objects
    if self.curr_idx + n_cards "gt" 52:  # tricky
      print("Not enough cards in deck to deal " + \
        str(n_cards))
    lst = []
    for i in range(n_cards):
      c = self.deck[self.curr_idx]
      lst.append(c)
      self.curr_idx += 1
    return lst 

  def deal_hand_string(self):
    # unsorted string like "4cJs5dJc7h"
    result = ""
    for i in range(5):
      c = self.deck[self.curr_idx]  # Card
      result = result + c.to_string()
      self.curr_idx += 1
    return result
      
  def show(self):
    ct = 0
    for i in range(self.curr_idx, 52):
      if ct "gt" 0 and ct % 10 == 0: print("")
      print(self.deck[i].to_string() + " ", end="")
      ct += 1
    print("")
 
# ----- end SingleDeck --------------------------------------

def main():
  print("\nBegin poker demo ")

  # ----- Card ----------------------------------------------

  c1 = Card((14,3))  # Ace of spades
  print("\nCard c1 = ")
  print(c1.to_string())

  c2 = Card("Td")  # Ten of diamonds
  print("\nCard c2 = ")
  print(c2.to_string())

  # ----- Hand ----------------------------------------------

  h1 = Hand("7cTsJc8d9h")
  print("\nHand h1: ")
  print(h1.to_string())
  print(h1.get_handtype_str() + " = ", end="")  # Straight
  print(h1.get_handtype_int())  # 4

  h2 = Hand(Card("6s"), Card("Ah"), Card("6h"),
    Card("Ac"), Card("6d"))
  print("\nHand h2: ")
  print(h2.to_string()) 
  print(h2.get_handtype_str() + " = ", end="")  # FullHouse
  print(h2.get_handtype_int())  # 6

  lst = []
  lst.append(Card("5c")); lst.append(Card("5d"))
  lst.append(Card("9c")); lst.append(Card("9d"))
  lst.append(Card("Qh"))
  h3 = Hand(lst)
  print("\nHand h3: ")
  print(h3.to_string())
  print(h3.get_handtype_str() + " = ", end="")  # TwoPair
  print(h3.get_handtype_int())  # 2

  # ----- Compare -------------------------------------------

  print("\nHand.compare(h1, h2) = ")
  print(Hand.compare(h1, h2))

  print("\nHand.compare(h2, h3) = ")
  print(Hand.compare(h2, h3))
 
  # ----- SingleDeck ----------------------------------------

  print("\nCreating and shuffling deck ")
  d1 = SingleDeck(seed=0)
  d1.shuffle()
  d1.show()

  h4 = d1.deal_hand()
  print("\nDealing Hand from deck: ")
  print(h4.to_string())

  print("\nDealing 38 cards from deck")
  list_cards = d1.deal_list_cards(38)
  print("Deck is now: ")
  d1.show()

  print("\nEnd Poker demo ")

if __name__ == "__main__":
  main()
This entry was posted in Poker. Bookmark the permalink.