Beispiel #1
0
class Game:
    def __init__(self) -> None:
        self.new_game()
        # Set the game options to the defaults
        self.options = {
            key: value.default
            for key, value in GAME_OPTIONS.items()
        }

    def new_game(self) -> None:
        self.state = GameState.NO_GAME
        # The players participating in the game
        self.players = []
        # The players participating in the current hand
        self.in_hand = []
        # The index of the current dealer
        self.dealer_index = 0
        # The index of the first person to bet in the post-flop rounds
        self.first_bettor = 0
        # The deck that we're dealing from
        self.cur_deck = None
        # The five cards shared by all players
        self.shared_cards = []
        # Used to keep track of the current value of the pot, and who's in it
        self.pot = PotManager()
        # The index of the player in in_hand whose turn it is
        self.turn_index = -1
        # The last time that the blinds were automatically raised
        self.last_raise = None

    # Adds a new player to the game, returning if they weren't already playing
    def add_player(self, user: discord.User) -> bool:
        if self.is_player(user):
            return False
        self.players.append(Player(user))
        print("GameJoin {}".format(user.name))
        return True

    # Returns whether a user is playing in the game
    def is_player(self, user: discord.User) -> bool:
        for player in self.players:
            if player.user == user:
                return True
        return False

    # Removes a player from being able to bet, if they folded or went all in
    def leave_hand(self, to_remove: Player) -> None:
        for i, player in enumerate(self.in_hand):
            if player == to_remove:
                index = i
                break
        else:
            # The player who we're removing isn't in the hand, so just
            # return
            return
        self.in_hand.pop(index)
        # Adjust the index of the first person to bet and the index of the
        # current player, depending on the index of the player who just folded
        if index < self.first_bettor:
            self.first_bettor -= 1
        if self.first_bettor >= len(self.in_hand):
            self.first_bettor = 0
        if self.turn_index >= len(self.in_hand):
            self.turn_index = 0

    # Returns some messages to update the players on the state of the game
    def status_between_rounds(self) -> List[str]:
        messages = []
        for player in self.players:
            messages.append("**{}** has $*{}*.".format(player.user.name,
                                                       player.balance))
        messages.append(
            "**{}** is the current dealer. Message ?deal to deal when you're ready."
            .format(self.dealer.user.mention))
        print("---{} DEALER.".format(self.dealer.user.name))
        return messages

    # Moves on to the next dealer
    def next_dealer(self) -> None:
        self.dealer_index = (self.dealer_index + 1) % len(self.players)

    # Returns the current dealer
    @property
    def dealer(self) -> Player:
        return self.players[self.dealer_index]

    @property
    def cur_bet(self) -> int:
        return self.pot.cur_bet

    # Returns the player who is next to move
    @property
    def current_player(self) -> Player:
        return self.in_hand[self.turn_index]

    # Starts a new game, returning the messages to tell the channel
    def start(self) -> List[str]:
        self.state = GameState.NO_HANDS
        self.dealer_index = 0
        for player in self.players:
            player.balance = self.options["buy-in"]
        # Reset the blind to be the starting blind value
        self.options["blind"] = self.options["starting-blind"]
        return ["The game has begun!"] + self.status_between_rounds()

    # Starts a new round of Hold'em, dealing two cards to each player, and
    # return the messages to tell the channel
    def deal_hands(self) -> List[str]:
        # Shuffles a new deck of cards
        self.cur_deck = Deck()

        # Start out the shared cards as being empty
        self.shared_cards = []

        # Deals hands to each player, setting their initial bets to zero and
        # adding them as being in on the hand
        self.in_hand = []
        for player in self.players:
            player.cards = (self.cur_deck.draw(), self.cur_deck.draw())
            player.cur_bet = 0
            player.placed_bet = False
            self.in_hand.append(player)

        self.state = GameState.HANDS_DEALT
        messages = ["The hands have been dealt!"]

        # Reset the pot for the new hand
        self.pot.new_hand(self.players)

        if self.options["blind"] > 0:
            messages += self.pay_blinds()

        self.turn_index -= 1
        return messages + self.next_turn()

    # Makes the blinds players pay up with their initial bets
    def pay_blinds(self) -> List[str]:
        messages = []

        # See if we need to raise the blinds or not
        raise_delay = self.options["raise-delay"]
        if raise_delay == 0:
            # If the raise delay is set to zero, consider it as being turned
            # off, and do nothing for blinds raises
            self.last_raise = None
        elif self.last_raise is None:
            # Start the timer, if it hasn't been started yet
            self.last_raise = datetime.now()
        elif datetime.now() - self.last_raise > timedelta(minutes=raise_delay):
            messages.append("**Blinds are being doubled this round!**")
            self.options["blind"] *= 2
            self.last_raise = datetime.now()

        blind = self.options["blind"]

        # Figure out the players that need to pay the blinds
        if len(self.players) > 2:
            small_player = self.players[(self.dealer_index + 1) %
                                        len(self.in_hand)]
            big_player = self.players[(self.dealer_index + 2) %
                                      len(self.in_hand)]
            # The first player to bet pre-flop is the player to the left of the big blind
            self.turn_index = (self.dealer_index + 3) % len(self.in_hand)
            # The first player to bet post-flop is the first player to the left of the dealer
            self.first_bettor = (self.dealer_index + 1) % len(self.players)
        else:
            # In heads-up games, who plays the blinds is different, with the
            # dealer playing the small blind and the other player paying the big
            small_player = self.players[self.dealer_index]
            big_player = self.players[self.dealer_index - 1]
            # Dealer goes first pre-flop, the other player goes first afterwards
            self.turn_index = self.dealer_index
            self.first_bettor = self.dealer_index - 1

        messages.append("**{}** has paid the small blind of $*{}*.".format(
            small_player.name, blind))

        if self.pot.pay_blind(small_player, blind):
            messages.append("***{} is all in!***".format(small_player.name))
            tmp = self.leave_hand(small_player)
        messages.append("**{}** has paid the big blind of $*{}*.".format(
            big_player.name, blind * 2))
        if self.pot.pay_blind(big_player, blind * 2):
            messages.append("***{} is all in!***".format(big_player.name))
            tmp = self.leave_hand(big_player)

        return messages

    # Returns messages telling the current player their options
    def cur_options(self) -> List[str]:
        messages = [
            "It is **{}'s** turn. ".format(self.current_player.user.mention),
            "**{}** currently has ".format(self.current_player.user.name),
            "$*{}*. ".format(self.current_player.balance),
            "The pot is currently $***{}***.".format(self.pot.value)
        ]
        if self.pot.cur_bet > 0:
            messages.append(
                "The current bet to meet is $*{}*, and **{}** has bet $*{}*.".
                format(self.cur_bet, self.current_player.name,
                       self.current_player.cur_bet))
        else:
            messages.append("The current bet to meet is $*{}*.".format(
                self.cur_bet))
        if self.current_player.cur_bet == self.cur_bet:
            messages.append("Message ?check, ?raise or ?fold.")
        elif self.current_player.max_bet > self.cur_bet:
            messages.append("Message ?call, ?raise or ?fold.")
        else:
            messages.append("Message ?all-in or ?fold.")
        return messages

    # Advances to the next round of betting (or to the showdown), returning a
    # list messages to tell the players
    def next_round(self) -> List[str]:
        messages = []
        if self.state == GameState.HANDS_DEALT:
            messages.append("Dealing the flop:")
            self.shared_cards.append(self.cur_deck.draw())
            self.shared_cards.append(self.cur_deck.draw())
            self.shared_cards.append(self.cur_deck.draw())
            self.state = GameState.FLOP_DEALT
        elif self.state == GameState.FLOP_DEALT:
            messages.append("Dealing the turn:")
            self.shared_cards.append(self.cur_deck.draw())
            self.state = GameState.TURN_DEALT
        elif self.state == GameState.TURN_DEALT:
            messages.append("Dealing the river:")
            self.shared_cards.append(self.cur_deck.draw())
            self.state = GameState.RIVER_DEALT
        elif self.state == GameState.RIVER_DEALT:
            return self.showdown()
        messages.append("  ".join(str(card) for card in self.shared_cards))
        self.pot.next_round()
        self.turn_index = self.first_bettor
        return messages + self.cur_options()

    # Finish a player's turn, advancing to either the next player who needs to
    # bet, the next round of betting, or to the showdown
    def next_turn(self) -> List[str]:
        if self.pot.round_over():
            if self.pot.betting_over():
                return self.showdown()
            else:
                return self.next_round()
        else:
            self.turn_index = (self.turn_index + 1) % len(self.in_hand)
            return self.cur_options()

    def showdown(self) -> List[str]:
        while len(self.shared_cards) < 5:
            self.shared_cards.append(self.cur_deck.draw())

        messages = [
            "We have reached the end of betting. "
            "All cards will be revealed."
        ]

        messages.append("  ".join(str(card) for card in self.shared_cards))

        for player in self.pot.in_pot():
            messages.append("**{}'s** hand: **{}  {}**".format(
                player.user.mention, player.cards[0], player.cards[1]))

        winners = self.pot.get_winners(self.shared_cards)
        for winner, winnings in sorted(winners.items(),
                                       key=lambda item: item[1]):
            hand_name = str(best_possible_hand(self.shared_cards,
                                               winner.cards))
            messages.append("**{}** wins $***{}*** with a **{}**.".format(
                winner.user.mention, winnings, hand_name))
            print("{} WINS +{}".format(winner.user.name, winnings))
            winner.balance += winnings

        # Remove players that went all in and lost
        i = 0
        while i < len(self.players):
            player = self.players[i]
            if player.balance > 0:
                i += 1
            else:
                messages.append(
                    "**{}** has been knocked out of the game!".format(
                        player.user.mention))
                print("{} OUT.".format(player.user.name))
                self.players.pop(i)
                if len(self.players) == 1:
                    # There's only one player, so they win
                    messages.append(
                        "**{}** wins the game! Congratulations!".format(
                            self.players[0].user.mention))
                    print("WINNER {}\n".format(self.players[0].name))
                    self.state = GameState.NO_GAME
                    return messages
                if i <= self.dealer_index:
                    self.dealer_index -= 1

        # Go on to the next round
        self.state = GameState.NO_HANDS
        self.next_dealer()
        messages += self.status_between_rounds()
        return messages

    # Make the current player check, betting no additional money
    def check(self) -> List[str]:
        self.current_player.placed_bet = True
        print("---{} CHECKED.".format(self.current_player.name))
        return ["{} checks.".format(self.current_player.name)
                ] + self.next_turn()

    # Has the current player raise a certain amount
    def raise_bet(self, amount: int) -> List[str]:
        self.pot.handle_raise(self.current_player, amount)
        messages = [
            "**{}** raises by $*{}*.".format(self.current_player.name, amount)
        ]
        print("---{} RAISE +{}.".format(self.current_player.name, amount))
        if self.current_player.balance == 0:
            messages.append("***{} is all in!***".format(
                self.current_player.name))
            print("---{} WENT ALL IN.".format(self.current_player.name))
            self.leave_hand(self.current_player)
            self.turn_index -= 1
        return messages + self.next_turn()

    # Has the current player match the current bet
    def call(self) -> List[str]:
        self.pot.handle_call(self.current_player)
        messages = ["**{}** calls.".format(self.current_player.name)]
        print("---{} CALLED.".format(self.current_player.name))
        if self.current_player.balance == 0:
            messages.append("***{} is all in!***".format(
                self.current_player.name))
            print("---{} WENT ALL IN.".format(self.current_player.name))
            self.leave_hand(self.current_player)
            self.turn_index -= 1
        return messages + self.next_turn()

    def all_in(self) -> List[str]:
        if self.pot.cur_bet > self.current_player.max_bet:
            return self.call()
        else:
            return self.raise_bet(self.current_player.max_bet - self.cur_bet)

    # Has the current player fold their hand
    def fold(self) -> List[str]:
        messages = ["**{}** has folded.".format(self.current_player.name)]
        print("---{} FOLDED.".format(self.current_player.name))
        self.pot.handle_fold(self.current_player)
        self.leave_hand(self.current_player)

        # If only one person is left in the pot, give it to them instantly
        if len(self.pot.in_pot()) == 1:
            winner = list(self.pot.in_pot())[0]
            messages += [
                "**{}** wins $***{}***!".format(winner.user.mention,
                                                self.pot.value)
            ]
            winner.balance += self.pot.value
            self.state = GameState.NO_HANDS
            self.next_dealer()
            return messages + self.status_between_rounds()

        # If there's still betting to do, go on to the next turn
        if not self.pot.betting_over():
            self.turn_index -= 1
            return messages + self.next_turn()

        # Otherwise, have the showdown immediately
        return self.showdown()

    # Send a message to each player, telling them what their hole cards are
    async def tell_hands(self, client: discord.Client):
        for player in self.players:
            await client.send_message(
                player.user,
                str(player.cards[0]) + "  " + str(player.cards[1]))
Beispiel #2
0
class Room():
    def __init__(self, room_name, blind, buyin, room_id):
        self.room_name = room_name
        if isinstance(blind, int) and blind < 100:
            self.blind = blind
        else:
            raise Exception('blind must be int and small than 100')
        if isinstance(buyin, int):
            self.buyin = buyin
        else:
            raise Exception('buyin must be int')
        self.lock = Lock()
        self.room_id = room_id
        self.stage = 1  #未开局/翻牌/转牌/河牌/开牌
        self.players = []
        self.change_banker = False
        self.banker = 1
        self.speak = 0  #all speak
        self.queue = Queue()
        self.poker_engine = PokerEngine()
        self.public = Deck()
        self.players_cache = {}

    def AddPlayer(self, player_name):
        if player_name not in self.players_cache:
            player = Player(player_name, self.buyin, len(self.players) + 1)
            self.players_cache[player_name] = player
        else:
            player = self.players_cache[player_name]
        for player_in_room in self.players:
            if player_in_room.player_name == player_name:
                return False
        player.player_id = len(self.players) + 1
        player.active = False
        player.ready = False
        self.players.append(player)
        return True

    def DelPlayer(self, player_name):
        idx = -1
        for player_idx, player in enumerate(self.players):
            if player.player_name == player_name:
                idx = player_idx
        if idx == -1:
            raise Exception('can not find player {}'.format(player_name))

        self.players_cache[player_name] = self.players[idx - 1]
        del self.players[idx - 1]
        for player_idx, player in enumerate(self.players):
            player.player_id = player_idx

    def GetRoomInfo(self):
        rsp = RoomResponse()
        rsp.code = 0
        rsp.room_name = self.room_name
        rsp.room_id = self.room_id
        rsp.blind = self.blind
        rsp.buyin = self.buyin
        return rsp

    def PushAction(self, user_name, action):
        self.queue.push((user_name, action))

    def GetStatus(self, user_name):
        rsp = RoomStatus()
        rsp.stage = self.stage
        for player in self.players:
            #print('get status find player {}, id = {}'.format(player.player_name,player.player_id))
            if player.player_name == user_name or self.stage == 1:
                rsp.players.append(player.GetStatus(show_hands=True))
            else:
                rsp.players.append(player.GetStatus(show_hands=False))
        for suit, value in self.public.get_pokers():
            rsp.public.append(Poker(suit=suit, value=value))
        rsp.banker = self.banker
        rsp.speak = self.speak
        #print('get status = {}'.format(rsp))
        return rsp

    def active_player(self):
        res = 0
        for player in self.players:
            if player.ready:
                res += 1
        return res

    def find_player(self, player_name):
        for player in self.players:
            if player.player_name == player_name:
                return player

    def run(self):
        try:
            self._run()
        except:
            print(traceback.format_exc())

    def _run(self):
        target = 0
        btn = 0
        ptr = 0
        while True:
            while len(self.players) == 0:
                time.sleep(1)
            if self.stage == 1:
                while not (len(self.players) >= 2
                           and self.active_player() == len(self.players)):
                    try:
                        user_name, action = self.queue.get()
                        print('recieve action:{} {}'.format(user_name, action))
                        if action['action'] == 'quit':
                            self.DelPlayer(user_name)
                            continue
                        elif action['action'] != 'ready':
                            continue
                        player = self.find_player(user_name)
                        player.ready = True
                        print('set player {} ready'.format(player.player_name))
                    except queue.Empty:
                        for player in self.players:
                            if not player.ready:
                                self.DelPlayer(player.player_name)
                        continue
                for player in self.players:
                    if player.counter < self.blind * 2:
                        player.BuyIn(self.buyin)
                #print('begin stage 2')
                self.stage = 2
            elif self.stage == 2:
                if self.change_banker:
                    self.banker = self.next_id(self.banker)
                self.poker_engine.wash()
                #TODO:发牌和清除状态
                for player in self.players:
                    player.hands.clear()
                    player.hands.draw(self.poker_engine)
                    player.hands.draw(self.poker_engine)
                    player.active = True
                self.public.clear()
                sb_idx = self.next_id(self.banker)
                sb = self.players[sb_idx - 1]
                sb.PutBlind(self.blind)
                bb_idx = self.next_id(sb_idx)
                bb = self.players[bb_idx - 1]
                bb.PutBlind(self.blind * 2)
                ptr = self.next_id(bb_idx)
                btn = ptr
                target = self.blind * 2
                first_ptr_flag = True
                #print('ptr = {}, btn = {}'.format(ptr, btn))
                while ptr != btn or (ptr == btn and first_ptr_flag):
                    if ptr == btn and first_ptr_flag:
                        first_ptr_flag = False
                    if self.players[ptr - 1].active:
                        self.speak = ptr
                        new_target = self.players[ptr - 1].Speak(
                            target, self.queue)
                        if new_target != target:
                            target = new_target
                            btn = ptr
                    ptr = self.next_id(ptr)
                self.stage = 3
            elif self.stage == 3:
                for i in range(3):
                    self.public.draw(self.poker_engine)
                ptr = self.next_id(self.banker)
                btn = ptr
                first_ptr_flag = True
                while ptr != btn or (ptr == btn and first_ptr_flag):
                    if ptr == btn and first_ptr_flag:
                        first_ptr_flag = False
                    if self.players[ptr - 1].active:
                        self.speak = ptr
                        new_target = self.players[ptr - 1].Speak(
                            target, self.queue)
                        if new_target != target:
                            target = new_target
                            btn = ptr
                    ptr = self.next_id(ptr)
                self.stage = 4
            elif self.stage == 4:
                self.public.draw(self.poker_engine)
                ptr = self.next_id(self.banker)
                btn = ptr
                first_ptr_flag = True
                while ptr != btn or (ptr == btn and first_ptr_flag):
                    if ptr == btn and first_ptr_flag:
                        first_ptr_flag = False
                    if self.players[ptr - 1].active:
                        self.speak = ptr
                        new_target = self.players[ptr - 1].Speak(
                            target, self.queue)
                        if new_target != target:
                            target = new_target
                            btn = ptr
                    ptr = self.next_id(ptr)
                self.stage = 5
            elif self.stage == 5:
                self.public.draw(self.poker_engine)
                ptr = self.next_id(self.banker)
                btn = ptr
                first_ptr_flag = True
                while ptr != btn or (ptr == btn and first_ptr_flag):
                    if ptr == btn and first_ptr_flag:
                        first_ptr_flag = False
                    if self.players[ptr - 1].active:
                        self.speak = ptr
                        new_target = self.players[ptr - 1].Speak(
                            target, self.queue)
                        if new_target != target:
                            target = new_target
                            btn = ptr
                    ptr = self.next_id(ptr)
                self.stage = 6
            elif self.stage == 6:
                #按牌型大小排序
                winners = CalWinners(self.players, self.public)
                for idx, win_player in enumerate(winners):
                    if win_player.active:  #如果玩家没有fold
                        for lose_player_idx in range(
                                len(winners) - 1, idx - 1, -1):
                            lose_player = winners[lose_player_idx]
                            if win_player.pool >= lose_player.pool:
                                money = lose_player.pool
                            else:
                                money = win_player.pool
                            win_player.counter += money
                            lose_player.pool -= money
                            print('player {} got {} from {}'.format(
                                win_player.player_name, money,
                                lose_player.player_name))
                self.stage = 1
                self.change_banker = True
                self.speak = 0  #all speak
                for player in self.players:
                    player.ready = False

    def next_id(self, idx, times=1):
        for i in range(times):
            idx = idx + 1
            if idx > len(self.players):
                idx = 1
        return idx

    def pre_id(self, idx, times=1):
        for i in range(times):
            idx = idx - 1
            if idx < 1:
                idx = len(self.players)
        return idx