示例#1
0
文件: blackjack.py 项目: parwe/bot
 def __init__(self, bot, ctx):
     super().__init__(ctx)
     self.deck = Deck()
     self.bot = bot
     self.announcer = BlackjackPlayerAnnouncer(bot)
     self.ai_generator = AiUserGenerator(bot)
     self.dealer = self.init_dealer()
     self._turn_timer = TurnTimer(bot, self)
     self.standing_players = [
     ]  # Players to compare with the dealer's hand at the end of the game.
     self.payouts = []
     self.id = GAME_ID['BLACKJACK']
     self.payout_handler = PayoutHandler(self)
     self.max_num_players = 5
示例#2
0
 def __init__(self, ctx, bot):
     super().__init__(ctx)
     self.bot = bot
     self.title = "game of Bombtile"
     self.id = GAME_ID["BOMBTILE"]
     self._turn_timer = TurnTimer(bot, self)
     self.min_players = 2
     self.max_players = 5
     self.grid_values = []  # The hidden tile symbols.
     self.visible_grid = []  # The tiles that users see.
     self._payouts = []
     self.num_cells = None  # TBD -- Depends on number of players.
     self.num_columns = None  # TBD
     self.num_rows = None  # TBD
     self.grid_handler = None  # TBD
     self.announcer = None  # TBD -- Relies on grid dimensions.
     self.ai_generator = AiUserGenerator(bot)
示例#3
0
class Bombtile(GameCore):
    """
    A game where players take turns revealing tiles.
    Most tiles are empty, with several containing multipliers that increase your wager (and therefore
    increase your payout or loss).
    If a player reveals the "bomb" tile, they lose their wager to the rest of the players.
    """
    def __init__(self, ctx, bot):
        super().__init__(ctx)
        self.bot = bot
        self.title = "game of Bombtile"
        self.id = GAME_ID["BOMBTILE"]
        self._turn_timer = TurnTimer(bot, self)
        self.min_players = 2
        self.max_players = 5
        self.grid_values = []  # The hidden tile symbols.
        self.visible_grid = []  # The tiles that users see.
        self._payouts = []
        self.num_cells = None  # TBD -- Depends on number of players.
        self.num_columns = None  # TBD
        self.num_rows = None  # TBD
        self.grid_handler = None  # TBD
        self.announcer = None  # TBD -- Relies on grid dimensions.
        self.ai_generator = AiUserGenerator(bot)

    def add_user(self, user) -> None:
        player = BombtilePlayer(user)
        super().add_player(player)
        super().add_user(user)

    async def add_ai(self) -> None:
        # Get from a random pool of AI "users"
        ai_user = self.ai_generator.get_ai_user()
        ai_player = BombtilePlayer.create_ai(ai_user)
        super().add_player(ai_player)
        await self.ctx.send(f"{ai_player.name} has joined the game.")

    def is_max_num_players(self) -> bool:
        return len(self.players) == self.max_players

    async def run(self) -> None:
        if self.__can_start_game():
            self.__initialize_grid()
            # Grid dimensions are a dependency of feedback, because feedback uses a string representation of the grid.
            # Hence the ordering.
            await self.__initialize_feedback()
            super().start_game()
            random.shuffle(self.players)
            await self.announcer.announce_current_turn()
            await self._turn_timer.run()
        else:
            await self.ctx.send("Bombtile needs at least two players to start."
                                )

    async def flip(self, coordinates) -> None:
        """
        The player action that reveals a tile.
        """
        y = coordinates[0]
        x = coordinates[1]
        tile = self.__reveal_tile(y, x)
        await self.announcer.render_grid()
        await self.__check_multiplier(tile)
        await self.__check_game_end(tile)

    def get_payouts(self) -> List[dict]:
        return self._payouts

    def is_turn(self, user) -> bool:
        return user == self.get_current_player().user

    async def resolve_afk(self) -> None:
        await self.announcer.announce_afk()
        await self.__auto_flip_tile()

    def is_flippable_tile(self, coordinates: List[int]) -> bool:
        y = coordinates[0]
        x = coordinates[1]
        return self.visible_grid[y][x] is NEUTRAL_TILE

    def get_current_player(self) -> BombtilePlayer:
        return self.players[0]

    async def __initialize_feedback(self) -> None:
        self.announcer = BombtileAnnouncer(self)
        await self.announcer.announce_start()

    def __can_start_game(self) -> bool:
        num_players = len(self.players)
        return num_players >= 2

    def __initialize_grid(self) -> None:
        # Number of cells in the grid are dependent on the number of players.
        self.num_cells = 3 * len(self.players)
        self.__initialize_grid_dimensions()
        self.__initialize_tile_values()
        self.__initialize_visible_tiles()

    def __initialize_visible_tiles(self) -> None:
        # All tiles start blank to the users.
        tiles = []
        for i in range(self.num_cells):
            tiles.append(NEUTRAL_TILE)
        self.visible_grid = self.grid_handler.generate_grid(tiles)

    def __initialize_grid_dimensions(self) -> None:
        """
        The grid tends to be wider than it is tall.
        """
        if len(self.players) > 3:
            self.num_columns = len(self.players)
            self.num_rows = self.num_cells // len(self.players)
        else:
            self.num_columns = self.num_cells // len(self.players)
            self.num_rows = len(self.players)
        self.grid_handler = GridHandler(self.num_columns, self.num_rows)

    def __initialize_tile_values(self) -> None:
        self.grid_values.append(BOMB)
        self.__add_multipliers()
        self.__add_empty_tiles()
        random.shuffle(self.grid_values)
        self.grid_values = self.grid_handler.generate_grid(self.grid_values)

    def __add_empty_tiles(self) -> None:
        tiles_remaining = self.num_cells - len(self.grid_values)
        for i in range(tiles_remaining):
            self.grid_values.append(EMPTY_TILE)

    async def __check_multiplier(self, tile) -> None:
        if tile is not BOMB and tile is not EMPTY_TILE:
            #  Then the tile must be a multiplier
            multiplier = tile['value']
            self.get_current_player().update_multiplier(multiplier)
            await self.announcer.announce_multiplier()

    async def __check_game_end(self, tile) -> None:
        if tile is BOMB:
            await self.end_game()
        else:
            await self.__next_turn()

    async def end_game(self) -> None:
        await self.announcer.report_loss()
        await self.__resolve_payouts()
        super().end_game()

    async def __next_turn(self) -> None:
        self.__requeue_player()
        self._turn_timer.refresh_turn_timer()
        if await self.__is_final_tile():
            return
        await self.announcer.announce_current_turn()
        await self.__check_ai_turn()

    async def __check_ai_turn(self) -> None:
        """
        If the current player is an AI, automatically flip a tile.
        """
        if self.get_current_player().is_ai:
            await asyncio.sleep(2.0)
            await self.__auto_flip_tile()

    async def __auto_flip_tile(self) -> None:
        """
        Flips a random neutral tile.
        """
        valid_tiles = self.__get_neutral_tiles()
        random_tile = roll(valid_tiles)
        await self.flip(random_tile)

    def __get_neutral_tiles(self) -> List[List[int]]:
        """
        Get a list of all indices for flippable tiles.
        """
        neutral_tiles = []
        for x in range(self.num_columns):
            for y in range(self.num_rows):
                if self.is_flippable_tile([y, x]):
                    neutral_tiles.append([y, x])
        return neutral_tiles

    def __requeue_player(self) -> None:
        # If the game is not over, put the player at the end of the queue after their turn.
        player = self.players.pop(0)
        self.players.append(player)

    async def __is_final_tile(self) -> bool:
        """
        If a player is stuck with the last remaining tile, it gets auto flipped on their turn.
        """
        tiles = self.__get_neutral_tiles()
        if len(tiles) == 1:
            player = self.get_current_player()
            await self.announcer.auto_reveal(player)
            await self.flip(tiles[0])
            return True

    async def __resolve_payouts(self) -> None:
        loser = self.players.pop(0)
        for winner in self.players:
            await self.__resolve_payout(winner, loser)

    async def __resolve_payout(self, winner, loser) -> None:
        to_user = winner.user
        amount = winner.wager * winner.get_multiplier() * loser.get_multiplier(
        )
        from_user = loser.user
        await self.announcer.report_payout(winner, amount)
        self.__add_payout(to_user, amount, from_user)

    def __add_payout(self, to_user, amount: int, from_user) -> None:
        self._payouts.append({
            'to_user': to_user,
            'amount': amount,
            'from_user': from_user
        })

    def __reveal_tile(self, y: int, x: int) -> dict:
        self.visible_grid[y][x] = self.grid_values[y][x]
        return self.grid_values[y][x]

    def __add_multipliers(self) -> None:
        """
        Multiplier tiles multiply your wager by the shown amount.
        Eg. if you reveal a TWO tile, you could lose x2 more gold, or win x2 more gold.
        """
        num_players = len(self.players)
        num_multipliers = random.randint(num_players - 1, num_players)
        for i in range(num_multipliers):
            self.__add_random_multiplier()

    def __add_random_multiplier(self) -> None:
        possible_multipliers = [TWO, TWO, TWO, THREE, THREE, FIVE]
        multiplier = roll(possible_multipliers)
        self.grid_values.append(multiplier)
示例#4
0
文件: blackjack.py 项目: parwe/bot
class Blackjack(GameCore):
    def __init__(self, bot, ctx):
        super().__init__(ctx)
        self.deck = Deck()
        self.bot = bot
        self.announcer = BlackjackPlayerAnnouncer(bot)
        self.ai_generator = AiUserGenerator(bot)
        self.dealer = self.init_dealer()
        self._turn_timer = TurnTimer(bot, self)
        self.standing_players = [
        ]  # Players to compare with the dealer's hand at the end of the game.
        self.payouts = []
        self.id = GAME_ID['BLACKJACK']
        self.payout_handler = PayoutHandler(self)
        self.max_num_players = 5

    def init_dealer(self) -> BlackjackDealer:
        # TODO let players host blackjack games
        user = self.ai_generator.get_ai_user()
        return BlackjackDealer(user, self)

    async def start_game(self):
        self.__dispense_cards()
        await self.__show_player_cards()
        await self.dealer.show_face_up()
        await self.__check_initial_dealer_cards()
        super().start_game()
        await self._turn_timer.run()

    def add_user(self, user):
        super().add_user(user)
        self.add_player(user)

    def add_player(self, user) -> None:
        player = BlackjackPlayer(user)
        super().add_player(player)

    def is_turn(self, user):
        first_in_queue = self.__get_current_player().user
        return user is first_in_queue

    async def resolve_afk(self) -> None:
        player = self.__get_current_player()
        is_last_player = len(self.players) == 1
        if player.afk > 0 or is_last_player:
            await self.bot.say(
                f"{player.name} is away. They have forfeited the match.")
            self.forfeit()
        else:
            await self.__requeue_afk_player()
        await self.__next_turn()

    async def __requeue_afk_player(self):
        player = self.players.pop(0)
        self.players.append(player)
        await self.bot.say(
            f"{player.name} seems to be away. Skipping their turn...")

    def forfeit(self):
        """
        A player who forfeits or quits loses their hands' wagers.
        """
        player = self.__get_current_player()
        for hand in player.get_hands():
            self.payout_handler.add_bust(player, hand)
        self.__knock_out_current_player()

    async def hit(self) -> None:
        hand = self.__get_current_player().get_active_hand()
        new_card = self.deck.draw_card()
        hand.hit(new_card)
        await self.announcer.report_hit(hand, new_card)
        await self.__check_hit_bust(hand)

    async def stand_current_hand(self) -> None:
        hand = self.__get_current_player().get_active_hand()
        hand.end_turn()
        await self.announcer.progressing()
        await self.__check_next_hand()

    async def attempt_double_down(self) -> None:
        hand = self.__get_current_player().get_active_hand()
        if self._double_down(hand):
            wager = hand.get_wager()
            await self.announcer.double_down_success(wager)
            await self.stand_current_hand()
        else:
            await self.announcer.double_down_fail()

    async def attempt_split(self) -> None:
        player = self.__get_current_player()
        hand = player.get_active_hand()
        if player.split_hand():
            await self.announcer.split_successful(hand)
        else:
            await self.announcer.split_fail()

    def get_payouts(self):
        return self.payouts

    def _double_down(self, hand: PlayerHand) -> bool:
        """
        Double wager, then draw and finish.
        """
        if hand.can_double_down():
            card = self.deck.draw_card()
            hand.double_down(card)
            return True

    async def end_game(self) -> None:
        await self.payout_handler.resolve_outcomes()
        self.payouts = self.payout_handler.get_payouts()
        super().end_game()

    def __dispense_cards(self) -> None:
        for player in self.players:
            self.__add_initial_cards(player)
        self.__add_initial_cards(self.dealer)

    def __add_initial_cards(self, participant):
        starting_hand = participant.get_active_hand()
        num_cards = 2
        for i in range(num_cards):
            card = self.deck.draw_card()
            starting_hand.add_card(card)

    async def __show_player_cards(self) -> None:
        for player in self.players:
            hand = player.get_active_hand()
            await self.announcer.player_cards(player.name, hand)

    async def __check_initial_dealer_cards(self) -> None:
        if await self.dealer.has_blackjack():
            await self.end_game()
        else:
            await self.__next_turn()

    async def __next_turn(self) -> None:
        are_player_turns_remaining = self.players
        if are_player_turns_remaining:
            await self.__next_player_turn()
        else:
            await self.__check_dealer_turn()
            await self.end_game()

    async def __check_dealer_turn(self) -> None:
        self._turn_timer.refresh_turn_timer()
        are_players_standing = self.standing_players
        if are_players_standing:
            await self.dealer.make_move()
        else:
            await self.announcer.no_players_left()

    async def __next_player_turn(self) -> None:
        self._turn_timer.refresh_turn_timer()
        current_player = self.__get_current_player()
        player_name = current_player.name
        hand = current_player.get_active_hand()
        await self.announcer.next_turn(player_name, hand)

    async def __check_next_hand(self) -> None:
        active_hand = self.__get_current_player().get_active_hand()
        if active_hand:
            await self.announcer.next_hand_options(active_hand)
        else:
            self.__current_player_stand()
            await self.__next_turn()
        self._turn_timer.refresh_turn_timer()

    def __current_player_stand(self) -> None:
        """
        When a player stands, their turn ends. Hands are compared at the end of the game.
        """
        player = self.players.pop(0)
        self.standing_players.append(player)

    async def __check_hit_bust(self, hand: BlackjackHand) -> None:
        if not hand.is_bust():
            await self.announcer.ask_hit_again()
            return
        await self.announcer.declare_player_bust()
        await self.__bust_current_hand()

    async def __bust_current_hand(self) -> None:
        player = self.__get_current_player()
        hand_to_bust = player.bust_active_hand()
        self.payout_handler.add_bust(player, hand_to_bust)
        await self.__check_knock_out()

    async def __check_knock_out(self) -> None:
        """
        A player is removed from the game if they have no valid hands remaining.
        """
        if self.__get_current_player().get_hands():
            await self.__check_next_hand()
        else:
            self.__knock_out_current_player()
            await self.__next_turn()

    def __get_current_player(self) -> BlackjackPlayer:
        """
        The player whose turn it is.
        """
        return self.players[0]

    def __knock_out_current_player(self) -> None:
        del self.players[0]