Programming Challenge: Sorting poker hands


In this programming challenge, you will create a program to rank hands of cards according to the rules of Poker. In the game of Poker, like in many other, players have to build simple patterns: four identical cards, three identical cards, contiguous increasing series of cards, etc. Computers can be programmed to recognise those patterns. And it is your challenge to make such a program!

Simple rules, simple program

There are several rules for the game of Poker, but the ranking of hands is always the same: five of a kind, straight flush, four of a kind, full house, etc. You can check the Wikipedia page on the topic for full details.

For simplicity, you can ignore wild cards and use a 52 cards deck. This excludes the “five of a kind” hand.

The challenge for you is to write a single function that compares two hands of five cards. Check the replies below for skeleton code and hints on where to start.

Suits in Unicode

The four suits of a game of card – clubs, diamond, hearts, and spades – are common markers, for cards and other things. So much so that they’ve been added to the Unicode character set. Depending on the fonts installed on your system, these characters might not display correctly: :clubs:, :diamonds:, :heart:, :spades:.

Unicode is a collection of character that aims to be universal and to contain all the characters of most writing systems. Unicode also contains many symbols from mathematics and music notations. And games: the suits of cards, the chess pieces, the 6-sided dice, etc.

Unicode is important because it helps everyone use computers – without having to learn a new language.

Unicode and UTF-8

Unicode is a list of characters. The Unicode standard is a big list of entries that all look like so: “the character number 26149 is ‘春’.” This is useful if you want to wish people a Happy Chinese New Year (春节).

However, it is not practical to tell people to look up that gigantic table that contains over 136,000 characters. Instead, you can type the characters on your keyboard, or at least copy-paste them from the Internet. This works because your computer encodes the number corresponding to each character into a series of 0s and 1s.

A common way to encode Unicode characters into a series of 0s and 1s is UTF-8. In the UTF-8 encoding, the 26149th character, “春”, is encoded as 111001101001100010100101. Fortunately, you do not need to read all those numbers because your computer can simply display them on your screen… provided you have the necessary fonts installed.

(Note that the entries in the Unicode character set have a bit more information than just the number and character. For example, it indicates the type of character: a number, a letter, a diacritic, a symbol, an emoticon, etc.)

Other pattern games

If poker is not your thing, or if you finish the challenge quickly and want to do more, you can try to make programs that recognise the patterns of the following games.

Yatzee is a game in which players build patterns of dice rolls: four of a kind, five of a kind, contiguous increasing sequence, etc. You can find the scoring rules on

Mahjong is a game in which players build patterns of tiles: three of a kind, four of a kind, contiguous increasing sequence, etc. You can find the scoring rules on

Note that both dice faces (⚀, ⚁, ⚂, ⚃, ⚄, ⚅) and mahjong tiles (🀐, 🀑, 🀒, etc.) are represented in Unicode.


You can start by defining constants for manipulating cards:

CLUB = "club"
DIAMOND = "diamond"
HEART = "heart"
SPADE = "spade"


ACE = 1
TWO = 2
# etc.

RANKS = list(range(1,14))

Then, decide how to represent cards using these constants. A simple way is as a tuple with the suit and rank. A few useful functions can be coded for that:

def suit_of_card(card):
    return card[0]
def rank_of_card(card):
    return card[1]

Then, to represent a hand you can simply use a list of cards.

And now you have all the basic infrastructure to start the programming. A good place to start is to make functions that recognise specific hands.

def is_straight_flush(hand):
    # start work here

def is_four_of_a_kind(hand):
    # and then here

def is_full_house(hand):
    # and here

# etc.

If you get stuck, try to think of useful intermediate functions you can write. For example, in hands such as four-of-a-kind you can ignore the suit of all cards; so having a function that removes the suit information from a hand makes the function is_four_of_a_kind simpler.


Here’s a few tips if you are stuck or if you are not sure how to start with is_straight_flush. This function can be very long if you just list all the possible straight flush that can exist. You’d need to list all of them for a suit, and then copy-paste for all three other suits.

Below is a solution to recognise the pattern of straight flush without having a complete list of all of them. It works by erasing some information that is somewhat irrelevant.

def all_have_suit(hand, suit):
    # Simply checks that all the cards of `hand` have the suit `suit`.
    # If the syntax below looks strange, read about comprehensions
    return all([suit_of_card(card) == suit for card in hand])

def all_same_suit(hand):
    # This just checks that the cards have the same suit.
    # It simply checks each suit one by one
    for suit in SUITS:
        if all_have_suit(hand, suit):
            return True
    return False

The preliminary functions above are interesting to check the suit part of the straight-flush. Once it is determined that the suits are identical, checking for a straight flush pattern is pretty easy.

def sorted_ranks_of_hand(hand):
    # extracts the ranks of each card and sort them
    return sorted([rank_of_card(card) for card in hand])

def shifted_ranks_of_ranks(ranks, idx):
    # shifts all the ranks so that the one at index idx is 0
    return [rank - ranks[idx] for rank in ranks]

The two functions above erase information that doesn’t matter to much to recognise a flush pattern. Basically, straight flush can be shifted up or down in ranks, they are still straight flush. So the helper functions help summarise the hand information to make it simpler to recognise the pattern.

def is_straight_flush(hand):
    # First, check that the suits are the same
    if not all_same_suit(hand):
        return False

    # Then summarise the rank information
    sorted_ranks = sorted_ranks_of_hand(hand)
    shifted_ranks = shifted_ranks_of_ranks(sorted_ranks, 0)

    # Then recognise the summarised patterns
    if shifted_ranks == [0, 1, 2, 3, 4]:
        # General pattern
        return True
    if shifted_ranks == [0, 9, 10, 11, 12]:
        # Specific pattern when there's an ace that counts as 13
        return True

    # In case no pattern was recognised
    return False

For other functions, you can also summarise the hand information to simplify the pattern checking.


Below is a (partial) solution. All of the glue is there as well as guidance on completing the missing parts.

Distinguishing hand types and hand values

Remember the function to recognise a straight flush:

def is_straight_flush(hand):
    if not all_same_suit(hand):
        return False
    sorted_ranks = sorted_ranks_of_hand(hand)
    shifted_ranks = shifted_ranks_of_ranks(sorted_ranks, 0)
    if shifted_ranks == [0, 1, 2, 3, 4]:
        return True
    if shifted_ranks == [0, 9, 10, 11, 12]:
        return True
    return False

This function by itself is not sufficient. Indeed, there is an order between hands (the straight flush is of greater value than the four of a kind), but there is also an order amongst hands of the same type.

To compare two straight flushes, the ranks of their cards are compared. You need a function to extract the rank of a straight flush. This function is relatively simple, but you need to be careful when the hand includes an ace.

def straight_flush_start(hand):
    if is_straight_flush(hand):
        sorted_ranks = sorted_ranks_of_hand(hand)
        shifted_ranks = shifted_ranks_of_ranks(sorted_ranks, 0)
        if shifted_ranks == [0, 1, 2, 3, 4]:
            # common case: the start is the value of the lowest card
            return sorted_ranks[0]
        if shifted_ranks == [0, 9, 10, 11, 12]:
            # special case: ignore the ace that shows up as lowest card
            return sorted_ranks[1]
        # Handling errors by returning a recognisably
        # non-sensical value
        return -1

Four of a kind

And so, to compare hands, all you need is a way to recognise the hand pattern and a way to extract the value for that specific type.

Here is an example for the four of a kind.

def is_four_of_a_kind(hand):
    sorted_ranks = sorted_ranks_of_hand(hand)
    first_shifted_ranks = shifted_ranks_of_ranks(sorted_ranks, 0)
    if first_shifted_ranks[0:4] == [0, 0, 0, 0]:
        # The pattern is X X X X Y
        return True
    last_shifted_ranks = shifted_ranks_of_ranks(sorted_ranks, -1)
    if last_shifted_ranks[1:5] == [0, 0, 0, 0]:
        # The pattern is X Y Y Y Y
        return True
    # Or the pattern is something else
    return False

To give a value to a four of a kind hand, you simply take the rank of the cards that compose it. There is a simple trick: the second card of the sorted hand is always part of the four. And there is one special case to be wary of: aces (which have a higher value than other cards).

def four_of_a_kind_rank(hand):
    if is_four_of_a_kind(hand):
        sorted_ranks = sorted_ranks_of_hand(hand)
        # The ranks of a four of a kind are either of those two
        # patterns: X Y Y Y Y or X X X X Y
        # In any case, the second element (index 1) is always part
        # of the four
        rank = sorted_ranks[1]
        if ranks == ACE:
            return 14 # higher than other cards
            return rank
        return -1

Glue using lexicographic order

Once you have those two functions for each of the hand type, you can glue it all together. The main idea in the program below is to create a tuple where the first component is the value associated to the general pattern and the second component is the value of hands within that pattern.

This is interesting because Python uses the lexicographic order when comparing tuples. This means that, when comparing two tuples, it first checks which of the first components is the highest. If these components are equal, it then compares the second components.

You can then leverage this order to compare tuples that encode the value of the hand. Indeed, it first compares the hand type (which is the most important characteristic) and then the value within that hand (which is only relevant for hands of the same type).

# Hand value order

def value_of_hand(hand):
    if is_straight_flush(hand):
        secondary_value = straight_flush_start(hand)
        return (STRAIGHT_FLUSH, secondary_value)
    if is_four_of_a_kind(hand):
        secondary_value = four_of_a_kind_rank(hand)
        return (FOUR_OF_A_KIND, secondary_value)
    # Etc. for each hand type

With this function ready, it is trivial to build the comparison function:

def compare(hand_1, hand_2):
    # This function returns
    # - 0 if the hands are of equal value,
    # - +1 if the first hand is of greater value
    # - -1 if the second hand is of greater value
    value_1 = value_of_hand(hand_1)
    value_2 = value_of_hand(hand_2)
    if value_1 == value_2:
        return 0
    elif value_1 > value_2:
        return 1
    elif value_1 < value_2:
        return (-1)


You can test that the code works by giving the function hands to compare:

if compare(h1, h2) == 1: