# -*- coding: utf-8 -*- import logging from telegram import InlineKeyboardButton, InlineKeyboardMarkup from telegram.keyboardbutton import KeyboardButton from telegram.replykeyboardmarkup import ReplyKeyboardMarkup from telegram.replykeyboardremove import ReplyKeyboardRemove from database.statistics import add_game_played, set_game_won from game.dealer import Dealer from game.deck import CardDeck from game.message import Message from game.player import Player from lang.language import translate __author__ = 'Rico' class BlackJackGame(object): PRIVATE_CHAT = 0 GROUP_CHAT = 1 MULTIPLAYER_GAME = 2 MAX_PLAYERS = 5 # Adds Player to the Game def add_player(self, user_id, first_name, message_id, silent=False): if self.game_running: return if self.get_player_by_id(user_id) is None and len(self.players) < self.MAX_PLAYERS: self.logger.debug("Adding user '" + first_name + "' to players.") player = Player(user_id, first_name, join_id=message_id) self.players.append(player) if silent: return # TODO When game is multiplayer then print current players? keyboard = [[InlineKeyboardButton(text=translate("start_game", self.lang_id), callback_data="start_game")]] reply_markup = InlineKeyboardMarkup(keyboard) self.send_message(self.chat_id, translate("playerJoined", self.lang_id).format(first_name), message_id=message_id, reply_markup=reply_markup, game_id=self.__game_id) else: self.send_message(self.chat_id, translate("alreadyJoined", self.lang_id).format(first_name)) self.logger.debug("User '{}' already in player list. Or max players reached".format(first_name)) def get_player_by_id(self, user_id): for user in self.players: if user.user_id == user_id: return user return None def next_player(self): if self.game_running: if self.current_player is not None and self.current_player < (len(self.players) - 1): user = self.players[self.current_player] self.logger.debug("Next Player!") self.current_player += 1 self.send_message(self.chat_id, translate("overview", self.lang_id) + "\n\n" + self.get_player_overview(show_points=True) + "\n" + translate("nextPlayer", self.lang_id).format(self.players[self.current_player].first_name), message_id=user.join_id, reply_markup=self.keyboard_running, game_id=self.__game_id) self.give_player_one() else: self.logger.debug("Dealer's turn") self.current_player = None self.dealers_turn() def give_player_one(self): if not self.game_running: return user = self.players[self.current_player] self.logger.debug("Giving player one card | chatID: {} | player: {}".format(self.chat_id, user.first_name)) if not user.has_cards(): # give user 2 cards at beginning for _ in range(2): card = self.deck.pick_one_card() user.give_card(card) cards_string = "\n" + user.get_cards_string() + "\n" if user.cardvalue == 21: got21_text = "\n\n" + user.first_name + " " + translate("got21", self.lang_id) self.send_message(self.chat_id, translate("yourCardsAre", self.lang_id).format(user.first_name, cards_string, str(user.cardvalue)) + got21_text, reply_markup=ReplyKeyboardRemove(), game_id=self.__game_id) self.next_player() else: self.send_message(self.chat_id, str(translate("yourCardsAre", self.lang_id).format( user.first_name, cards_string, str(user.cardvalue))), reply_markup=self.keyboard_running, message_id=user.join_id, game_id=self.__game_id) else: card = self.deck.pick_one_card() message = Message() if user.has_ace and (user.cardvalue + card.value > 21): # user got already an ace -> soft hand user.remove_ace() message.add_text(translate("softHandLater", self.lang_id)) if self.game_type == self.PRIVATE_CHAT: message.add_text_nl(translate("playerDraws1", self.lang_id).format(card)) else: message.add_text_nl(translate("playerDrew", self.lang_id).format(user.first_name, card)) user.give_card(card) message.add_text_nl(translate("cardvalue", self.lang_id).format(user.cardvalue)) if user.cardvalue < 21: self.send_message(self.chat_id, text=message.get_text(), reply_markup=self.keyboard_running, game_id=self.__game_id) return if user.cardvalue > 21: if self.game_type == self.GROUP_CHAT: message.add_text("\n\n" + translate("playerBusted", self.lang_id).format(user.first_name)) elif user.cardvalue == 21: message.add_text("\n\n" + user.first_name + " " + translate("got21", self.lang_id)) self.send_message(self.chat_id, text=message.get_text(), game_id=self.__game_id) self.next_player() # Gives the dealer cards def dealers_turn(self): if self.dealer.get_number_of_cards() < 2: card = None for _ in range(2): card = self.deck.pick_one_card() self.dealer.give_card(card) text = "" if self.game_type == self.PRIVATE_CHAT: text += translate("gameBegins", self.lang_id) + "\n" text += "\n*{}*\n\n{}, | -- |".format(translate("dealersCards", self.lang_id), str(card)) self.send_message(self.chat_id, text, parse_mode="Markdown", reply_markup=self.keyboard_running) else: output_text = translate("croupierDrew", self.lang_id) + "\n\n" while self.dealer.cardvalue <= 16: card = self.deck.pick_one_card() self.dealer.give_card(card) i = 0 for card in self.dealer.cards: if i == 0: output_text += str(card) else: output_text += " , " + str(card) i += 1 output_text += "\n\n{} {}".format(translate("cardvalueDealer", self.lang_id), self.dealer.cardvalue) self.send_message(self.chat_id, output_text, parse_mode="Markdown", reply_markup=ReplyKeyboardRemove()) self.evaluation() def start_game(self, message_id: int = None) -> None: if self.game_running: self.send_message(self.chat_id, translate("alreadyAGame", self.lang_id)) return if ((self.game_type == self.GROUP_CHAT or self.game_type == self.MULTIPLAYER_GAME) and len(self.players) > 1) or self.game_type == self.PRIVATE_CHAT: self.game_running = True for player in self.players: add_game_played(player.user_id) if self.game_type == self.GROUP_CHAT or self.game_type == self.MULTIPLAYER_GAME: self.send_message(self.chat_id, translate("gameBegins", self.lang_id) + "\n" + translate("gameBegins2", self.lang_id) + "\n\n" + self.get_player_overview(), game_id=self.__game_id) self.dealers_turn() self.give_player_one() else: self.send_message(self.chat_id, translate("notEnoughPlayers", self.lang_id), message_id=message_id, game_id=self.__game_id) def evaluation(self) -> None: list_21 = [] list_busted = [] list_lower_21 = [] for user in self.players: if user.cardvalue > 21: list_busted.append(user) elif user.cardvalue == 21: list_21.append(user) elif user.cardvalue < 21: list_lower_21.append(user) if self.dealer.cardvalue > 21: list_busted.append(self.dealer) elif self.dealer.cardvalue == 21: list_21.append(self.dealer) elif self.dealer.cardvalue < 21: list_lower_21.append(self.dealer) list_21 = sorted(list_21, key=lambda x: x.cardvalue, reverse=True) list_lower_21 = sorted(list_lower_21, key=lambda x: x.cardvalue, reverse=True) list_busted = sorted(list_busted, key=lambda x: x.cardvalue, reverse=True) if self.dealer.cardvalue > 21: for user in list_21: set_game_won(user.user_id) for user in list_lower_21: set_game_won(user.user_id) # Alle mit 21 > Punkte >= 0 haben Einsatz x 1,5 gewonnen. # Alle mit 21 haben Einsatz mal 2 gewonnen # Alle mit 21 und Kartenanzahl = 2 haben Einsatz mal 3 gewonnen elif self.dealer.cardvalue == 21: # todo differentiate between blackjack and 21 for user in list_21: if user.first_name != translate("dealerName", self.lang_id): set_game_won(user.user_id) # Alle mit 21 > Punkte >= 0 haben verloren . || Alle mit 21 haben Einsatz gewonnen || Alle mit 21 und Kartenanzahl = 2 haben Einsatz mal 2 gewonnen # todo if dealer got Blackjack: || Everyone with BlackJack won their bet back. || Everone else lost elif self.dealer.cardvalue < 21: for user in list_21: set_game_won(user.user_id) for user in list_lower_21: if user.cardvalue > self.dealer.cardvalue: set_game_won(user.user_id) # print(str(user.get_userid()) + " you've got " + ) # Alle mit Dealer > Punkte haben verloren. # Alle mit Dealer = Punkte erhalten Einsatz # Alle mit 21 > Punkte > Dealer haben Einsatz x 1,5 gewonnen. # Alle mit 21 haben Einsatz mal 2 gewonnen # Alle mit 21 und Kartenanzahl = 2 haben Einsatz mal 3 gewonnen # 7er Drilling 3/2 Gewinn (Einsatz x 1,5) final_message = translate("playerWith21", self.lang_id) + "\n" for user in list_21: final_message += str(user.cardvalue) + " - " + user.first_name + "\n" final_message += "\n" + translate("playerLess21", self.lang_id) + "\n" for user in list_lower_21: final_message += str(user.cardvalue) + " - " + user.first_name + "\n" final_message += "\n" + translate("playerOver21", self.lang_id) + "\n" for user in list_busted: final_message += str(user.cardvalue) + " - " + user.first_name + "\n" keyboard = [[InlineKeyboardButton(text=translate("new_game", self.lang_id), callback_data="new_game")]] reply_markup = InlineKeyboardMarkup(keyboard) self.send_message(self.chat_id, final_message, game_id=self.__game_id, reply_markup=reply_markup) self.game_handler.gl_remove(self.chat_id) def get_player_overview(self, show_points: bool = False, dealer: bool = False) -> str: """Return the overview of all players in a game room""" text = "" if not self.game_running: return text for counter, user in enumerate(self.players): if counter == self.current_player: text += "▶️" else: text += "👤" if show_points is True and (counter < self.current_player or self.current_player == -1): text += "{} - [{}]\n".format(user.first_name, user.cardvalue) else: text += (user.first_name + "\n") if dealer is True: text += ("🎩" + translate("dealerName", self.lang_id) + " - [" + str(self.dealer.cardvalue) + "]") return text # Messages are analyzed here. Most function calls come from here def analyze_message(self, update): """Commands for a game are forwarded to the specific game's 'analyze_message' method""" text = update.message.text user_id = update.message.from_user.id first_name = update.message.from_user.first_name message_id = update.message.message_id # Remove leading slash from command if text.startswith("/"): command = str(text[1:]).lower() else: command = text.lower() # TODO following "or self.game_type == self.MULTIPLAYER_GAME" is not neccessary if self.game_type == self.GROUP_CHAT or self.game_type == self.MULTIPLAYER_GAME: if command.startswith(translate("join", self.lang_id)): self.add_player(user_id, first_name, message_id) elif command.startswith(translate("startCmd", self.lang_id)): self.start_game() if self.game_running: if command.startswith(translate("oneMore", self.lang_id)): if self.current_player is None or self.current_player < 0: return if user_id == self.players[self.current_player].user_id: self.give_player_one() else: self.send_message(self.chat_id, translate("notYourTurn", self.lang_id).format(first_name), game_id=self.__game_id) elif command.startswith(translate("noMore", self.lang_id)): if self.current_player is None or self.current_player < 0: return if user_id == self.players[self.current_player].user_id: self.logger.debug("User doesn't want another card") self.next_player() elif command.startswith(translate("stopCmd", self.lang_id)): if user_id == self.players[0].user_id: self.send_message(self.chat_id, translate("gameEnded", self.lang_id), game_id=self.__game_id) self.game_handler.gl_remove(self.chat_id) @property def game_id(self) -> str: """Return the game_id of the current game""" return self.__game_id # When game is being initialized def __init__(self, chat_id: int, user_id: int, lang_id: str, first_name: str, game_handler: object, message_id: int, send_message: callable, multiplayer: bool = None, game_id: str = None): # declare variables and set initial values self.players = [] self.chat_id = chat_id self.__game_id = game_id self.lang_id = lang_id self.deck = CardDeck(lang_id) # TODO language of the cards & dealer cannot be changed # TODO especially with new multiplayer important! self.dealer = Dealer(translate("dealerName", lang_id), self.deck) self.game_running = False self.current_player = 0 self.game_handler = game_handler self.send_message = send_message self.logger = logging.getLogger(__name__) if multiplayer: self.game_type = self.MULTIPLAYER_GAME chat_id = 0 elif chat_id >= 0: self.game_type = self.PRIVATE_CHAT else: self.game_type = self.GROUP_CHAT one_more_button = KeyboardButton(translate("keyboardItemOneMore", lang_id)) no_more_button = KeyboardButton(translate("keyboardItemNoMore", lang_id)) stop_button = KeyboardButton(translate("keyboardItemStop", lang_id)) self.keyboard_running = ReplyKeyboardMarkup(keyboard=[[one_more_button, no_more_button], [stop_button]], selective=True) self.add_player(user_id, first_name, message_id, silent=True) # Only send a "Please join the game" message, when it's a group chat if self.game_type == self.GROUP_CHAT: keyboard = [[InlineKeyboardButton(text=translate("join", self.lang_id).capitalize(), callback_data="join_game")]] reply_markup = InlineKeyboardMarkup(keyboard) send_message(chat_id, translate("newRound", lang_id), message_id=message_id, reply_markup=reply_markup) elif self.game_type == self.MULTIPLAYER_GAME: pass else: self.start_game() # start game and send message to private chat # When game is being ended / object is destructed def __del__(self): pass