# Arup Guha
# 10/29/2020
# Blackjack!
# Edited on 11/3/2020, to complete the game!!!

import random

# Flag depending on whether we are in debug mode or not.
DEBUG = False

# Cards in our deck, initially.
NUMCARDS = 52

SUITS = ["Clubs", "Diamonds", "Hearts", "Spades"]

def main():

    # No matter what version we play, we want a shuffled deck.
    deck = makeUnshuffledDeck()
    deck = rifleShufflenTimes(deck,30)

    # Just for debugging.
    if DEBUG:
        print(deck)
        for c in deck:
            print(getCard(c),getCardValue(c))

    # Get who the person is playing against.
    choice = int(input("Do you want to play against the computer(1) or another person(2)?\n"))

    # First user.
    p1Score = 0
    p1Cards = []
    

    # Second user or computer.
    p2Score = 0
    p2Cards = []

    # Alternate dealing 2 cards to each.
    for i in range(2):
        p1Cards.append(dealcard(deck))
        p2Cards.append(dealcard(deck))

    # Just check if this worked or not.
    if DEBUG:
        printHand(p1Cards)
        printHand(p2Cards)

    # Initially, both players may want more cards.
    p1Flag = True
    p2Flag = True
    curPlayer = 1

    # Go as long as at least one player wants a card, maybe.
    while p1Flag or p2Flag:

        # Player one's turn.
        if curPlayer == 1 and p1Flag:

            # Show hand
            print("Player 1, here is your hand.")
            printHand(p1Cards)
            print("Your score is", getHandValue(p1Cards))
            newCard = input("Do you want a new card (yes/no)?\n")

            if newCard == "yes":
                thisCard = dealcard(deck)
                print("You got the card", getCard(thisCard))
                p1Cards.append(thisCard)

            if newCard != "yes":
                print("You won't receive any more cards.")
                p1Flag = False
                
            elif getHandValue(p1Cards) > 21:
                print("You have busted, so you won't receive more cards.")
                p1Flag = False

        # A second human player
        elif curPlayer == 2 and choice == 2 and p2Flag:

            # Show hand
            print("Player 2, here is your hand.")
            printHand(p2Cards)
            print("Your score is", getHandValue(p2Cards))
            newCard = input("Do you want a new card (yes/no)?\n")

            if newCard == "yes":
                thisCard = dealcard(deck)
                print("You got the card", getCard(thisCard))
                p2Cards.append(thisCard)

            if newCard != "yes":
                print("You won't receive any more cards.")
                p2Flag = False
                
            elif getHandValue(p2Cards) > 21:
                print("You have busted, so you won't receive more cards.")
                p2Flag = False

        # Computer player.
        elif choice == 1 and p2Flag:

            # Tell the user how the computer is doing.
            print("It's the computer's turn. It has")
            printHand(p2Cards)
            curCompScore = getHandValue(p2Cards)
            print("The computer score is", curCompScore)

            # Computer always hits if less than 17.
            if curCompScore < 17:
                print("The computer will take another card.")
                thisCard = dealcard(deck)
                print("The computer got the card", getCard(thisCard))
                p2Cards.append(thisCard)

            # Computer stands.
            else:
                print("The computer will stand and take no more cards.")
                p2Flag = False

            # Computer busted.
            if getHandValue(p2Cards) > 21:
                print("The computer has busted.")
                p2Flag = False

        # Change players
        if curPlayer == 1:
            curPlayer = 2
        else:
            curPlayer = 1
                

    # Call a function to determine the outcome.
    printResults(p1Cards, p2Cards, choice)


# Returns an unshuffled deck of 52 cards.
def makeUnshuffledDeck():
    deck = []
    for i in range(NUMCARDS):
        deck.append(i)
    return deck

# Shuffles deck by repeatedly swapping randomly chosen cards.
def shuffleRandom(deck):

    # Cards in deck.
    size = len(deck)
    
    # Do this 1000 times.
    for i in range(1000):

        # Pick two random slots in the deck.
        c1 = random.randint(0,size-1)
        c2 = random.randint(0,size-1)

        # Swap the values in those two slots.
        temp = deck[c1]
        deck[c1] = deck[c2]
        deck[c2] = temp

    # Return the new list
    return deck

def rifleShufflenTimes(deck,n):

    mydeck = rifleShuffle(deck)
    for i in range(n-1):
        mydeck = rifleShuffle(mydeck)
    return mydeck

# Leaves deck unchanged but returns a newly formed list created from
# deck by simulating a shuffle where you take 1 or 2 cards from one side
# and then swap sides.
def rifleShuffle(deck):

    # Store new cards here.
    newdeck = []

    # i is index into left half, j into right half.
    i = 0
    j = len(deck)//2

    # Which side I take from.
    side = random.randint(0,1)

    while i<len(deck)//2 or j<len(deck):
        #print("len new",len(newdeck),"len deck",len(deck))

        # Take from left.
        if (side == 0 and i < len(deck)//2) or (side == 1 and j==len(deck)):

            # Figure out 1 or 2.
            numCards = random.randint(1,2)

            # Special case.
            if i == len(deck)//2 - 1:
                numCards = 1

            # Adds each card from the deck to this side.
            for loc in range(i, i+numCards):
                #print("in left loc",loc)
                newdeck.append(deck[loc])

            # Update where we will add from next time.
            i += numCards

        else:

            # Figure out 1 or 2.
            numCards = random.randint(1,2)

            # Special case.
            if j == len(deck) - 1:
                numCards = 1

            # Adds each card from the deck to this side.
            for loc in range(j, j+numCards):
                #print("loc is ",loc)
                newdeck.append(deck[loc])

            # Update where we will add from next time.
            j += numCards            


        # Flips the side for next time.
        side = 1 - side

    # Return it.
    return newdeck

# Removes and returns the top card in deck.
def dealcard(deck):
    return deck.pop(0)

# Returns the String version of a card given its number.
def getCard(cardVal):

    # The suit is from the SUITS array, and we just index with the card number
    # divided by 13.
    suit = SUITS[cardVal//13]

    # This is the kind of card.
    kind = cardVal%13

    if kind == 0:
        return "King of "+suit
    elif kind == 1:
        return "Ace of "+suit
    elif kind == 11:
        return "Jack of "+suit
    elif kind == 12:
        return "Queen of "+suit
    else:
        return str(kind)+" of "+suit

# Prints each card in the hand, one card per line, numbered.
def printHand(hand):

    # Go through each card and print it, one per line.
    for i in range(len(hand)):
        print(str(i+1)+". "+ getCard(hand[i]))

# Returns the value of the card.
def getCardValue(cardVal):

    # The suit is from the SUITS array, and we just index with the card number
    # divided by 13.
    suit = SUITS[cardVal//13]

    # This is the kind of card.
    kind = cardVal%13

    # All face cards and ten.
    if kind == 0 or kind >= 10:
        return 10

    # The ace.
    elif kind == 1:
        return 11

    # Rest of the cards.
    else:
        return kind

# Returns the score of this hand.
def getHandValue(hand):

    # Keep track of both the score and the # of aces.
    score = 0
    aceCnt = 0

    # Go through each card.
    for c in hand:

        # Get the value of this card.
        cardScore = getCardValue(c)

        # Add to our score.
        score += cardScore

        # Update our ace count.
        if cardScore == 11:
            aceCnt += 1

    # Here we potentially subtract points if the person has busted but has aces.
    while score > 21 and aceCnt > 0:
        score -= 10
        aceCnt -= 1

    # This is your final score.
    return score

# Prints the results given that player 1 has the cards in p1Cards, player 2 has
# the cards in p2Cards and computer == 1 means that the second player was a
# computer player, or a human player otherwise.
def printResults(p1Cards, p2Cards, computer):

    # Get both scores.
    score1 = getHandValue(p1Cards)
    score2 = getHandValue(p2Cards)

    # Both busted.
    if score1 > 21 and score2 > 21:
        print("Both players busted! There is no winner.")

    # Player 2 busted only.
    elif score2 > 21:

        # Different message depending on human or computer.
        if computer == 1:
            print("The computer busted, so player 1 won!")
        else:
            print("Player 2 busted, so player 1 won!")

    # Player 1 busted only.
    elif score1 > 21:

        # Different message depending on if player 2 is a computer or human.
        if computer == 1:
            print("The computer beat you, since you busted!")
        else:
            print("Player 1 busted, so player 2 won!")

    # Player 1 won without anyone busting.
    elif score1 > score2:

        if computer == 1:
            print("You beat the computer!")
        else:
            print("Player 1, you beat Player 2!")

    # Player 2 won without anyone busting.
    elif score2 > score1:

        if computer == 1:
            print("The computer beat you!")
        else:
            print("Player 2, you beat Player 1!")

    # Last case is a time.
    else:
        print("Both players tied!")

# Run it!
main()
