Level 2 Python projects are projects meant for those who have a grasp on the basics of Python already. If you are familiar with most of the Super Simple Python projects and can do each of them in under 30 minutes, you’re ready to go. The goal of the Level 2 Python projects is to get you familiar with using well known Python libraries, building more logically complex applications, and learning API development.
In this tutorial, we’ll be building a simple version of Texas Hold Em. This is an exercise for building more logically complex applications. Not going to lie, I thought this would be a beginner level Python project until I started working on it and I was like oh, this is harder than I thought it would be. In fact, Texas Hold Em is such a complex game, that we will leave the full implementation as a Level 3 Python tutorial. Before we get into the code make sure you have a solid understanding of the basics of Python classes. I’ve attached the source code for this project at the end.
Creating the Card Deck
The first thing we need to do is create a deck of cards. There are 13 values and 4 suits in a standard deck of 52 cards. The values range from 2 to 14 with 11, 12, 13, and 14 being Jack, Queen, King, and Ace respectively. The suits are clubs, diamonds, hearts, and spades. For purposes of displaying the cards in a standard manner, let’s also create a dictionary that translates the numbers to the face cards and vice versa. Each card can be represented as a value and a suit, you can do this with a class or a tuple in Python. I have chosen to do it with a class for added clarity.
import random
# 11 = J, 12 = Q, 13 = K, 14 = A
card_values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
suits = ["clubs", "diamonds", "hearts", "spades"]
face_cards = {
"J": 11,
"Q": 12,
"K": 13,
"A": 14,
11: "J",
12: "Q",
13: "K",
14: "A"
}
class Card:
def __init__(self, value, suit):
self.value = value
self.suit = suit
Once we’ve set up our `Card` class and the lists of possible values and suits, we can generate our deck of cards. Let’s create a `generate_cards` function which will initialize an empty list of cards and populate it. We’ll do this by looping through all the values and suits and initializing a card object for each possible combination. If the value is in the `face_cards` dictionary we created earlier, we’ll save it with the appropriate face card representation (J, Q, K, A). Our `generate_cards` function will then return that list of cards. To use it, we’ll simply set a variable, `cards` equal to the list of cards returned by the function.
def generate_cards():
cards = []
for value in card_values:
for suit in suits:
if value in face_cards:
_card = Card(face_cards[value], suit)
else:
_card = Card(value, suit)
cards.append(_card)
return cards
cards = generate_cards()
Dealing the Cards for the Poker Game
Now that we have a deck of cards stored in our `cards` variable it’s time to deal the cards. We’ll start by making a function to deal a card. Our `deal_card` function will intake a list, `cards`. Unlike real life, we won’t be dealing the top card, we’ll be picking a random card from the deck to deal. We’ll be using the `randint` function from the `random` library we imported earlier. We’ll pick a random index and pop the card. Then we’ll return the card and the edited card deck.
def deal_card(cards):
i = random.randint(0, len(cards)-1)
card = cards[i]
cards.pop(i)
return card, cards
Now that we’ve created a way to deal a card, let’s create a `deal` function to deal hands to the players’. This function will take two parameters, the cards and the number of opponents. For this example, we’ll set the default of the `cards` parameter to the `cards` variable we created earlier and set the default number of opponents to 2.
We’ll start off this function by dealing the opponents’ hands. In real Texas Hold Em, they deal one card at a time, but this is just a simple example. Note that I use the `_` placeholder variable in the for loop, that’s because we never need to access the index. After we deal the opponents’ hands, we deal our hand. Finally, we’ll return two variables, our hand, and the list of our opponents’ hands.
def deal(cards = cards, num_opp = 2):
opp_hands = []
for _ in range(num_opp):
card1, cards = deal_card(cards)
card2, cards = deal_card(cards)
opp_hands.append([card1, card2])
card1, cards = deal_card(cards)
card2, cards = deal_card(cards)
your_hand = [card1, card2]
return your_hand, opp_hands
your_hand, opp_hands = deal()
print([(card.value, card.suit) for card in your_hand])
Let’s print here out here to see what your hand should look like at this point.
Flop, Turn, and River
Other than the player hands that are dealt, Texas Hold Em also has a “table”. The table is dealt as the Flop, Turn, and River. The Flop is the first three cards, the Turn is the fourth, and the River is the fifth. There is usually betting involved between each deal, but we’ll leave that to the advanced course (Level 3). For this we’ll make two functions, `flop`, and `table_deal`. We’ll use `table_deal` for both the turn and river because they’re both the same deal. Could we use the `deal_card` function and simply not deal with the returned deck of `cards`? Yes, but in this tutorial, we won’t. This is just an example of another possible design choice.
Let’s start with the `flop` function. The `flop` function will take `cards` as a parameter and use `deal_card` to deal three cards and then return a list of those cards. The `table_deal` function will take `cards` as a parameter and call `deal_card` to deal a card and return only the card dealt.
We’ll initialize the table using the `flop()` function and print out the cards on the table. Then we’ll call the `table_deal` function two times to deal the Turn and River.
def flop(cards=cards):
card1, cards = deal_card(cards)
card2, cards = deal_card(cards)
card3, cards = deal_card(cards)
return [card1, card2, card3]
def table_deal(cards=cards):
card, cards = deal_card(cards)
return card
table = flop()
print(f"Cards on the table: {[(card.value, card.suit) for card in table]}")
table.append(table_deal())
print(f"Cards after turn: {[(card.value, card.suit) for card in table]}")
table.append(table_deal())
print(f"Cards after river: {[(card.value, card.suit) for card in table]}")
At this point if we print run our program, we should see something like the image below.
Evaluating the Hands
In real Texas Hold Em, you’ll need to compare your hand to the cards on the table and create your highest hand of 5. However, that’s out of the scope of a Level 2 Python project. We’ll cover that in the Level 3 version. Let’s create a function called `evaluate`. The `evaluate` function will take a players’ hand and the cards on the table as its parameters, combine the hands, and evaluate what the highest hand is in that combined set of cards.
To do the evaluation, we’ll need to know the counts of each value and the counts of each suit. We’ll initialize those to empty dictionaries. We’ll also keep a set of values, initialized to an empty set. Why have values separate from the counts of the values? Because `counts` will be used for calculating pairs/triples and `vals` will be used to see if we have a straight. Can we just use a `counts` and later check its indices? Yes, but once again, design choice. It’s just easier to loop through the list in order with `vals`.
We will populate the sets and dictionaries we created earlier by iterating through the cards in the `total_hand`. If the card’s value is in `face_cards` we’ll convert back from the letter representation (J, Q, K, A) into its numerical value. One of the nice things about Python dictionaries is that they can contain indices of any type or combination of types.
def evaluate(hand, table):
total_hand = hand + table
# count values and suit
counts = {}
suits = {}
vals = set()
# loop through all the cards
for card in total_hand:
if card.value in face_cards:
card_value = face_cards[card.value]
else:
card_value = card.value
vals.add(card_value)
if card_value in counts:
counts[card_value] += 1
else:
counts[card_value] = 1
if card.suit in suits:
suits[card.suit] += 1
else:
suits[card.suit] = 1
Sort Counts of Values, Suits, and actual Values
Continuing our function, we’ll sort our `counts`, our `suits`, and `vals`. We do this in reverse order and by the number of occurrences of the value or the suit. Once we’ve gotten everything sorted out, we’ll check for straights and flushes. This is because the order of hands in Poker is high, pair, 2 pair, 3 kind, straight, flush, full house, quads, and straight flush.
# sort counts and suits
sorted_counts = sorted(counts.items(), key=lambda item:(item[1], item[0]), reverse=True)
sorted_suits = sorted(suits.items(), key=lambda item:(item[1], item[0]), reverse=True)
# check if vals contains a straight
run = [sorted(list(vals))[0]]
lastval = sorted(list(vals))[0]
is_straight = False
for val in sorted(list(vals)):
if val - lastval == 1:
run.append(val)
else:
run = [val]
lastval = val
if len(run) == 5:
is_straight = True
break
# check if sorted_suits contains a flush
is_flush = False
if sorted_suits[0][1] == 5:
is_flush = True
Find Highest Hand
Alright let’s get into the annoying logic of this program. Earlier I listed the winning hands in order → high, pair, 2 pair, 3 kind, straight, flush, full house, quads, and straight flush. So, we’re going to check for each of these in backwards order to optimize run time. If we see a straight flush, we’ll return a straight flush automatically. You can set the values of the straight flush as well, but it’s so rare to see (odds are 649,739 to 1) that if you see a straight flush, your chance of winning is almost 100%. After the straight flush, we’ll check for quads based on the highest count of a card value in `counts`. Then we go down the list, a full house is a triple and a double. We’ve already checked for flushes and straights for the straight flush, here we just check them separately for a flush and a straight respectively. After straight, it’s a triple, a two pair, a pair, and then the high card. All of these are easily checked using the `counts` dictionary.
# check for straight flush
if is_straight:
if is_flush:
return "Straight Flush!"
if sorted_counts[0][1] == 4:
return f"Quad {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s!"
if sorted_counts[0][1] == 3:
if sorted_counts[1][1] == 2:
return f"Full house {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s over {face_cards.get(sorted_counts[1][0]) if sorted_counts[1][0] in face_cards else sorted_counts[1][0]}s!"
if is_flush:
return f"Flush in {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
if is_straight:
return f"Straight! {run}"
# check for groups
if sorted_counts[0][1] == 3:
return f"Triple {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s!"
if sorted_counts[0][1] == 2:
if sorted_counts[1][1] == 2:
return f"Two pair {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]} and {face_cards.get(sorted_counts[1][0]) if sorted_counts[1][0] in face_cards else sorted_counts[1][0]}!"
else:
return f"Pair of {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
if sorted_counts[0][1] == 1:
return f"High Card {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
Finalized Function
I just want to add what the finalized function for the Level 2 Python version of evaluating a hand looks like.
def evaluate(hand, table):
total_hand = hand + table
# count values and suit
counts = {}
suits = {}
vals = set()
# loop through all the cards
for card in total_hand:
if card.value in face_cards:
card_value = face_cards[card.value]
else:
card_value = card.value
vals.add(card_value)
if card_value in counts:
counts[card_value] += 1
else:
counts[card_value] = 1
if card.suit in suits:
suits[card.suit] += 1
else:
suits[card.suit] = 1
# sort counts and suits
sorted_counts = sorted(counts.items(), key=lambda item:(item[1], item[0]), reverse=True)
sorted_suits = sorted(suits.items(), key=lambda item:(item[1], item[0]), reverse=True)
# check if vals contains a straight
run = [sorted(list(vals))[0]]
lastval = sorted(list(vals))[0]
is_straight = False
for val in sorted(list(vals)):
if val - lastval == 1:
run.append(val)
else:
run = [val]
lastval = val
if len(run) == 5:
is_straight = True
break
# check if sorted_suits contains a flush
is_flush = False
if sorted_suits[0][1] == 5:
is_flush = True
# check for straight flush
if is_straight:
if is_flush:
return "Straight Flush!"
if sorted_counts[0][1] == 4:
return f"Quad {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s!"
if sorted_counts[0][1] == 3:
if sorted_counts[1][1] == 2:
return f"Full house {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s over {face_cards.get(sorted_counts[1][0]) if sorted_counts[1][0] in face_cards else sorted_counts[1][0]}s!"
if is_flush:
return f"Flush in {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
if is_straight:
return f"Straight! {run}"
# check for groups
if sorted_counts[0][1] == 3:
return f"Triple {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}s!"
if sorted_counts[0][1] == 2:
if sorted_counts[1][1] == 2:
return f"Two pair {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]} and {face_cards.get(sorted_counts[1][0]) if sorted_counts[1][0] in face_cards else sorted_counts[1][0]}!"
else:
return f"Pair of {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
if sorted_counts[0][1] == 1:
return f"High Card {face_cards.get(sorted_counts[0][0]) if sorted_counts[0][0] in face_cards else sorted_counts[0][0]}!"
“Playing” the Poker Game in Python
Now that we’ve finished up all the coding, let’s take a look at running the game. We’ll just have the game automatically play for us by dealing the cards and then calculating the winning hand. This function will take three parameters – your hand, the opponents’ hands, and the cards on the table. It will evaluate all of the hands and print out the highest possible poker hand from each one.
"""
takes your hand, the opponents hands, and the cards on the table
determines the values
returns a winner with all values shown
"""
def determine(hand, opp_hands, table):
print(f"Your highest poker hand: {evaluate(hand, table)}")
for opp in opp_hands:
print(f"Opponent hand: {opp[0].value} {opp[0].suit}, {opp[1].value} {opp[1].suit}")
print(f"Your opponents highest poker hand: {evaluate(opp, table)}")
determine(your_hand, opp_hands, table)
When we run this, we should get an output like the image below. In the image below the first opponent with the pair of 3’s has won. In the Level 2 Python version, we’re not going to implement a way to calculate the winner’s hand.
An Exercise for the Reader
As I stated above, there are some things that I have left for the Level 3 Python version of Texas Hold Em. The reason I’ve left these out of the Level 2 Python version is because they add quite some complexity to the program. For example, the real way to calculate a hand requires you to sort through 10 different possible hands (5 choose 3 is 10) and pick the highest one. There’s quite a few things I’ve left out on purpose that you can try to figure out or wait for me to drop the Level 3 article to find out 🙂
Thanks for reading! Here’s the Source Code.
Further Reading
- Python Counting Sort Guide and Implementation
- Create Animations with
animation.funcanimation
- Level 2 Python: Creating Your Own Quiz
- Build Your Own AI Text Summarizer
- Neural Network Code in Python
I run this site to help you and others like you find cool projects and practice software skills. If this is helpful for you and you enjoy your ad free site, please help fund this site by donating below! If you can’t donate right now, please think of us next time.
Make a one-time donation
Your contribution is appreciated.
Donate
Make a monthly donation
Your contribution is appreciated.
Donate monthly
Make a yearly donation
Your contribution is appreciated.
Donate yearly
In this tutorial, you will learn step-by-step how to implement a poker bot in Python.
Step 1 – Setup Python and Install Packages
First, we need an engine in which we can simulate our poker bot. Install the following package (PyPokerEngine) using pip:
1 |
|
It also has a GUI available which can graphically display a game. If you are interested, you can optionally install the following package (PyPokerGUI):
Both the engine and the GUI have excellent tutorials on their GitHub pages in how to use them. The choice for the engine (and/or the GUI) is arbitrary and can be replaced by any engine (and/or GUI) you like. However, the implementation of the bot in this tutorial depends on this choice, so you need to rewrite some if the code if you plan to change the engine (and/or GUI).
Small note on the GUI: it did not work for my directly using Python 3. This fix explains how to make it work.
Step 2 – Implement your Bot!
The first step is to setup the skeleton of the code such that it works. In order to do so, I created three files. One file containing the code for the bot (databloggerbot.py), another file containing the code for a bot which always calls and another file for simulating one game of poker (simulate.py) in which many runs are simulated. The files initially have the following contents:
1 |
|
databloggerbot.py
1 |
|
callbot.py
1 |
|
simulate.py
The bot uses Monte Carlo simulations running from a given state. Suppose you start with 2 high cards (two Kings for example), then the chances are high that you will win. The Monte Carlo simulation then simulates a given number of games from that point and evaluates which percentage of games you will win given these cards. If another King shows during the flop, then your chance of winning will increase. The Monte Carlo simulation starting at that point, will yield a higher winning probability since you will win more games on average.
If we run the simulations, you can see that the bot based on Monte Carlo simulations outperforms the always calling bot. If you start with a stack of $100,-, you will on average end with a stack of $120,- (when playing against the always-calling bot).
It is also possible to play against our bot in the GUI. You first need to setup the configuration (as described in the Git repository) and you can then run the following command to start up the GUI:
1 |
|
Good luck beating the bot!
Conclusion (TL;DR)
In this simple tutorial, we created a bot based on Monte Carlo simulations. In a later blog post, we will implement a more sophisticated Poker bot using different AI methods. If you have any preference for an AI method, please let me know in the comments!
To put it simply, this article will discuss all the ins and outs of developing a computer vision-based poker bot in Python. Developers can refer to the information given below while building bots for routinely testing their web poker platforms. It’s essential mentioning that the codes and libraries necessary for this task are typically Windows-specific.
Is It Possible To Make A Poker Bot?
There’s no denying that ever since online poker platforms started emerging back in the early 2000s, people have been trying hard to find out how to beat a poker bot for greater rewards. Whenever any hardworking grinder took up the mantle of honing their bluffs, others have delegated the grafting almost instantaneously.
This was when poker bots entered the picture, primarily as a form of artificial intelligence. All the founding bright minds decided to use poker bots to profitably “solve” poker.
Creating a Poker Bot on Python
There’s no denying that digital language programming seems like a foreign language to any first-timer when they begin studying it. But then again, with all the resources available on the internet, the process of learning more about programming becomes convenient. Similarly, people can dive back on the internet to find niche resources that explore the basics of creating a bot.
How Do You Set Up A Poker Bot?
Here’s a detailed breakdown of how you set up a poker bot from scratch. You can use the below-listed commands to determine players:
- Import Pypokerengine
- From Consoleplayer Import Consoleplayer
- From Fishplayer Import Fishplayer
- From Randomplayer Import Randomplayer
Up next, you must enter the code for building a poker cash tournament or game. Eventually, the developer must allow the game to play out and judge the success of their bot. It’s noteworthy that your Artificial Intelligence (AI) won’t function as per satisfaction if you don’t employ any advanced programming.
Are Poker Bots Illegal?
Most people believe that internet poker rooms struggle big time arresting AI. This is primarily since the lack of a qualified security team. Most poker sites are on the hunt for efficient bots. It is essential mentioning that they’ll suspend associated accounts that use programs.
How Do Poker Sites Detect Bots?
There’s no denying that it’s hardly possible for anybody to play poker online across the clock. However, they put a substitution in place of them, i.e., some poker bots. This allows them continuous gameplay until the poker rooms realize. For any first-timer, it’s essential to note that bots can win real cash for the players.
How Much Can You Win With A Bot?
In conclusion, it only fits mentioning that not every poker bot is a winner. There are loads of players who have gone gung-ho with such programs and still lose. And accompanying the problem is that players could face suspensions from poker sites when caught. That’s undoubtedly a bummer- developing or purchasing an AI, only to get banned from the poker sites instead of earning cash.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
def check_combination(cards: list) -> str: minimal_value = min(cards) if sorted(cards) == list(range(minimal_value, minimal_value + 5)): return 'Straight' uniques = list(set(cards)) uniques_length = len(uniques) if uniques_length == 1: return 'Impossible' if uniques_length == 4: return 'One Pair' if uniques_length == 2: if any(filter(lambda x: cards.count(x) == 4, uniques)): return 'Four of a Kind' else: return 'Full House' if uniques_length == 3: if any(filter(lambda x: cards.count(x) == 3, uniques)): return 'Three of a Kind' else: return 'Two Pairs' return 'Nothing' assert check_combination([1, 1, 1, 1, 1]) == "Impossible" assert check_combination([1, 1, 1, 1, 0]) == "Four of a Kind" assert check_combination([1, 1, 1, 2, 2]) == "Full House" assert check_combination([1, 2, 3, 4, 5]) == "Straight" assert check_combination([1, 5, 3, 2, 4]) == "Straight" assert check_combination([1, 1, 1, 2, 4]) == "Three of a Kind" assert check_combination([1, 1, 2, 2, 4]) == "Two Pairs" assert check_combination([1, 1, 2, 3, 4]) == "One Pair" assert check_combination([1, 9, 2, 3, 4]) == "Nothing" |
How to make a poker game in Python?
#poker print("poker username: ") name = input("") print("age: ") age = input("") #continue input("nnPress the [enter key]") #make random cards by importing import random #player cards print("your cards: ") card1 = random.randint(1, 12) card2 = random.randint(1, 12) player_cards = card1, card2 print(player_cards) #first table cards table1 = random.randint(1, 12) table2 = random.randint(1, 12) table3 = random.randint(1, 12) table_full = table1, table2, table3 print("the first table cards are:") print(table_full) #bet print("put in the amount you would like to bet") bet1 = input("") #next table card table4 = random.randint(1, 12) print("full table is now: ") table = table1, table2, table3, table4 print(table) #bet number 2 print("put in the amount you would like to bet") bet2 = input("") #next next table card table5 = random.randint(1, 12) print("full table is now: ") table12345 = table1, table2, table3, table4, table5 print(table12345) #loser or winner? print("player2 had: ") card12 = random.randint(1, 12) card22 = random.randint(1, 12) player2 = card12, card22 print(player2) #full layout print("this is the full table now") print(player_cards) print(table12345) print(player2) #play again? play = input("do you want to play again") if play == "yes": exec(open("./pokerface.py").read()) else: exit()
First impressions — this code is very clear. I like the variable names — that makes lines like if straight and flush
read very naturally.
I don’t think there’s a need to treat a Royal Flush separately — this is the same rank as Straight Flush, just with the largest possible high-card. The «should not occur» comment is irrelevant — no matter how rare, we must correctly score Straight Flush (and we would expect a thorough test set to include some).
Instead of needing a separate tiebreaker
function to evaluate the kickers, we could return a tuple from eval_hand
, with the kickers following the rank+value. We’re already doing something like that for Full House; it’s easy to extend that throughout. I’d collect values
in descending order:
values = sorted([c[0] for c in hand], reverse=True)
straight = (values == range(max(values), min(values)-1, -1)) # but see below
Then we can return as much as is necessary for the rank:
if straight and flush:
return 8, values[0]
if three_of_a_kind and pair_present: return 6, three_value, pairs[0], values
if flush: return 5, values
if straight: return 4, values[0]
if three_of_a_kind: return 3, three_value, values
if len(pairs) == 2: return 2, pairs, values
if len(pairs) == 1: return 1, pairs[0], values
Counting pairs/trips/quads can be simplified, given that we have the values in sorted order. We can use itertools.groupby()
to get the count of identical values, rather than needing to search with values.count()
:
trips = []
pairs = []
for v, group in itertools.groupby(values):
count = sum(1 for _ in group)
if count == 4:
return 7, v, values
if count == 3:
trips.append(v)
elif count == 2:
pairs.append(v)
Some additional test cases indicated a few problems:
-
The test for a straight doesn’t work when I compare a list against a range (Python 3.6.5). I needed to materialise it as a list first:
straight = values == list(range(values[0], values[-1]-1, -1))
-
We don’t test for an ace-low straight:
straight = (values == list(range(values[0], values[0]-5, -1)) or values == [14, 5, 4, 3, 2])
-
Flushes should be comparable — highest differing card still tie-breaks between flushes of different suits.
And some simplifications:
- Straights and flushes can both be returned before checking for pairs/trips/quads
- We don’t need boolean flags to say that
three_value
orpairs
is populated — we can directly test those variables are truthy or not. - For ranks 0, 1, 2, the rank is exactly the number of pairs found.
- Instead of
try
/except
, we can populatevalue_dict
with the digits 2 to 9. - We’re not required to find anything other than the number of Player 1 wins, so remove the code that was useful while debugging.
Applying the above, I get the following modified code:
import itertools
value_dict = {'T': 10, 'J': 11, 'Q': 12, 'K': 13, 'A': 14}
value_dict.update((str(x), x) for x in range(2,10))
def eval_hand(hand):
# Return ranking followed by tie-break information.
# 8. Straight Flush
# 7. Four of a Kind
# 6. Full House
# 5. Flush
# 4. Straight
# 3. Three of a Kind
# 2. Two pair
# 1. One pair
# 0. High card
values = sorted([c[0] for c in hand], reverse=True)
suits = [c[1] for c in hand]
straight = (values == list(range(values[0], values[0]-5, -1))
or values == [14, 5, 4, 3, 2])
flush = all(s == suits[0] for s in suits)
if straight and flush: return 8, values[1]
if flush: return 5, values
if straight: return 4, values[1]
trips = []
pairs = []
for v, group in itertools.groupby(values):
count = sum(1 for _ in group)
if count == 4: return 7, v, values
elif count == 3: trips.append(v)
elif count == 2: pairs.append(v)
if trips: return (6 if pairs else 3), trips, pairs, values
return len(pairs), pairs, values
player1_wins = 0
with open("p054_poker.txt") as f:
for line in f:
cards = [(value_dict[x[0]], x[1]) for x in line.split(' ')]
player1_wins += eval_hand(cards[:5]) > eval_hand(cards[5:])
print(player1_wins)