Create a Simple 5-Card Draw Poker Game in Python with Asyncio
Rain Leander
Posted on April 1, 2023
In this blog post, we will create a simple 5-card draw poker game in Python using the asyncio library. The game will allow 2 to 4 players to play without a betting aspect, but it will determine a winner based on their poker hands. The main purpose of using the asyncio library in this example is to familiarize ourselves with asynchronous programming concepts, even though our game does not require concurrent execution.
Asynchronous programming is a programming paradigm that allows multiple tasks to be performed concurrently without waiting for one task to finish before starting the next one. This is particularly useful in situations where tasks involve I/O-bound operations, such as reading from a file or making network requests, which can take a significant amount of time. The asyncio library is an essential component of asynchronous programming in Python. It provides an event loop, coroutines, and other utilities that enable developers to write efficient, non-blocking code, significantly improving the performance and responsiveness of applications, particularly in networking and web-based contexts.
In this tutorial, we will leverage the asyncio library to create a simple poker game, demonstrating the power and flexibility of asynchronous programming in Python.
Requirements:
Python 3.7+
Step 1: Importing necessary libraries and defining the dataclasses
First, let's import the required libraries and define our dataclasses for Card, Rank, Suit, and GameState:
import asyncio
import random
from collections import Counter
from dataclasses import dataclass
from enum import Enum, auto
from typing import List, Tuple
class Suit(Enum):
SPADES = "♠"
HEARTS = "♥"
DIAMONDS = "♦"
CLUBS = "♣"
class Rank(Enum):
TWO = 2
THREE = 3
FOUR = 4
FIVE = 5
SIX = 6
SEVEN = 7
EIGHT = 8
NINE = 9
TEN = 10
JACK = 11
QUEEN = 12
KING = 13
ACE = 14
@dataclass
class Card:
suit: Suit
rank: Rank
def __str__(self):
return f"{self.rank.name.capitalize()}{self.suit.value}"
@dataclass
class GameState:
deck: List[Card]
players: List[List[Card]]
Step 2: Implementing the deck creation and shuffling functions
Now, let's create a function to create a deck of cards and another function to shuffle the deck using asyncio:
def create_deck() -> List[Card]:
return [Card(suit, rank) for suit in Suit for rank in Rank]
async def shuffle_deck(deck: List[Card]) -> List[Card]:
await asyncio.sleep(0) # simulating asynchronous behavior
random.shuffle(deck)
return deck
Step 3: Dealing and ranking hands
Next, we need to implement a function to deal cards from the deck and a function to rank the hands of the players:
async def deal_cards(game_state: GameState, num_cards: int) -> List[Card]:
new_cards = []
for _ in range(num_cards):
card = game_state.deck.pop()
new_cards.append(card)
return new_cards
def rank_hand(hand: List[Card]) -> Tuple[int, List[int]]:
ranks = sorted([card.rank.value for card in hand], reverse=True)
suits = [card.suit for card in hand]
rank_counts = Counter(ranks)
is_flush = len(set(suits)) == 1
is_straight = len(set(ranks)) == 5 and max(ranks) - min(ranks) == 4
# Determine hand rank based on poker hand rankings
# ... (refer to the previous code snippets for the full rank_hand function)
Step 4: Drawing cards and playing the game
Now, let's add the functionality to draw new cards after discarding and the main game loop:
async def draw_cards(game_state: GameState, player_idx: int, discard_indices: List[int]) -> None:
player_hand = game_state.players[player_idx]
for index in sorted(discard_indices, reverse=True):
del player_hand[index]
new_cards = await deal_cards(game_state, len(discard_indices))
game_state.players[player_idx] = player_hand + new_cards
async def play_game(num_players: int) -> None:
deck = await shuffle_deck(create_deck())
game_state = GameState(deck=deck, players=[[] for _ in range(num_players)])
for i in range(num_players):
game_state.players[i] = await deal_cards(game_state, 5)
for i, player_hand in enumerate(game_state.players):
print(f"Player {i + 1}'s hand: {', '.join(str(card) for card in player_hand)}")
for i in range(num_players):
discard_indices = input(f"Player {i + 1}, enter the indices of the cards to discard (0-4, separated by spaces): ")
discard_indices = [int(index) for index in discard_indices.split()]
await draw_cards(game_state, i, discard_indices)
for i, player_hand in enumerate(game_state.players):
print(f"Player {i + 1}'s final hand: {', '.join(str(card) for card in player_hand)}")
hand_ranks = [rank_hand(hand) for hand in game_state.players]
max_rank = max(hand_ranks)
winner_idx = hand_ranks.index(max_rank)
print(f"Player {winner_idx + 1} wins with a {', '.join(str(card) for card in game_state.players[winner_idx])}!")
Step 5: Running the game
Finally, let's add the main function to run the game:
if __name__ == "__main__":
num_players = int(input("Enter the number of players (2-4): "))
while not (2 <= num_players <= 4):
num_players = int(input("Enter a valid number of players (2-4): "))
asyncio.run(play_game(num_players))
Now, save the code in a file named poker.py and run it using the following command:
python3 poker.py
Congratulations! You've created a simple 5-card draw poker game in Python using the asyncio library. Although the primary purpose was to familiarize ourselves with asynchronous programming, the game can be further extended and improved with additional features, such as betting, improved user interface, or even networked multiplayer capabilities.
Happy coding and enjoy playing poker!
Posted on April 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.