mirror of
https://github.com/OpenBMB/ChatDev.git
synced 2024-11-07 18:40:13 +03:00
255 lines
11 KiB
Python
255 lines
11 KiB
Python
'''
|
|
Game module that manages the Texas Hold'em Poker game.
|
|
'''
|
|
from player import HumanPlayer, AIPlayer
|
|
from deck import Deck
|
|
class Game:
|
|
def __init__(self):
|
|
self.players = []
|
|
self.deck = Deck()
|
|
self.community_cards = []
|
|
self.small_blind = 5
|
|
self.big_blind = 10
|
|
self.current_bet = 0
|
|
self.pot = 0
|
|
def start(self):
|
|
self.initialize_players()
|
|
self.initialize_blinds()
|
|
self.play_round()
|
|
def initialize_players(self):
|
|
self.players.append(HumanPlayer("Human", 1000))
|
|
self.players.append(AIPlayer("AI 1", 1000))
|
|
self.players.append(AIPlayer("AI 2", 1000))
|
|
self.players.append(AIPlayer("AI 3", 1000))
|
|
self.players.append(AIPlayer("AI 4", 1000))
|
|
def initialize_blinds(self):
|
|
for player in self.players:
|
|
if player.name == "AI 4":
|
|
player.blind = self.small_blind
|
|
elif player.name == "Human":
|
|
player.blind = self.big_blind
|
|
def play_round(self):
|
|
while len(self.players) > 1:
|
|
self.deck.shuffle()
|
|
self.reset_round()
|
|
self.collect_blinds()
|
|
self.deal_hole_cards()
|
|
self.play_betting_round()
|
|
if len(self.players) > 1:
|
|
self.deal_community_cards()
|
|
self.play_betting_round()
|
|
self.deal_community_cards()
|
|
self.play_betting_round()
|
|
self.deal_community_cards()
|
|
self.play_betting_round()
|
|
if len(self.players) > 1:
|
|
self.determine_winner()
|
|
elif len(self.players) == 1:
|
|
self.players[0].chips += pot
|
|
def reset_round(self):
|
|
self.community_cards = []
|
|
self.current_bet = 0
|
|
self.pot = 0
|
|
for player in self.players:
|
|
player.reset()
|
|
def collect_blinds(self):
|
|
for player in self.players:
|
|
if player.blind == self.small_blind:
|
|
self.pot += player.blind_bet(self.small_blind)
|
|
elif player.blind == self.big_blind:
|
|
self.pot += player.blind_bet(self.big_blind)
|
|
def deal_hole_cards(self):
|
|
for player in self.players:
|
|
player.receive_cards(self.deck.draw(2))
|
|
def play_betting_round(self):
|
|
for player in self.players:
|
|
if len(self.community_cards) != 0:
|
|
player.previous_bet = 0
|
|
player.blind = 0
|
|
self.current_bet = 0
|
|
|
|
else:
|
|
self.current_bet = 10
|
|
|
|
if len(self.community_cards) != 0:
|
|
print (f"Community cards: {self.community_cards}")
|
|
|
|
print (f"Pot: {self.pot}")
|
|
current_player_index = self.get_next_player_index(0)
|
|
last_raiser_index = None
|
|
while True:
|
|
current_player = self.players[current_player_index]
|
|
if current_player.is_active():
|
|
action = current_player.make_decision(self.current_bet)
|
|
if action == "fold":
|
|
current_player.fold()
|
|
elif action == "check":
|
|
if last_raiser_index is not None:
|
|
if (self.current_bet > current_player.previous_bet):
|
|
print(f"{current_player.name} attempted to check. Forcing fold...")
|
|
current_player.fold() #Bot likes to check to avoid calling, force them to fold with this
|
|
else:
|
|
current_player.check()
|
|
elif action == "call":
|
|
if last_raiser_index is not None: #Someone raised
|
|
if (amount >= current_player.chips): #All in
|
|
self.pot += current_player.all_in()
|
|
else: #Has the chips to call
|
|
prev_bet = current_player.call(self.current_bet - current_player.previous_bet - current_player.blind)
|
|
self.pot += prev_bet
|
|
current_player.previous_bet += prev_bet + current_player.blind
|
|
current_player.blind = 0
|
|
else: #No one raised, pay big blind
|
|
self.pot += current_player.call(self.big_blind - current_player.blind)
|
|
self.previous_bet = 10
|
|
current_player.previous_bet = 10
|
|
current_player.blind = 0
|
|
elif action == "raise":
|
|
amount = current_player.get_raise_amount(self.current_bet)
|
|
new_amount = amount + self.current_bet*2 - current_player.blind - current_player.previous_bet
|
|
self.current_bet = new_amount + current_player.blind + current_player.previous_bet
|
|
current_player.blind = 0
|
|
self.pot += current_player.raise_bet(new_amount)
|
|
print(f"Current top bet is: {self.current_bet}")
|
|
last_raiser_index = current_player_index
|
|
current_player.previous_bet = self.current_bet
|
|
current_player_index = self.get_next_player_index(current_player_index)
|
|
if current_player_index == last_raiser_index:
|
|
break
|
|
def deal_community_cards(self):
|
|
if len(self.players[0].community_cards) == 0:
|
|
self.community_cards.extend(self.deck.draw(3))
|
|
elif len(self.players[0].community_cards) == 3:
|
|
self.community_cards.extend(self.deck.draw(1))
|
|
elif len(self.players[0].community_cards) == 4:
|
|
self.community_cards.extend(self.deck.draw(1))
|
|
for player in self.players:
|
|
player.receive_community_cards(self.community_cards)
|
|
def determine_winner(self):
|
|
best_hand_value = 0
|
|
winners = []
|
|
for player in self.players:
|
|
if player.is_active():
|
|
hand_value = self.calculate_hand_value(player)
|
|
if hand_value > best_hand_value:
|
|
best_hand_value = hand_value
|
|
winners = [player]
|
|
elif hand_value == best_hand_value:
|
|
winners.append(player)
|
|
|
|
if len(winners) == 1:
|
|
winning_player = winners[0]
|
|
print(f"{winning_player.name} wins with {self.get_hand_name(best_hand_value)}!")
|
|
winning_player.chips += self.pot
|
|
else:
|
|
print("It's a tie!")
|
|
|
|
def calculate_hand_value(self, player):
|
|
# Implement logic to calculate the unique hand value as described in the provided information.
|
|
hand_type = self.determine_hand_type(player.hand)
|
|
card_values = [self.get_card_value(card) for card in player.hand]
|
|
hand_value = hand_type
|
|
|
|
for card_value in card_values:
|
|
hand_value = (hand_value << 4) + card_value
|
|
|
|
print(f"{player.name} with cards: {player.hand} has hand value: {hand_value}")
|
|
return hand_value
|
|
|
|
def determine_hand_type(self, hand):
|
|
# Sort the hand in descending order based on card values
|
|
sorted_hand = sorted(hand, key=lambda card: self.get_card_value(card), reverse=True)
|
|
|
|
# Check for specific hand types in decreasing order of rank
|
|
if self.is_royal_flush(sorted_hand):
|
|
return 9 # Royal flush
|
|
if self.is_straight_flush(sorted_hand):
|
|
return 8 # Straight flush
|
|
if self.is_four_of_a_kind(sorted_hand):
|
|
return 7 # Four of a kind
|
|
if self.is_full_house(sorted_hand):
|
|
return 6 # Full house
|
|
if self.is_flush(sorted_hand):
|
|
return 5 # Flush
|
|
if self.is_straight(sorted_hand):
|
|
return 4 # Straight
|
|
if self.is_three_of_a_kind(sorted_hand):
|
|
return 3 # Three of a kind
|
|
if self.is_two_pair(sorted_hand):
|
|
return 2 # Two pair
|
|
if self.is_pair(sorted_hand):
|
|
return 1 # Pair
|
|
|
|
return 0 # High card
|
|
|
|
def get_card_value(self, card):
|
|
# Map card ranks (2, 3, 4, etc.) to their corresponding values (0 to 12)
|
|
rank_to_value = {
|
|
"2": 0, "3": 1, "4": 2, "5": 3, "6": 4, "7": 5, "8": 6,
|
|
"9": 7, "10": 8, "J": 9, "Q": 10, "K": 11, "A": 12
|
|
}
|
|
rank = card.split()[0] # Extract the rank from the card string
|
|
return rank_to_value.get(rank, 0) # Default to 0 for unknown ranks
|
|
|
|
def get_hand_name(self, hand_type):
|
|
# Map hand types to their human-readable names
|
|
hand_names = {
|
|
9: "Royal flush", 8: "Straight flush", 7: "Four of a kind",
|
|
6: "Full house", 5: "Flush", 4: "Straight",
|
|
3: "Three of a kind", 2: "Two pair", 1: "Pair", 0: "High card"
|
|
}
|
|
return hand_names.get(hand_type, "Unknown")
|
|
|
|
def is_royal_flush(self, hand):
|
|
# Check for a Royal flush (A, K, Q, J, 10 of the same suit)
|
|
return self.is_straight_flush(hand) and self.get_card_value(hand[0]) == 12
|
|
|
|
def is_straight_flush(self, hand):
|
|
# Check for a Straight flush (five consecutive cards of the same suit)
|
|
return self.is_straight(hand) and self.is_flush(hand)
|
|
|
|
def is_four_of_a_kind(self, hand):
|
|
# Check for Four of a kind (four cards of the same rank)
|
|
return self.has_n_of_a_kind(hand, 4)
|
|
|
|
def is_full_house(self, hand):
|
|
# Check for a Full house (three cards of one rank and two cards of another rank)
|
|
return self.has_n_of_a_kind(hand, 3) and self.has_n_of_a_kind(hand, 2)
|
|
|
|
def is_flush(self, hand):
|
|
# Check for a Flush (five cards of the same suit)
|
|
suits = [card.split()[-1] for card in hand]
|
|
return all(suit == suits[0] for suit in suits)
|
|
|
|
def is_straight(self, hand):
|
|
# Check for a Straight (five consecutive cards of different suits)
|
|
values = [self.get_card_value(card) for card in hand]
|
|
values.sort()
|
|
return all(values[i] == values[i - 1] + 1 for i in range(1, len(values)))
|
|
|
|
def is_three_of_a_kind(self, hand):
|
|
# Check for Three of a kind (three cards of the same rank)
|
|
return self.has_n_of_a_kind(hand, 3)
|
|
|
|
def is_two_pair(self, hand):
|
|
# Check for Two pair (two cards of one rank and two cards of another rank)
|
|
ranks = [self.get_card_value(card) for card in hand]
|
|
rank_counts = {rank: ranks.count(rank) for rank in set(ranks)}
|
|
return list(rank_counts.values()).count(2) == 2
|
|
|
|
def is_pair(self, hand):
|
|
# Check for a Pair (two cards of the same rank)
|
|
return self.has_n_of_a_kind(hand, 2)
|
|
|
|
def has_n_of_a_kind(self, hand, n):
|
|
# Helper function to check if there are 'n' cards of the same rank in the hand
|
|
ranks = [self.get_card_value(card) for card in hand]
|
|
rank_counts = {rank: ranks.count(rank) for rank in set(ranks)}
|
|
return n in rank_counts.values()
|
|
|
|
def get_next_player_index(self, current_index):
|
|
next_index = current_index + 1
|
|
if next_index >= len(self.players):
|
|
next_index = 0
|
|
return next_index
|