Ejemplo n.º 1
0
def index():
    title = 'random'

    deck = Deck()

    deck.shuffle()

    card = deck.drawCard()

    return render_template('index.html', title=title, card=card)
Ejemplo n.º 2
0
 def test_shuffle(self):
     d1 = Deck()
     old = []
     for i in xrange(20):
         old.append(d1.cards[i].rank)
     d1.shuffle()
     same = True
     for i in xrange(20):
         if old[i] != d1.cards[i].rank:
             same = False
             break
     self.assertEqual(same, False)
Ejemplo n.º 3
0
class Table:
    INIT_CHIPS = 100
    SMALL_BET = 2
    BIG_BET = 4

    def __init__(self, players_classes: List[Type[BasicPlayer]]) -> None:
        self._init_chips = self.INIT_CHIPS
        self._deck = Deck()
        self._players = self._create_players(players_classes)
        self._total_players = self._players.count()
        self._pot = 0
        self._pot_leftover = 0
        self._community_cards = []
        self._current_bet = 0
        self._small_bet = self.SMALL_BET
        self._big_bet = self.BIG_BET
        self._current_raise = self._small_bet
        self._raise_cnt = 0
        self._is_round_active = True
        self._is_game_active = True
        self._observers = Observers()
        self._players_who_lost = []
        self._current_phase = None

    def run_tournament(self) -> None:
        while self._is_game_active:
            self._init_pre_flop_phase()
            self._init_flop_phase()
            self._init_turn_phase()
            self._init_river_phase()
            self._init_showdown_phase()
            self._init_pot_collection_phase()
            self._prepare_next_round()

    def reset_tournament(self) -> None:
        self._reset_players()
        self._reset_player_chips()
        self._reset_play()
        self._is_game_active = True

    def get_winner_name(self) -> str:
        if self._is_winner_present():
            return self._players.name
        return str(None)

    @property
    def player_names(self) -> List[str]:
        names = []
        for player in self._players:
            names.append(player.name)

        return names

    def attach_observer(self, observer: BaseObserver) -> None:
        self._observers.attach(observer)

    def detach_observer(self, observer: BaseObserver) -> None:
        self._observers.detach(observer)

    def _create_players(self, players_classes: List[Type[BasicPlayer]]) -> TablePlayers:
        if len(players_classes) < 2 or len(players_classes) > 10:
            raise ValueError('Only between 2 and 10 players allowed...')

        dealer = None
        previous_table_player = None

        for player_cnt in range(len(players_classes)):
            player_name = f'Player_{str(player_cnt + 1)} ({players_classes[player_cnt].__name__})'
            basic_player = players_classes[player_cnt](self._init_chips, player_name)

            if not isinstance(basic_player, BasicPlayer):
                raise ValueError('Class has to be extended from game.Player base class')

            table_player = TablePlayers(basic_player)

            if previous_table_player is not None:
                previous_table_player.next = table_player
            else:
                dealer = table_player

            previous_table_player = table_player

        previous_table_player.next = dealer

        return dealer

    def _init_pre_flop_phase(self) -> None:
        self._current_phase = Phases.PRE_FLOP

        self._deck.shuffle()
        self._deal_cards()
        self._collect_blinds()
        self._current_raise = self._current_bet = self._small_bet

        self._init_betting_round(self._players.get_by_position(3))

        self._update_round_active_state()

        self._notify_observers()

    def _init_flop_phase(self) -> None:
        self._current_phase = Phases.FLOP

        self._deal_community_cards(3)

        if self._is_round_active:
            self._init_common_phase(self._small_bet)

        self._notify_observers()

    def _init_turn_phase(self) -> None:
        self._current_phase = Phases.TURN

        self._deal_community_cards()

        if self._is_round_active:
            self._init_common_phase(self._big_bet)

        self._notify_observers()

    def _init_river_phase(self) -> None:
        self._current_phase = Phases.RIVER

        self._deal_community_cards()

        if self._is_round_active:
            self._init_common_phase(self._big_bet)

        self._notify_observers()

    def _init_showdown_phase(self) -> None:
        self._current_phase = Phases.SHOWDOWN

        self._find_players_final_hand()

        self._notify_observers()

    def _init_pot_collection_phase(self) -> None:
        self._current_phase = Phases.POT_COLLECTION

        sorted_players = self._sort_players_by_score()
        individual_pot_collection = self._split_pot_among_players(sorted_players)
        pot_leftover_collections = 0

        for player in self._players:
            if player in individual_pot_collection:
                collecting_chips = self._take_from_pot(individual_pot_collection[player])
                same_win_amount = list(individual_pot_collection.values()).count(individual_pot_collection[player])
                total_pot_leftover = self._pot_leftover * (pot_leftover_collections + 1)
                if self._pot_leftover > 0 and total_pot_leftover % same_win_amount == 0:
                    collecting_chips += self._take_from_pot_leftover(int(total_pot_leftover / same_win_amount))
                    pot_leftover_collections += 1

                player.receive_chips(collecting_chips)
            else:
                player.receive_chips(0)

        self._pot_leftover += self._take_from_pot(self._pot)

        self._notify_observers(individual_pot_collection)

    def _prepare_next_round(self) -> None:
        self._define_next_dealer()
        self._kick_out_players_who_lost()
        self._update_game_active_state()

        if self._is_game_active:
            self._reset_play()

    def _init_common_phase(self, bet_size: int) -> None:
        self._prepare_common_phase(bet_size)

        self._init_betting_round(self._players.next)

        self._update_round_active_state()

    def _prepare_common_phase(self, bet_amount: int) -> None:
        self._current_raise = bet_amount
        self._raise_cnt = 0
        self._current_bet = 0

        for player in self._players:
            player.current_bet = 0
            if player.current_move is not Moves.FOLD and player.current_move is not Moves.ALL_IN:
                player.current_move = None

    def _init_betting_round(self, stopping_player: TablePlayers) -> None:
        player = stopping_player

        while True:
            self._update_round_active_state()
            if not self._is_round_active:
                break

            moves = self._generate_player_moves(player)

            if moves is None:
                player = player.next
                if player is stopping_player:
                    break
                continue

            move = player.make_move(moves, self.generate_game_state(tuple(moves)))

            raise_cnt_before_move_execution = self._raise_cnt

            self._execute_player_move(player, move)

            if player.current_move is Moves.RAISE or (
                    player.current_move is Moves.ALL_IN and raise_cnt_before_move_execution != self._raise_cnt):
                stopping_player = player

            player = player.next

            if player is stopping_player:
                break

        for player in self._players:
            if player.current_move is Moves.ALL_IN and player.is_active:
                player.is_active = False

    def _deal_cards(self) -> None:
        for _ in range(2):
            self._deal_one_round()

    def _deal_community_cards(self, amount: int = 1):
        self._deck.burn()
        self._community_cards += self._deck.deal(amount)

    def _deal_one_round(self) -> None:
        for player in self._players:
            player.receive_cards(self._deck.deal())

    def _collect_blinds(self) -> None:
        player = self._players.next
        self._collect_blind(player, int(self._small_bet / 2))

        player = player.next
        self._collect_blind(player, self._small_bet)

        self._current_bet = self._small_bet

    def _collect_blind(self, player: TablePlayers, blind: int) -> None:
        if player.get_amount_of_chips() > blind:
            self._collect_bet(player, blind)
        else:
            amount = player.get_amount_of_chips()
            self._collect_bet(player, amount)
            player.current_move = Moves.ALL_IN

    def _collect_bet(self, player: TablePlayers, amount: int) -> None:
        self._pot += player.spend_chips(amount)
        player.current_bet += amount
        player.total_bet += amount

    def _generate_player_moves(self, player: TablePlayers) -> Optional[List[Moves]]:
        moves = list()

        if player.current_move is Moves.FOLD or player.get_amount_of_chips() == 0:
            return None

        if player.current_bet < self._current_bet < player.get_amount_of_chips() + player.current_bet:
            moves.append(Moves.CALL)

        if player.get_amount_of_chips() + player.current_bet <= self._current_bet \
                or (player.get_amount_of_chips() + player.current_bet <= (self._current_bet + self._current_raise)
                    and not self._is_raising_capped()):
            moves.append(Moves.ALL_IN)

        if self._current_bet == player.current_bet:
            moves.append(Moves.CHECK)

        if not self._is_raising_capped() \
                and player.get_amount_of_chips() + player.current_bet > (self._current_bet + self._current_raise) \
                and self._players.count() - (
                len(self._players.find_by_move(Moves.FOLD)) + len(self._players.find_by_move(Moves.ALL_IN))) > 1:
            moves.append(Moves.RAISE)

        moves.append(Moves.FOLD)

        return moves

    def _is_raising_capped(self) -> bool:
        return not self._raise_cnt < 4

    def generate_game_state(self, allowed_moves: Tuple[Moves]) -> State:
        return State(
            community_cards=tuple(Card(c.rank, c.suit, c.value) for c in self._community_cards),
            total_players=self._total_players,
            total_chips=self._total_players * self._init_chips,
            nbr_of_active_players=self._players.count() - len(self._players.find_by_move(Moves.FOLD)),
            current_phase=self._current_phase,
            is_raising_capped=self._is_raising_capped(),
            allowed_moves=allowed_moves,
            pot=self._pot,
            current_bet=self._current_bet
        )

    def _execute_player_move(self, player: TablePlayers, move: Moves) -> None:

        if move is Moves.CALL:
            amount = self._calculate_amount_to_call(player)
            self._collect_bet(player, amount)

        elif move is Moves.RAISE:
            amount = self._calculate_amount_to_raise(player)
            self._collect_bet(player, amount)
            self._current_bet = player.current_bet
            self._raise_cnt += 1

        elif move is Moves.ALL_IN:
            amount = player.get_amount_of_chips()
            self._collect_bet(player, amount)
            if self._current_bet < player.current_bet:
                self._raise_cnt += 1
                self._current_bet = player.current_bet
            else:
                player.is_active = False

        elif move is Moves.FOLD:
            player.is_active = False

        player.current_move = move

    def _calculate_amount_to_call(self, player: TablePlayers) -> int:
        return self._current_bet - player.current_bet

    def _calculate_amount_to_raise(self, player: TablePlayers) -> int:
        return self._calculate_amount_to_call(player) + self._current_raise

    def _update_round_active_state(self) -> None:
        total_players = self._players.count()
        folded_players = len(self._players.find_by_move(Moves.FOLD))
        not_folded_players = total_players - folded_players
        none_move_players = len(self._players.find_by_move(None))

        self._is_round_active = (self._players.count_active()) > 1 or (not_folded_players > 1 and none_move_players > 0)

    def _find_players_final_hand(self) -> None:

        for player in self._players:
            if player.current_move is not Moves.FOLD:
                final_hand = StrongestFinalHandFinder.find(self._community_cards + player.get_hand())

                player.final_hand = final_hand.hand
                player.final_hand_type = final_hand.type
                player.score = final_hand.score

    def _sort_players_by_score(self) -> List[List[TablePlayers]]:
        sorted_players = list()

        for player in self._players:
            sorted_players = self._insert_player_in_sorted_list(player, sorted_players)

        return sorted_players

    def _insert_player_in_sorted_list(self, player: TablePlayers, sorted_players: List[List[TablePlayers]]) \
            -> List[List[TablePlayers]]:
        has_player_been_inserted = False

        for i in range(len(sorted_players)):
            if player.score > self._players.find(sorted_players[i][0]).score:
                sorted_players.insert(i, [player])
                has_player_been_inserted = True

            elif player.score == self._players.find(sorted_players[i][0]).score:
                comparing_player = sorted_players[i][0]
                stronger_player = self._find_stronger_player_on_draw(player, comparing_player)

                if stronger_player is None:
                    sorted_players[i].append(player)
                    has_player_been_inserted = True

                elif player is stronger_player:
                    sorted_players.insert(i, [player])
                    has_player_been_inserted = True

            if has_player_been_inserted:
                break

        if not has_player_been_inserted:
            sorted_players.append([player])

        return sorted_players

    @staticmethod
    def _find_stronger_player_on_draw(player_1: TablePlayers, player_2: TablePlayers) -> Optional[TablePlayers]:
        if player_1.final_hand is None:
            return None

        for i, player_1_card in enumerate(player_1.final_hand):
            if player_1_card.value > player_2.final_hand[i].value:
                return player_1

            if player_2.final_hand[i].value > player_1_card.value:
                return player_2

        return None

    def _split_pot_among_players(self, players_grouped_by_strength: List[List[TablePlayers]]) \
            -> Dict[TablePlayers, int]:
        players_pot_collections = {player: 0 for player in self._players}
        is_pot_collection = True

        while is_pot_collection:
            collecting_players = players_grouped_by_strength.pop(0)
            sub_pot = self._calculate_sub_pot(collecting_players, players_grouped_by_strength)

            if sub_pot is None:
                pot = 0

                for player in self._players:
                    pot += player.total_bet

                pot_division = pot / len(collecting_players)

                for player in collecting_players:
                    players_pot_collections[player] = int(pot_division)

                is_pot_collection = False

            else:
                individual_pot_collection = self._split_sub_pot_among_players(collecting_players)
                is_pot_collection = self._should_pot_collection_continue(collecting_players,
                                                                         players_grouped_by_strength)

                for player in collecting_players:
                    players_pot_collections[player] = int(individual_pot_collection[player])

                if is_pot_collection:
                    self._update_players_total_bet(collecting_players, players_grouped_by_strength)

        return players_pot_collections

    def _calculate_sub_pot(self, collecting_players: List[TablePlayers],
                           players_grouped_by_strength: List[List[TablePlayers]]) -> Optional[int]:
        is_any_player_all_in = False
        sub_pot = 0

        for player in collecting_players:
            player_bet = player.total_bet
            sub_pot += player_bet

            if player.current_move is Moves.ALL_IN:
                is_any_player_all_in = True

        if not is_any_player_all_in:
            return None
        else:
            highest_bet = self._find_highest_player_bet(collecting_players)
            for player_group in players_grouped_by_strength:
                sub_pot += self._calculate_sub_pot_portion(player_group, highest_bet)

            return sub_pot

    @staticmethod
    def _calculate_sub_pot_portion(players: List[TablePlayers], amount: int) -> int:
        sub_pot_portion = 0

        for player in players:
            if player.total_bet < amount:
                sub_pot_portion += player.total_bet
            else:
                sub_pot_portion += amount

        return sub_pot_portion

    def _split_sub_pot_among_players(self, collecting_players: List[TablePlayers]) -> Dict[TablePlayers, int]:
        individual_pot_collection = {p: 0 for p in collecting_players}
        highest_bet = self._find_highest_player_bet(collecting_players)
        players_bets_asc = [{
            'bet': highest_bet,
            'total_players': 0,
            'collecting_players': 0
        }]

        for player in self._players:
            index = None
            for i, player_bet in enumerate(players_bets_asc):
                if player.total_bet < player_bet['bet']:
                    index = i
                    break

            if index is not None:
                players_bets_asc.insert(index, {
                    'bet': player.total_bet,
                    'total_players': 0,
                    'collecting_players': 0
                })

        for player in self._players:
            for player_bet in players_bets_asc:
                if player.total_bet == player_bet['bet']:
                    player_bet['total_players'] += 1
                    if player in collecting_players:
                        player_bet['collecting_players'] += 1
                    break

                if player.total_bet > player_bet['bet']:
                    player_bet['total_players'] += 1
                    if player in collecting_players:
                        player_bet['collecting_players'] += 1

        for player in collecting_players:
            sub = 0
            for player_bet in players_bets_asc:
                if player.total_bet >= player_bet['bet']:
                    individual_pot_collection[player] += \
                        ((player_bet['bet'] - sub) * player_bet['total_players']) / player_bet['collecting_players']
                sub = player_bet['bet']

        return individual_pot_collection

    @staticmethod
    def _find_lowest_bet(players: List[TablePlayers]) -> int:
        comparing_player = players[0]
        lowest_bet = comparing_player.total_bet

        for i in range(1, len(players)):
            if players[i].total_bet < lowest_bet:
                lowest_bet = players[i].total_bet

        return lowest_bet

    def _should_pot_collection_continue(self, collecting_players: List[TablePlayers],
                                        players_grouped_by_strength: List[List[TablePlayers]]) -> bool:
        should_collection_continue = False
        highest_collecting_player_bet = 0

        for player in collecting_players:
            if player.total_bet > highest_collecting_player_bet:
                highest_collecting_player_bet = player.total_bet

        for player_group in players_grouped_by_strength:
            highest_not_collecting_player_bet = self._find_highest_player_bet(player_group)

            if highest_not_collecting_player_bet > highest_collecting_player_bet:
                should_collection_continue = True
                break

        return should_collection_continue

    @staticmethod
    def _find_highest_player_bet(players: List[TablePlayers]) -> int:
        comparing_player = players[0]
        highest_bet = comparing_player.total_bet

        for i in range(1, len(players)):
            if players[i].total_bet > highest_bet:
                highest_bet = players[i].total_bet

        return highest_bet

    def _update_players_total_bet(self, collecting_players: List[TablePlayers],
                                  players_grouped_by_strength: List[List[TablePlayers]]) -> None:
        highest_bet = self._find_highest_player_bet(collecting_players)

        for player in collecting_players:
            player.total_bet = 0

        for player_group in players_grouped_by_strength:
            self._reduce_players_total_bet(player_group, highest_bet)

    @staticmethod
    def _reduce_players_total_bet(players: List[TablePlayers], amount: int) -> None:
        for player in players:
            if player.total_bet < amount:
                player.total_bet = 0
            else:
                player.total_bet -= amount

    def _take_from_pot(self, amount: int) -> int:
        self._pot -= amount
        return amount

    def _take_from_pot_leftover(self, amount: int) -> int:
        self._pot_leftover -= amount
        return amount

    def _define_next_dealer(self) -> None:
        for player in self._players:
            if player.next.get_amount_of_chips() > 0:
                self._players = player.next
                break

    def _kick_out_players_who_lost(self) -> None:
        players_who_lost = []

        for player in self._players:
            if player.get_amount_of_chips() == 0:
                players_who_lost.append(player)

        for player in players_who_lost:
            self._players.remove_player(player)
            self._players_who_lost.append(player)

    def _reset_play(self) -> None:
        self._community_cards = []
        self._current_bet = 0
        self._current_raise = 0
        self._is_round_active = True
        self._raise_cnt = 0
        self._deck = Deck()

        for player in self._players:
            player.reset()

    def _update_game_active_state(self) -> None:
        self._is_game_active = not self._is_winner_present()

    def _is_winner_present(self) -> bool:
        return self._players.count() == 1

    def _reset_players(self) -> None:
        players = []

        for p in self._players_who_lost:
            players.append(p)

        for p in self._players:
            players.append(p)

        np = None
        for p in players:
            if np is not None:
                np.next = p
            np = p
        np.next = players[0]
        self._players = np.next
        self._players_who_lost = []

    def _reset_player_chips(self) -> None:
        for p in self._players:
            a = p.get_amount_of_chips()
            p.spend_chips(a)
            p.receive_chips(self._init_chips)

    def _notify_observers(self, individual_pot_collection: Optional[Dict[TablePlayers, int]] = None) -> None:
        state = ObserverState(
            players=self._players,
            community_cards=deepcopy(self._community_cards),
            pot=self._pot,
            phase=self._current_phase,
            individual_pot_collection=individual_pot_collection
        )
        self._observers.notify(state)

    def print_player_info(self):
        print('--- PLAYER INFO ---')
        for player in self._players:
            hand = player.get_hand()
            print('NAME:\t\t' + str(player.name))
            print('HAND:\t\t' + (str(hand[0].rank) + ' ' + str(hand[0].suit) + ', '
                                 + str(hand[1].rank) + ' ' + str(hand[1].suit) if len(hand) > 0 else str(
                hand)))
            print('CURRENT_BET:\t' + str(player.current_bet))
            print('CURRENT_MOVE:\t' + str(player.current_move))
            print('CURRENT_CHIPS:\t' + str(player.get_amount_of_chips()))
            print('SCORE:\t\t' + str(player.score))
            print('FINAL_HAND:\t' + str(player.final_hand_type))
            print()

    def print_state_info(self):
        cards = ''
        for card in self._community_cards:
            cards += '[' + str(card.rank) + ' ' + str(card.suit + '] ')

        print('--- STATE INFO ---')
        print('IS_ACTIVE:\t' + str(self._is_round_active))
        print('SMALL_BET:\t' + str(self._small_bet))
        print('BIG_BET:\t' + str(self._big_bet))
        print('POT:\t\t' + str(self._pot))
        print('DEALER:\t\t' + str(self._players.name))
        print('CURRENT_BET:\t' + str(self._current_bet))
        print('CURRENT_RAISE:\t' + str(self._current_raise))
        print('RAISE_CNT:\t' + str(self._raise_cnt))
        print('COMMUNITY_CARDS:' + cards)
        print()
Ejemplo n.º 4
0
class GameEngine:
    class States(Enum):
        INIT = 1
        PLAYING = 2
        FINISHED = 3

    def __init__(self):
        self.state = self.States.INIT
        self.current_player_index = 0
        self.players = []
        self.deck = Deck()
        self.rules = Rules()
        self.stack = Stack()
        self.cheat_card = ""

    def initialize_game(self):
        self.deck.build()
        self.deck.shuffle()
        self.rules.read_rules()

    def create_players(self):
        number_players = int(input("Enter the number of players? "))
        for player_number in range(1, number_players + 1):
            player_question = f"Enter the name of player{player_number}? "
            name = input(player_question)
            self.players.append(Player(name))

    def current_player(self):
        return self.players[self.current_player_index]

    def deal_cards(self):
        player_index = 0
        for card in self.deck.cards:
            self.players[player_index].hand.append(card)
            player_index += 1
            if player_index >= len(self.players):
                player_index = 0

    def next_player(self):
        if self.current_player().no_more_cards():
            print(f"{self.current_player().name} won the game!!!!!")
            self.state = self.States.FINISHED

        self.current_player_index += 1
        if self.current_player_index >= len(self.players):
            self.current_player_index = 0

    def previous_player(self):
        if self.current_player_index > 0:
            return self.players[self.current_player_index - 1]
        else:
            return self.players[len(self.players) - 1]

    def print_rules(self):
        print(self.rules)

    def game_loop(self):
        while self.state != self.States.FINISHED:
            if self.state == self.States.INIT:
                self.create_players()
                self.deal_cards()
                self.state = self.States.PLAYING

            print(f"{self.current_player().name} it is your turn")
            print(self.stack)
            print(self.current_player())

            command = input((f"What do you want to do?"
                             " (help, playcard, cheater) "))

            if command == "help":
                self.print_rules()
            elif command == "playcard":
                call_card = input("Which card do you want to play? ")
                if self.current_player().has_card(call_card):
                    card = self.current_player().get_card(call_card)
                    self.stack.add_card(card)
                    self.cheat_card = input(("What card do you "
                                             "want to say you "
                                             "played? "))
                    self.next_player()
                else:
                    print("You don't have that card")
            elif command == "cheater":
                lastcard = self.stack.get_last_card()
                print(f"Last card was: {lastcard}")
                if self.cheat_card == str(lastcard):
                    print((f"No, {self.previous_player().name} did not cheat, "
                           "you will get all the played cards"))
                    played_cards = self.stack.get_cards()
                    self.current_player().add(played_cards)
                    self.stack.clear()
                else:
                    print((f"Yes, you are right {self.previous_player().name} "
                           f"cheated. {self.previous_player().name} will get "
                           "all played cards"))
                    played_cards = self.stack.get_cards()
                    self.previous_player().add(played_cards)
                    self.stack.clear()

    def start_game(self):
        self.initialize_game()
        self.game_loop()