import random from Ledger import Ledger #TODO Kill Benson #TODO Starting a game with 21 fucks with hidden cards #TODO Look into 5card jack thingo that Tim mentioned #TODO Insurance - If dealer starts with 21, you can bet against it and win your money back #TODO Make each game session more distinct (Embeds?) # Game States INIT = 0 PLAYERTURN = 1 DEALERTURN = 2 FINISHED = 3 OVER = 5 """ Class which describes playing cards """ class Card(): """ A joker card has value 0 and suit 0 """ HEARTS = 1 DIAMONDS = 2 SPADES = 3 CLUBS = 4 def __init__(self, value, suit) -> None: self.value = value self.suit = suit self.hidden = False def turn_card(self): # XOR to flip hidden element self.hidden = self.hidden != True def get_value(self): return self.value def get_suit(self): return self.suit def __str__(self) -> str: suits = ["J", "♥", "♦", "♠", "♣"] special_cards = {1: "A", 11: "J", 12: "Q", 13: "K"} strValue = "" if self.value in special_cards.keys(): strValue = special_cards[self.value] else: strValue = str(self.value) string = "" string = f"{suits[self.suit]}{strValue}" if self.hidden: string = "??" return string def __repr__(self) -> str: return str(self) """ Class for interacting with a deck of cards """ class Deck(): def __init__(self, joker = False) -> None: self.deck = [] self.discard = [] self.joker = joker self.deck.extend([Card(x, Card.HEARTS) for x in range(1, 13)]) self.deck.extend([Card(x, Card.DIAMONDS) for x in range(1, 13)]) self.deck.extend([Card(x, Card.SPADES) for x in range(1, 13)]) self.deck.extend([Card(x, Card.CLUBS) for x in range(1, 13)]) if joker: self.deck.append(Card(0, 0)) def shuffle(self): random.shuffle(self.deck) def sort(self): self.deck.sort() def take_from_deck(self): card = self.deck.pop() return card def return_to_deck_top(self, card): self.deck.insert(0, card) def returnToDeckBottom(self, card): self.deck.append(card) def addToDiscard(self, card): self.discard.insert(0, card) def returnFromDiscard(self): self.returnToDeckTop(self.discard.pop()) def return_all_from_discard(self): self.deck.extend(self.discard) self.discard = [] def __str__(self) -> str: string = "" for card in self.deck: string += str(card) return string def __len__(self): return len(self.deck) class Hand(): def __init__(self) -> None: self.hand = [] def sortHand(self): self.hand.sort() def add_to_hand(self, card, index = None): if index is None: self.hand.append(card) else: self.hand.insert(index, card) def remove_from_hand(self, index): return self.hand.pop(index) def hide_card(self, index): card = self.remove_from_hand(index) card.turn_card() self.add_to_hand(card, index) def __len__(self): return len(self.hand) def __str__(self) -> str: string = "" for card in self.hand: string += str(card) string += " " return string def __repr__(self) -> str: value = 0 aces = 0 # Add static values for card in self.hand: if card.value == 1: aces += 1 continue if card.value > 10: card.value = 10 value += card.value # Dynamically add ace value based on ideal rules maxAceValue = aces * 11 tempValue = value + maxAceValue if tempValue < 21: return str(tempValue) while (tempValue > 21) and aces > 0: tempValue = tempValue - 10 aces = aces - 1 value = tempValue return str(value) def __iter__(self): self.iter = iter(self.hand) return self.iter def __next__(self): return next(self.iter) class BlackJack: def __init__(self) -> None: self.deck = Deck() self.playerHand = Hand() self.dealerHand = Hand() self.ledger = Ledger() def deal_card(self, hand): hand.add_to_hand(self.deck.take_from_deck()) def game_setup(self): # Deal cards in alternate order self.deck.shuffle() for _ in range(2): self.deal_card(self.playerHand) self.deal_card(self.dealerHand) # Hide one of the dealers cards self.dealerHand.hide_card(1) def discard_hand(self, hand): for _ in range(len(hand)): card = hand.remove_from_hand(0) self.deck.addToDiscard(card) async def show_cards(self, send, displayDealerScore=False): # Show Cards to player string = f"Player Hand = {repr(self.playerHand)}: {self.playerHand}\nDealer Hand = ??: {self.dealerHand}" if displayDealerScore: string = f"Player Hand = {repr(self.playerHand)}: {self.playerHand}\nDealer Hand = {repr(self.dealerHand)}: {self.dealerHand}" await send(string) async def play_game(self, ID, bet, recv, send): gameState = INIT while gameState != OVER: if gameState == INIT: playerStats = self.ledger.read(ID) if playerStats is None: self.ledger.write(ID) playerStats = self.ledger.read(ID) self.game_setup() gameState = PLAYERTURN if gameState == PLAYERTURN: await self.show_cards(send) playerHit = await recv("Hit or Stand?") if playerHit: self.deal_card(self.playerHand) else: #TODO Rename hide_card function self.dealerHand.hide_card(1) gameState = DEALERTURN if int(repr(self.playerHand)) >= 21: self.dealerHand.hide_card(1) gameState = FINISHED if gameState == DEALERTURN: if int(repr(self.dealerHand)) > int(repr(self.playerHand)): gameState = FINISHED continue self.deal_card(self.dealerHand) if int(repr(self.dealerHand)) >= 17: gameState = FINISHED if gameState == FINISHED: playerBlackJack = False dealerBlackJack = False playerBust = False dealerBust = False playerHigher = False dealerHigher = False tie = False playerScore = int(repr(self.playerHand)) dealerScore = int(repr(self.dealerHand)) await self.show_cards(send, True) if playerScore == 21: playerBlackJack = True if dealerScore == 21: dealerBlackJack = True if playerScore > 21: playerBust = True if dealerScore > 21: dealerBust = True if dealerScore > playerScore: dealerHigher = True elif playerScore > dealerScore: playerHigher = True else: tie = True self.discard_hand(self.playerHand) self.discard_hand(self.dealerHand) if tie: await send("You tied") elif playerBlackJack: await send("You reached BlackJack!") win = (2*bet, 1, 0, ID) self.ledger.update(ID, win) elif dealerBlackJack: await send("The dealer reached BlackJack!") loss = (-bet, 0, 1, ID) self.ledger.update(ID, loss) elif playerBust: await send("You busted!") loss = (-bet, 0, 1, ID) self.ledger.update(ID, loss) elif dealerBust: await send("The dealer busted!") win = (2*bet, 1, 0, ID) self.ledger.update(ID, win) elif playerHigher: await send("You won!") win = (2*bet, 1, 0, ID) self.ledger.update(ID, win) elif dealerHigher: await send("You lost!") loss = (-bet, 0, 1, ID) self.ledger.update(ID, loss) else: await send("Report this game to my creator!") if len(self.deck) < 0.25 * 51: self.deck.return_all_from_discard() self.deck.shuffle() await send("Everyday I'm shuffling") gameState = OVER def main(): game = BlackJack() trueFalseInput = lambda x: input(x) == "h" game.play_game(recv=trueFalseInput, send=print) pass if __name__ == "__main__": main()