import random from mautrix.util.config import BaseProxyConfig, ConfigUpdateHelper from maubot import Plugin, MessageEvent from maubot.handlers import command class BlackjackBot(Plugin): values = ['2','3','4','5','6','7','8','9','10','Jack','Queen','King','Ace'] suites = ['Hearts', 'Clubs', 'Diamonds', 'Spades'] deck = [] players = [] game_pending = False game_started = False awaiting_commands = False player_cards = {} player_score = {} dealer_cards = [] dealer_score = 0 player_status = {} def init_game(self): self.deck = [(card, category) for category in self.suites for card in self.values] def end_game(self): self.players = [] self.game_pending = False self.game_started = False self.awaiting_commands = False self.player_cards = {} self.player_score = {} self.dealer_cards = [] self.dealer_score = 0 self.player_status = {} def card_value(self, card): if card[0] in ['Jack', 'Queen', 'King']: return 10 elif card[0] == 'Ace': return 11 else: return int(card[0]) def get_score(self, cards): score = 0 aces = 0 for card in cards: score += self.card_value(card) if (card[0] == 'Ace'): aces += 1 while (score > 21 and aces > 0): aces -= 1 score -= 10 return score def cards_named(self, cards): named_list = [] for card in cards: named_list.append(card[0] + " of " + card[1]) return named_list def shuffle_deck(self) -> None: random.shuffle(self.deck) def add_player(self, player) -> None: self.players.append(player) async def dealers_turn(self, evt: MessageEvent) -> None: players_bust = 0 for player in self.players: if (self.player_status[player] == -1): players_bust += 1 if (players_bust == len(self.players)): for player in self.players: await evt.respond("Player " + player + " loses") self.end_game() return await evt.respond("Cards of Dealer: " + ', '.join(self.cards_named(self.dealer_cards))) await evt.respond("Score of Dealer: " + str(self.dealer_score)) while (self.dealer_score < 17): await evt.respond("Dealer draws") self.dealer_cards.append(self.deck.pop()) self.dealer_score = self.get_score(self.dealer_cards) await evt.respond("Cards of Dealer: " + ', '.join(self.cards_named(self.dealer_cards))) await evt.respond("Score of Dealer: " + str(self.dealer_score)) if (self.dealer_score > 21): await evt.respond("Dealer busts!") for player in self.players: if (self.player_status[player] != -1): await evt.respond("Player " + player + " wins!") elif (self.player_status[player] == -1): await evt.respond("Player " + player + " loses") else: for player in self.players: if (self.player_status[player] != -1): if (self.player_score[player] > self.dealer_score): await evt.respond("Player " + player + " wins!") elif (self.player_score[player] == self.dealer_score): await evt.respond("Player " + player + " pushes") elif (self.player_score[player] < self.dealer_score): await evt.respond("Player " + player + " loses") elif (self.player_status[player] == -1): await evt.respond("Player " + player + " loses") self.end_game() def bj_match(self, command: str) -> bool: return command in ["blackjack", "bj"] @command.new(aliases=bj_match) async def blackjack(self, evt: MessageEvent) -> None: if ( not self.game_pending and not self.game_started ): self.game_pending = True; await evt.respond("Starting a game of Blackjack, type !join to play") def join_match(self, command: str) -> bool: return command in ["join", "j"] @command.new(aliases=join_match) async def join(self, evt: MessageEvent) -> None: if (self.game_pending): if (evt.sender in self.players): return self.add_player(evt.sender) await evt.respond("Player " + evt.sender + " has joined the game!") await evt.respond("When all players have joined, type !begin to start the game") def begin_match(self, command: str) -> bool: return command in ["begin", "b"] @command.new(aliases=begin_match) async def begin(self, evt: MessageEvent) -> None: if (self.game_pending): self.game_pending = False self.game_started = True self.init_game() self.shuffle_deck() for player in self.players: self.player_cards[player] = [self.deck.pop(), self.deck.pop()] self.player_status[player] = 0 self.player_score[player] = self.get_score(self.player_cards[player]) self.dealer_cards = [self.deck.pop(), self.deck.pop()] self.dealer_score = self.get_score(self.dealer_cards) for player in self.players: await evt.respond("Cards " + player + " has: " + ', '.join(self.cards_named(self.player_cards[player]))) await evt.respond("Score of " + player + ": "+ str(self.player_score[player])) if (self.player_score[player] == 21): await evt.respond("Player " + player + " has blackjack!") self.player_status[player] = 2 if (self.dealer_score == 21): await evt.respond("Cards of Dealer: " + ' '.join(self.cards_named(self.dealer_cards))) await evt.respond("Dealer has blackjack!") for player in self.players: if (self.player_score[player] == 21): await evt.respond("Player " + player + " pushes") elif (self.player_score[player] < 21): await evt.respond("Player " + player + " loses") self.end_game() return else: await evt.respond("Cards of Dealer: " + ' '.join(self.cards_named(self.dealer_cards[1:]))) self.awaiting_commands = True # edge case handling when all players get blackjack waiting_for_players = 0 for player in self.players: if (self.player_status[player] == 0): waiting_for_players += 1 if (waiting_for_players == 0): self.end_game() def hit_match(self, command: str) -> bool: return command in ["hit", "h"] @command.new(aliases=hit_match) async def hit(self, evt: MessageEvent) -> None: player = evt.sender if (self.awaiting_commands and player in self.players): if (self.player_status[player] != 0): return self.player_cards[player].append(self.deck.pop()) self.player_score[player] = self.get_score(self.player_cards[player]) await evt.respond("Cards " + player + " has: " + ', '.join(self.cards_named(self.player_cards[player]))) await evt.respond("Score of " + player + ": "+ str(self.player_score[player])) if (self.player_score[player] > 21 ): self.player_status[player] = -1 await evt.respond("Player " + player + " busts!") waiting_for_players = 0 for player in self.players: if (self.player_status[player] == 0): waiting_for_players += 1 if (waiting_for_players == 0): await self.dealers_turn(evt) def stand_match(self, command: str) -> bool: return command in ["stand", "stay", "s"] @command.new(aliases=stand_match) async def stand(self, evt: MessageEvent) -> None: player = evt.sender if (self.awaiting_commands and player in self.players): if (self.player_status[player] != 0): return self.player_status[player] = 1 waiting_for_players = 0 for player in self.players: if (self.player_status[player] == 0): waiting_for_players += 1 if (waiting_for_players == 0): await self.dealers_turn(evt) @command.new("end") async def end(self, evt: MessageEvent) -> None: if (self.game_pending or self.game_started): self.end_game() await evt.respond("Blackjack has ended") @command.new("help") async def help(self, evt: MessageEvent) -> None: await evt.respond("!blackjack to initiate a new game players can join") await evt.respond("!join to join a pending game") await evt.respond("!begin to start once everyone is on") await evt.respond("!hit to draw a new card") await evt.respond("!stand to stop drawing") await evt.respond("!end to stop a currently running game")