Skip to content

Instantly share code, notes, and snippets.

@huytd
Created May 9, 2023 00:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save huytd/57d45dd4de192ba7dbb78c7ab8844e62 to your computer and use it in GitHub Desktop.
Save huytd/57d45dd4de192ba7dbb78c7ab8844e62 to your computer and use it in GitHub Desktop.
Card game Thirteen (a.k.a Tiến lên miền nam) made by Bing Chat
# Step 1: Import the random module
import random
# Step 2: Define a class for cards
class Card:
def __init__(self, rank, suit):
self.rank = rank # an integer from 3 to 15 (3 is lowest and 15 is highest)
self.suit = suit.lower() # a string from "spades", "clubs", "diamonds", or "hearts"
def __str__(self):
# return a string representation of the card using symbols for suits
rank_dict = {11: "J", 12: "Q", 13: "K", 14: "A", 15: "2"}
suit_dict = {"spades": "♠", "clubs": "♣", "diamonds": "♦", "hearts": "♥"}
if self.rank in rank_dict:
rank_str = rank_dict[self.rank]
else:
rank_str = str(self.rank)
suit_str = suit_dict[self.suit]
return rank_str + suit_str
def __lt__(self, other):
# return True if this card is lower than the other card based on rank and suit
if self.rank < other.rank:
return True
elif self.rank == other.rank:
if self.suit == "spades":
return True
elif self.suit == "clubs" and other.suit != "spades":
return True
elif self.suit == "diamonds" and other.suit in ["spades", "clubs"]:
return True
elif self.suit == "hearts" and other.suit == "hearts":
return True
else:
return False
else:
return False
# Step 3: Define a class for decks
class Deck:
def __init__(self):
self.cards = []
# create 52 cards and add them to the deck
for rank in range(3, 16):
for suit in ["spades", "clubs", "diamonds", "hearts"]:
card = Card(rank, suit)
self.cards.append(card)
def shuffle(self):
# shuffle the deck using the random module
random.shuffle(self.cards)
def deal(self):
# deal one card from the top of the deck and return it
return self.cards.pop()
# Step 4: Define a class for hands
class Hand:
def __init__(self):
self.cards = []
def add_card(self, card):
# add a card to the hand
self.cards.append(card)
def remove_cards(self, cards):
# remove a list of cards from the hand
for card in cards:
self.cards.remove(card)
def sort(self):
# sort the hand by rank and suit using the built-in sorted function
self.cards = sorted(self.cards)
def display(self):
# display the hand as a string of cards separated by spaces
return " ".join([str(card) for card in self.cards])
# Step 5: Define a class for players
class Player:
def __init__(self, name):
self.name = name # a string for the player's name
self.hand = Hand() # a hand object for the player's cards
def draw_card(self, deck):
# draw one card from the deck and add it to the hand
card = deck.deal()
self.hand.add_card(card)
def play_cards(self, cards):
# play a list of cards from the hand and return them
self.hand.remove_cards(cards)
return cards
def has_card(self, rank, suit):
# check if the player has a specific card in their hand
for card in self.hand.cards:
if card.rank == rank and card.suit == suit:
return True
return False
def is_empty(self):
# check if the player has no cards left in their hand
return len(self.hand.cards) == 0
# Step 6: Define a function for comparing two combinations of cards
def compare_combos(combo1, combo2):
# compare two combinations of cards based on their type and rank
# return 1 if combo1 is higher than combo2, -1 if combo2 is higher than combo1, or 0 if they are equal or invalid
# get the type and rank of each combination using helper functions (defined later)
type1, rank1 = get_combo_type_and_rank(combo1)
type2, rank2 = get_combo_type_and_rank(combo2)
# compare the types first
if type1 > type2:
return 1
elif type1 < type2:
return -1
else:
# if the types are equal, compare the ranks
if rank1 > rank2:
return 1
elif rank1 < rank2:
return -1
else:
return 0
# Step 7: Define a function for checking if a combination is valid based on the rules of the game
def is_valid_combo(combo, prev_combo=None):
# check if a combination of cards is valid based on the rules of the game
# optionally, check if it can beat the previous combination played
# get the type and rank of the combination using helper functions (defined later)
type, rank = get_combo_type_and_rank(combo)
# if the type is 0, it means the combination is invalid
if type == 0:
return False
# if there is no previous combination, any valid combination can be played
if prev_combo is None:
return True
# otherwise, compare the combination with the previous one using the compare_combos function (defined earlier)
result = compare_combos(combo, prev_combo)
# if the result is 1, it means the combination can beat the previous one
if result == 1:
return True
# otherwise, it cannot beat the previous one or it is equal or invalid
else:
return False
# Step 8: Define a function for getting user input for playing a combination or passing
def get_user_input(player, prev_combo=None):
# get user input for playing a combination or passing
# return a list of cards if the user plays a valid combination
# return an empty list if the user passes or enters an invalid input
# display the player's hand and the previous combination (if any)
print(f"{player.name}, your hand is: {player.hand.display()}")
if prev_combo is not None:
print(f"The previous combination is: {display_combo(prev_combo)}")
# loop until the user enters a valid input
while True:
# prompt the user to enter a combination or pass
user_input = input("Enter a combination of cards or pass: ").strip().upper()
# if the user enters "pass", return an empty list
if user_input == "PASS":
return []
# otherwise, try to parse the user input into a list of cards
try:
cards = []
splitted = user_input.split()
for i in range(0, len(splitted), 2):
rank = splitted[i]
suit = splitted[i + 1]
card = Card(int(rank), suit)
cards.append(card)
print("INPUT")
print(*cards, sep=", ")
print("PLAYER's HAND")
print(*player.hand.cards, sep=", ")
# check if the cards are in the player's hand
for card in cards:
if not player.has_card(card.rank, card.suit):
raise ValueError("You don't have that card in your hand.")
# check if the combination is valid and can beat the previous one (if any)
if is_valid_combo(cards, prev_combo):
# return the list of cards
return cards
else:
raise ValueError("That is not a valid combination.")
# if there is any error in parsing or validating the user input, display an error message and repeat the loop
except ValueError as e:
print(f"Error: {e}")
print("Please enter a valid combination or pass.")
# Step 9: Define a function for displaying the current state of the game
def display_state(players):
# display the current state of the game
# show how many cards each player has left and who is the current leader
# create a list of tuples containing the player's name and number of cards left
stats = [(player.name, len(player.hand.cards)) for player in players]
# sort the list by number of cards in ascending order
stats.sort(key=lambda x: x[1])
# print the list as a table with headers
print("Name\tCards")
print("----\t-----")
for name, cards in stats:
print(f"{name}\t{cards}")
# print who is the current leader based on the first element of the list
leader_name, leader_cards = stats[0]
print(f"The current leader is {leader_name} with {leader_cards} cards left.")
# Step 10: Define a function for simulating the computer's move based on some simple strategy
def get_computer_move(player, prev_combo=None):
# simulate the computer's move based on some simple strategy
# return a list of cards if the computer plays a valid combination
# return an empty list if the computer passes
# sort the player's hand by rank and suit
player.hand.sort()
# if there is no previous combination, play the lowest single card or the 3 of spades (if the player has it)
if prev_combo is None:
if player.has_card(3, "spades"):
return [Card(3, "spades")]
else:
return [player.hand.cards[0]]
# otherwise, try to find the lowest combination that can beat the previous one
else:
# get the type and rank of the previous combination using helper functions (defined later)
prev_type, prev_rank = get_combo_type_and_rank(prev_combo)
# loop through all possible combinations of cards in the player's hand that have the same type as the previous one
for i in range(len(player.hand.cards) - prev_type + 1):
combo = player.hand.cards[i:i+prev_type]
# check if the combination is valid and can beat the previous one using helper functions (defined earlier)
if is_valid_combo(combo, prev_combo):
# return the combination
return combo
# if no such combination is found, pass by returning an empty list
return []
def get_combo_type_and_rank(combo):
n = len(combo)
if n == 0:
return (0, 0)
elif n == 1:
rank = combo[0].rank
return (1, rank)
elif n == 2:
if combo[0].rank == combo[1].rank:
rank = combo[0].rank
return (2, rank)
else:
return (0, 0)
elif n == 3:
if combo[0].rank == combo[1].rank == combo[2].rank:
rank = combo[0].rank
return (3, rank)
else:
return (0, 0)
elif n == 4:
if combo[0].rank == combo[1].rank == combo[2].rank == combo[3].rank:
rank = combo[0].rank
return (4, rank)
else:
return (0, 0)
else:
combo = sorted(combo)
ranks = []
suits = []
for card in combo:
ranks.append(card.rank)
suits.append(card.suit)
if len(set(suits)) == 1 and ranks == list(range(min(ranks), max(ranks) + 1)):
highest_rank = max(ranks)
return (5, highest_rank)
else:
for i in range(1, n - 2):
if suits[i] != suits[i + 1]:
ranks1 = ranks[:i + 1]
suits1 = suits[:i + 1]
ranks2 = ranks[i + 1:]
suits2 = suits[i + 1:]
if len(set(suits1)) == len(set(suits2)) == 1 and ranks1 == list(range(min(ranks1), max(ranks1) + 1)) and ranks2 == list(range(min(ranks2), max(ranks2) + 1)):
highest_rank = max(max(ranks1), max(ranks2))
return (6, highest_rank)
return (0, 0)
def display_combo(combo):
return " ".join([str(card) for card in combo])
def main():
human = Player(input("Enter your name: "))
computer1 = Player("Alice")
computer2 = Player("Bob")
computer3 = Player("Charlie")
players = [human, computer1, computer2, computer3]
random.shuffle(players)
deck = Deck()
deck.shuffle()
for i in range(13):
for player in players:
player.draw_card(deck)
for player in players:
player.hand.sort()
for i, player in enumerate(players):
if player.has_card(3, "spades"):
players = players[i:] + players[:i]
break
prev_combo = None
while True:
for player in players:
display_state(players)
if player == human:
combo = get_user_input(player, prev_combo)
else:
combo = get_computer_move(player, prev_combo)
if len(combo) == 0:
print(f"{player.name} passes.")
else:
print(f"{player.name} plays: {display_combo(combo)}")
prev_combo = combo
if player.is_empty():
print(f"{player.name} has no cards left.")
if player == human:
print("You win!")
else:
print("You lose!")
return
print()
# run the main function
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment