<reponame>mwhite317/blackjack
import sys

from src.model.blackjack_players import *
from src.model.deck import *
from src.model.human_connections import SavePlayer


class Game:
    def __init__(self, human_player):
        self.human_player = human_player
        self.deck = Deck()
        self.players = [self.human_player]
        self.num_decks = 1
        for i in range(self.num_decks - 1):
            self.deck.add_deck_to_deck(Deck())

    def start(self):
        """
        Adds chips to human player if they start game with 0. Calls round class to begin round.

        :return: none
        """
        pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}
        if self.human_player.num_of_chips() == 0:
            print(
                "{} {}, It seems you don't have any chips with you. Don't worry we will buy you in..."
                "\ntransferring chips ...\n"
                    .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            self.human_player.add_chips(500)
            print(self.human_player.chip_string())
            self.shall_we_play()

        else:
            print("Welcome back {} {}, good to see you again."
                  .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            print(self.human_player.chip_string())
            self.shall_we_play()

        while self.human_player_left():
            rund = Round(self.deck, self.players)
            rund.start_round()
            self.cash_out()

    def human_player_left(self):
        """
        Checks if any human players are left in the round.

        :return: number  of human player left
        """
        return self.human_player.num_of_chips() > 0

    def shall_we_play(self):
        """
        Confirms if player wants to play. Provides an exit if the player wants to leave the game.

        :return: none
        """
        play_input = input("\nShall we play?\n" "1. yes\n" "2. no\n" ": ")

        while play_input not in "12":

            play_input = input("\nShall we play?\n" "1. yes\n" "2. no\n" ": ")
        if play_input == "1":
            pass

        elif play_input == "2":
            print("\nSaving...")
            print("Please remember to type in same first name, last name, and gender from the beginning of the "
                  "Gamgame to "
                  "access your account.")
            save = SavePlayer()
            save.save(self.human_player)
            pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}
            print("Goodbye {} {}, hope to see you soon."
                  .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            sys.exit()



    def cash_out(self):
        """
         Buys human players back in the game.

        :return: none
        """
        if self.human_player.num_of_chips() < 6:
            self.human_player.add_chips(250)
            print("It seems you do not have enough chips. Here is an additional 250 chips to keep you going.")
            print(self.human_player.chip_string())


class Round:
    def __init__(self, deck, players):
        self.deck = deck
        self.pot = 0
        self.dealer = Dealer("", "Dealer", "X")
        self.players = players + [self.dealer]
        self.human_player = players[0]
        self.human_bust = False
        self.player_continues = True
        self.dealer_continues = True
        self.option_functions = {
            "Hit": self.hit,
            "Double": self.double,
            "Split": self.split,
            "Stand": self.stand,
            "Fold": self.fold
        }

    def start_round(self):
        """
        Calls shuffle function. For each player, asks for buy in using calls buy_in function,
         then asks for bet using betting function.

        :return: none
        """
        self.shuffle()
        for p in self.players:
            self.buy_in(p)
        self.deal_cards()
        while self.player_continues:
            for p in self.players:
                self.betting(p)
        self.end_of_round()

    def deal_cards(self):
        """
        Deals two card to each player in the round.
        Note that each player receives one card before dealer gives a second.

        :return:  none
        """
        for p in self.players:
            p.take_card(self.deck.draw_card())

        for p in self.players:
            p.take_card(self.deck.draw_card())
        print("\nDealing Cards...\n")

    def shuffle(self):
        """
        Shuffles deck by calling Deck class in Deck file.

        :return: none
        """
        self.deck.shuffle_deck()
        print("Shuffling cards...\n")

    def options_string(self, options):
        """
        Formats the option menu. Adjust numbers depending on available options.

        :param options: refers to the options set in Hand class from Deck file.
        :return: the organised menu for player
        """
        output = "Please choose one...\n"
        i = 1
        for option in options:
            output += "{}. {}\n".format(i, option) if option != "" else ""
            i += 1 if option != "" else 0
        return output

    def betting(self, player):
        """
        Checks if player is human. Prints player's cards and dealer's first card for human player to see.
        Prints menu for player. Takes menu input from player.

        :param player:  player who is playing the round
        :return: none
        """
        if not player.is_human():
            return
        print(player.hand_string())
        print(self.dealer.hand_string())
        options = player.hand.get_options()

        if min(self.human_player.hand.values) < 22:
            while True:
                try:
                    choice_input = input(self.options_string(options) + ": ")
                    print("")

                    while int(choice_input) not in range(1, len(options) + 1):
                        choice_input = int(input("Error! " + self.options_string(options) + ": "))
                        print("")
                    break
                except:
                    pass

            choice = int(choice_input) - 1
            self.option_functions[options[choice]](player)
            return

        self.bust()

    def hit(self, player):
        """
        Player is given another card.

        :param player: player who is betting
        :return: the updated player's cards
        """
        player.take_card(self.deck.draw_card())
        print("Dealing a card...\n")
        self.player_continues = True

    def double(self, player):
        """
        Player pays the additional same amount paid in buy_in function. Player is given another card and is prevented
        to bet afterwards.

        :param player: player who is betting
        :return: none
        """
        self.pot += player.bets
        self.player_continues = False
        print("Doubling ...\n"
              "Transferring {} chips into the pot...\n".format(player.bets))
        player.bet_chips(player.bets)
        player.take_card(self.deck.draw_card())

    def split(self, player):
        # TODO implement split
        pass

    def stand(self, player):
        """
        Prevents player to continue betting.

        :param player: player who is betting
        :return: none
        """
        self.player_continues = False
        print("You stand.")

    def fold(self, player):
        """
        Prevents the player to continue betting.

        :param player: player who is betting
        :return: none
        """
        self.player_continues = False
        self.human_bust = True
        print("FOLD\n")

    def bust(self):
        """
         Prevents the player and dealer to continue betting. Player loses automatically.

        :return: print statement
        """

        self.player_continues = False
        self.human_bust = True
        self.dealer_continues = False
        print("BUST!\n")

    def end_of_round(self):
        """
        Determines winner if player did not bust or fold. Adds pot amount to winning player and resets values.

        :return: none
        """
        if self.human_bust:
            print("Dealer wins.\n")
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

            self.reset()
            return

        self.dealer_turn()

        print("Results:")
        print(self.human_player.hand_string())
        print(self.dealer.hand_string())

        # determine winner
        self.dealer.hand.values = [value for value in self.dealer.hand.values if value <= 21] + [0]
        self.human_player.hand.values = [value for value in self.human_player.hand.values if value <= 21] + [0]
        if max(self.dealer.hand.values) < max(self.human_player.hand.values):
            print("You win!\n")
            self.human_player.add_chips(self.pot)
            print("{} chips awarded to you!".format(self.pot))
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        elif max(self.dealer.hand.values) > max(self.human_player.hand.values):
            print("Dealer wins.\n")
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        else:
            print("It's a tie.\n")
            self.pot = self.pot / 2
            self.human_player.add_chips(self.pot)
            print("{} chips awarded to you!".format(self.pot))
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        self.reset()

    def dealer_turn(self):
        """
        Allows the dealer to hit, stand or fold
        :return:
        """
        self.dealer.bet_chips(self.pot)
        self.pot += self.pot
        print(self.human_player.hand_string())
        self.dealer.hand.__class__ = Hand
        print(self.dealer.hand_string())
        while self.dealer_continues:
            option = self.dealer.choose_option()
            if option == "Hit":
                self.dealer.take_card(self.deck.draw_card())
                print("Dealer hits...")
                print(self.dealer.hand_string())
            if option == "Bust":
                self.dealer_continues = False
                print("Dealer Busts.")
                print(self.dealer.hand_string())
            if option == "Stand":
                self.dealer_continues = False
                print("Dealer stands...")
                print(self.dealer.hand_string())

    def reset(self):
        """
        Resets hands, decks, and reshuffles deck.

        :return:
        """
        self.pot = 0
        self.reset_hands()
        self.deck = Deck()

    def reset_hands(self):
        """
        Resets each players hand.

        :return: none
        """
        for player in self.players:
            player.reset_hand()

    def buy_in(self, player):
        """
        Player buys in.
        Restrictions: must be more than 5 and within the account balance.
        Buy amount is then deducted from player account balance and added to pot.

        :param player: player who is betting
        :return: buy in amount
        """
        if not player.is_human():
            return
        print("-- Buy in is at least 5 chips --")
        while True:
            try:
                buyin = input("Enter your bet (or q to quit): ")
                if buyin in "qQ":
                    self.saving_game()
                    sys.exit()
                buyin = int(buyin)
                while buyin < 5 or buyin > player.num_of_chips():
                    if buyin < 5:
                        buyin = int(input("Bet too low! Enter your bet: "))
                    if buyin > player.num_of_chips():
                        buyin = int(input("Not enough funds! Enter your bet: "))
                break
            except ValueError:
                pass

        self.pot += buyin
        player.bet_chips(buyin)

    def saving_game(self):
        """
        Allows player to continue from where the ended.

        :return: none
        """
        pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}

        print("Saving...")
        print("Please remember to type in same first name, last name, and gender from the beginning of the game to "
              "access your account.")
        save = SavePlayer()
        save.save(self.human_player)
        print("Goodbye {} {}, hope to see you soon."
              .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))


if __name__ == '__main__':
    game = Game(Human("Mark", "White", "M", Chips(10)))
    game.start()

import sys

from src.model.blackjack_players import *
from src.model.deck import *
from src.model.human_connections import SavePlayer


class Game:
    def __init__(self, human_player):
        self.human_player = human_player
        self.deck = Deck()
        self.players = [self.human_player]
        self.num_decks = 1
        for i in range(self.num_decks - 1):
            self.deck.add_deck_to_deck(Deck())

    def start(self):
        """
        Adds chips to human player if they start game with 0. Calls round class to begin round.

        :return: none
        """
        pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}
        if self.human_player.num_of_chips() == 0:
            print(
                "{} {}, It seems you don't have any chips with you. Don't worry we will buy you in..."
                "\ntransferring chips ...\n"
                    .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            self.human_player.add_chips(500)
            print(self.human_player.chip_string())
            self.shall_we_play()

        else:
            print("Welcome back {} {}, good to see you again."
                  .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            print(self.human_player.chip_string())
            self.shall_we_play()

        while self.human_player_left():
            rund = Round(self.deck, self.players)
            rund.start_round()
            self.cash_out()

    def human_player_left(self):
        """
        Checks if any human players are left in the round.

        :return: number  of human player left
        """
        return self.human_player.num_of_chips() > 0

    def shall_we_play(self):
        """
        Confirms if player wants to play. Provides an exit if the player wants to leave the game.

        :return: none
        """
        play_input = input("\nShall we play?\n" "1. yes\n" "2. no\n" ": ")

        while play_input not in "12":

            play_input = input("\nShall we play?\n" "1. yes\n" "2. no\n" ": ")
        if play_input == "1":
            pass

        elif play_input == "2":
            print("\nSaving...")
            print("Please remember to type in same first name, last name, and gender from the beginning of the "
                  "Gamgame to "
                  "access your account.")
            save = SavePlayer()
            save.save(self.human_player)
            pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}
            print("Goodbye {} {}, hope to see you soon."
                  .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))
            sys.exit()



    def cash_out(self):
        """
         Buys human players back in the game.

        :return: none
        """
        if self.human_player.num_of_chips() < 6:
            self.human_player.add_chips(250)
            print("It seems you do not have enough chips. Here is an additional 250 chips to keep you going.")
            print(self.human_player.chip_string())


class Round:
    def __init__(self, deck, players):
        self.deck = deck
        self.pot = 0
        self.dealer = Dealer("", "Dealer", "X")
        self.players = players + [self.dealer]
        self.human_player = players[0]
        self.human_bust = False
        self.player_continues = True
        self.dealer_continues = True
        self.option_functions = {
            "Hit": self.hit,
            "Double": self.double,
            "Split": self.split,
            "Stand": self.stand,
            "Fold": self.fold
        }

    def start_round(self):
        """
        Calls shuffle function. For each player, asks for buy in using calls buy_in function,
         then asks for bet using betting function.

        :return: none
        """
        self.shuffle()
        for p in self.players:
            self.buy_in(p)
        self.deal_cards()
        while self.player_continues:
            for p in self.players:
                self.betting(p)
        self.end_of_round()

    def deal_cards(self):
        """
        Deals two card to each player in the round.
        Note that each player receives one card before dealer gives a second.

        :return:  none
        """
        for p in self.players:
            p.take_card(self.deck.draw_card())

        for p in self.players:
            p.take_card(self.deck.draw_card())
        print("\nDealing Cards...\n")

    def shuffle(self):
        """
        Shuffles deck by calling Deck class in Deck file.

        :return: none
        """
        self.deck.shuffle_deck()
        print("Shuffling cards...\n")

    def options_string(self, options):
        """
        Formats the option menu. Adjust numbers depending on available options.

        :param options: refers to the options set in Hand class from Deck file.
        :return: the organised menu for player
        """
        output = "Please choose one...\n"
        i = 1
        for option in options:
            output += "{}. {}\n".format(i, option) if option != "" else ""
            i += 1 if option != "" else 0
        return output

    def betting(self, player):
        """
        Checks if player is human. Prints player's cards and dealer's first card for human player to see.
        Prints menu for player. Takes menu input from player.

        :param player:  player who is playing the round
        :return: none
        """
        if not player.is_human():
            return
        print(player.hand_string())
        print(self.dealer.hand_string())
        options = player.hand.get_options()

        if min(self.human_player.hand.values) < 22:
            while True:
                try:
                    choice_input = input(self.options_string(options) + ": ")
                    print("")

                    while int(choice_input) not in range(1, len(options) + 1):
                        choice_input = int(input("Error! " + self.options_string(options) + ": "))
                        print("")
                    break
                except:
                    pass

            choice = int(choice_input) - 1
            self.option_functions[options[choice]](player)
            return

        self.bust()

    def hit(self, player):
        """
        Player is given another card.

        :param player: player who is betting
        :return: the updated player's cards
        """
        player.take_card(self.deck.draw_card())
        print("Dealing a card...\n")
        self.player_continues = True

    def double(self, player):
        """
        Player pays the additional same amount paid in buy_in function. Player is given another card and is prevented
        to bet afterwards.

        :param player: player who is betting
        :return: none
        """
        self.pot += player.bets
        self.player_continues = False
        print("Doubling ...\n"
              "Transferring {} chips into the pot...\n".format(player.bets))
        player.bet_chips(player.bets)
        player.take_card(self.deck.draw_card())

    def split(self, player):
        # TODO implement split
        pass

    def stand(self, player):
        """
        Prevents player to continue betting.

        :param player: player who is betting
        :return: none
        """
        self.player_continues = False
        print("You stand.")

    def fold(self, player):
        """
        Prevents the player to continue betting.

        :param player: player who is betting
        :return: none
        """
        self.player_continues = False
        self.human_bust = True
        print("FOLD\n")

    def bust(self):
        """
         Prevents the player and dealer to continue betting. Player loses automatically.

        :return: print statement
        """

        self.player_continues = False
        self.human_bust = True
        self.dealer_continues = False
        print("BUST!\n")

    def end_of_round(self):
        """
        Determines winner if player did not bust or fold. Adds pot amount to winning player and resets values.

        :return: none
        """
        if self.human_bust:
            print("Dealer wins.\n")
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

            self.reset()
            return

        self.dealer_turn()

        print("Results:")
        print(self.human_player.hand_string())
        print(self.dealer.hand_string())

        # determine winner
        self.dealer.hand.values = [value for value in self.dealer.hand.values if value <= 21] + [0]
        self.human_player.hand.values = [value for value in self.human_player.hand.values if value <= 21] + [0]
        if max(self.dealer.hand.values) < max(self.human_player.hand.values):
            print("You win!\n")
            self.human_player.add_chips(self.pot)
            print("{} chips awarded to you!".format(self.pot))
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        elif max(self.dealer.hand.values) > max(self.human_player.hand.values):
            print("Dealer wins.\n")
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        else:
            print("It's a tie.\n")
            self.pot = self.pot / 2
            self.human_player.add_chips(self.pot)
            print("{} chips awarded to you!".format(self.pot))
            self.dealer.add_chips(self.pot)
            print("Current amount: {}".format(self.human_player.num_of_chips()))

        self.reset()

    def dealer_turn(self):
        """
        Allows the dealer to hit, stand or fold
        :return:
        """
        self.dealer.bet_chips(self.pot)
        self.pot += self.pot
        print(self.human_player.hand_string())
        self.dealer.hand.__class__ = Hand
        print(self.dealer.hand_string())
        while self.dealer_continues:
            option = self.dealer.choose_option()
            if option == "Hit":
                self.dealer.take_card(self.deck.draw_card())
                print("Dealer hits...")
                print(self.dealer.hand_string())
            if option == "Bust":
                self.dealer_continues = False
                print("Dealer Busts.")
                print(self.dealer.hand_string())
            if option == "Stand":
                self.dealer_continues = False
                print("Dealer stands...")
                print(self.dealer.hand_string())

    def reset(self):
        """
        Resets hands, decks, and reshuffles deck.

        :return:
        """
        self.pot = 0
        self.reset_hands()
        self.deck = Deck()

    def reset_hands(self):
        """
        Resets each players hand.

        :return: none
        """
        for player in self.players:
            player.reset_hand()

    def buy_in(self, player):
        """
        Player buys in.
        Restrictions: must be more than 5 and within the account balance.
        Buy amount is then deducted from player account balance and added to pot.

        :param player: player who is betting
        :return: buy in amount
        """
        if not player.is_human():
            return
        print("-- Buy in is at least 5 chips --")
        while True:
            try:
                buyin = input("Enter your bet (or q to quit): ")
                if buyin in "qQ":
                    self.saving_game()
                    sys.exit()
                buyin = int(buyin)
                while buyin < 5 or buyin > player.num_of_chips():
                    if buyin < 5:
                        buyin = int(input("Bet too low! Enter your bet: "))
                    if buyin > player.num_of_chips():
                        buyin = int(input("Not enough funds! Enter your bet: "))
                break
            except ValueError:
                pass

        self.pot += buyin
        player.bet_chips(buyin)

    def saving_game(self):
        """
        Allows player to continue from where the ended.

        :return: none
        """
        pronouns = {"M": "Mr.", "F": "Ms.", "X": "Mx."}

        print("Saving...")
        print("Please remember to type in same first name, last name, and gender from the beginning of the game to "
              "access your account.")
        save = SavePlayer()
        save.save(self.human_player)
        print("Goodbye {} {}, hope to see you soon."
              .format(pronouns.get(self.human_player.gender, "X"), self.human_player.last_name))


if __name__ == '__main__':
    game = Game(Human("Mark", "White", "M", Chips(10)))
    game.start()
Adds chips to human player if they start game with 0. Calls round class to begin round.          :return: none Checks if any human players are left in the round.          :return: number  of human player left Confirms if player wants to play. Provides an exit if the player wants to leave the game.          :return: none Buys human players back in the game.          :return: none Calls shuffle function. For each player, asks for buy in using calls buy_in function,          then asks for bet using betting function.          :return: none Deals two card to each player in the round.         Note that each player receives one card before dealer gives a second.          :return:  none Shuffles deck by calling Deck class in Deck file.          :return: none Formats the option menu. Adjust numbers depending on available options.          :param options: refers to the options set in Hand class from Deck file.         :return: the organised menu for player Checks if player is human. Prints player's cards and dealer's first card for human player to see.         Prints menu for player. Takes menu input from player.          :param player:  player who is playing the round         :return: none Player is given another card.          :param player: player who is betting         :return: the updated player's cards Player pays the additional same amount paid in buy_in function. Player is given another card and is prevented         to bet afterwards.          :param player: player who is betting         :return: none # TODO implement split Prevents player to continue betting.          :param player: player who is betting         :return: none Prevents the player to continue betting.          :param player: player who is betting         :return: none Prevents the player and dealer to continue betting. Player loses automatically.          :return: print statement Determines winner if player did not bust or fold. Adds pot amount to winning player and resets values.          :return: none # determine winner Allows the dealer to hit, stand or fold         :return: Resets hands, decks, and reshuffles deck.          :return: Resets each players hand.          :return: none Player buys in.         Restrictions: must be more than 5 and within the account balance.         Buy amount is then deducted from player account balance and added to pot.          :param player: player who is betting         :return: buy in amount Allows player to continue from where the ended.          :return: none