I'm stuck in recursive function

Hello, I’m new in Python. I follow a python bootcamp course in Udemy, and one of the project is a blackjack game. I found the code is simple, but feels a bit too simple. Instead showing the card, it just showing a card value. I made some tweaks here and there, using recursion, aand it’s break a bit.

I’ve documented some of the problems I got in comments section in # !— —! pattern. Maybe it’s because I’m a newbie, yet trying to chew things I can’t swallowed.
Please shed some lights to me.

``````import random

# ---- INITIALIZATION ----
# Cards

the_card = {
"2" : 2,
"3" : 3,
"4" : 4,
"5" : 5,
"6" : 6,
"7" : 7,
"8" : 8,
"9" : 9,
"10": 10,
"J" : 10,
"Q" : 10,
"K" : 10,
"A" : 11,
}

# Initial account

initial = int(input("How many dollars you bring to the table?  \$"))

# ---- Functions ----
# ---- Betting function ----

def bet(acc):
''' Betting function '''
while True:
your_bet = int(input("How many you willing to bet?  \$"))
if your_bet > acc:
continue
else:
return your_bet
# ---- Draw cards ----
def dealing(cards):
''' Dealing card function. Return card lists and their value. '''
print("Game start.")
your_card = [random.choice(list(cards.keys())) for i in range(0, 2)]
your_val =  sum(cards[i] for i in your_card)
dealer_card = [random.choice(list(cards.keys())) for i in range(0, 2)]
dealer_val = sum(cards[i] for i in dealer_card)
return your_card, your_val, dealer_card, dealer_val
'''Draw another card '''
new_card = random.choice(list(cards.keys()))
hand.append(new_card)
new_val = sum(cards[i] for i in hand)
return hand, new_val
# ---- Compare hands ----
# !--- open_hand() returns None ---!
def open_hand(hand, hand_val, dealer_hand, dealer_val, acc, bet, cards):
''' Checking both hands' cards value. Return user money amount.'''
if dealer_val <= 17:
# Triggered when the dealer's hand is under 17.
print("Dealer's hand value is under 17. Dealer will draw another card.")
open_hand(hand, hand_val, new_dealer_hand, new_dealer_val, acc, bet, cards)
elif hand_val > 21 and dealer_val > 21:
# Will be triggered when your hand is more than 21.
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print("Both hands are over 21. It's draw.")
return acc
elif hand_val > 21 and dealer_val < 21:
# Will be triggered if user hand is over 21 and dealer's hand is under 21.
acc -= bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You lose. Your money now is \${acc}.")
return acc
elif hand_val < 21 and dealer_val > 21:
# Will be triggered if
acc += bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You win! Your money now is \${acc}.")
return acc
elif hand_val < 21 and dealer_val < 21:
# Triggered when both hands are under 21.
# Whoever have higher hand, wins.
if hand_val < dealer_val:
acc -= bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You lose. Your money now is \${acc}.")
return acc
elif hand_val > dealer_val:
acc += bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You win! Your money now is \${acc}.")
return acc
elif hand_val == dealer_val:
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print("Both hands are same. It's draw.")
return acc
# --- Control if user will stop or continue.
def if_continue(money, cards):
still_play = input("Do you want to play again? Press y to continue, or any other keys if you want to stop:  > ").lower()
if still_play == "y":
game_21(money, cards, status=True)
else:
# !--- After end_game() excecuted, the program is not terminated ---!
# !--- It's back to game_21() ---!
end_game(money)
# ---- End game ----
def end_game(money):
return f"Game over, your money is \${money}."
# ---- The body of program ----
# ---- Combining all other functions ----
# ---- Indirect recursion to game_21() from if_continue() ----
def game_21(money, cards, status):
''' Core game program.'''
put_bet = bet(money)
print("Let's begin the game.")
hand, hand_val, dealer_hand, dealer_val = dealing(cards)
print(f"Your hand is {hand} with value {hand_val}.\nDealer's first card is {dealer_hand[0]} with value {cards[dealer_hand[0]]}.")
# Need one more recursive here.
# ! --- The loop become endless ---!
while status:
if hand_val < 21:
draw_more = input("Do you want to draw again? Press y to continue, or any other keys if you want to stand:  > ").lower()
if draw_more =="y":
print(f"Your hand is {hand} with value {hand_val}.\nDealer's first card is {dealer_hand[0]} with value {cards[dealer_hand[0]]}.")
continue
else:
# !--- PROBLEM: open_hand() return None ---!
money = open_hand(hand, hand_val, dealer_hand, dealer_val, money, put_bet, cards)
if_continue(money, cards)
elif hand_val == 21:
money += put_bet
print("Blackjack!")
if_continue(money, cards)
else:
money -= put_bet
print(f"Your hand value is over than 21. You lose \${put_bet}. Your money now is \${money}.")
if_continue(money, cards)
status = False

# ---- PROGRAM STARTS HERE ----

game_21(initial, the_card, status = True)

# !--- Error raised ---!
# !--- TypeError: '>' not supported between instances of 'int' and 'NoneType' ---!
# !--- Help T_T ---!
``````

Thank you for your help, folks

Sorry if the code looks too cluttered.

Hey @yafethtb , it was a bit difficult to read your code, but simply adding two blank lines between each function (per PEP 8, the standard for Python code style) makes it much easier to read. Also, just FYI, per the docstring standard (PEP 257), docstrings should always use triple double quotes (`"""`), rather than triple single quotes (`'''`), and no spaces around them.

With those trivialities out of the way, onward to your issues!

First, in Python, if a function body finishes executing without an explicit return statement, the function returns `None`. If we look at all of the branches of `open_hand`, we can see that all of them `return acc`, which presumably should be a number…all but one of them, that is, the very first branch; its last statement calls `open_hand()` again which might return `acc`, but once execution returns to the top-level `open_hand` function, that function implicitly returns `None`, since the first branch doesn’t have a return statement. To fix this, just have it return the return value of `open_hand`, i.e. `return open_hand(...)`. If you prefer, for similarity with the others, you can also do:

``````acc = open_hand(...)
return acc
``````

This fixes `open_hand` returning `None` and the consequent `TypeError` (since `acc` in the parent function is set to `None`, which you of course cannot add numeric values to).

However, we still have a problem, as you noted: if we type `y` to play another game, everything works, but if we don’t, the original game continues on, with the dealer continuing to draw cards and you continuing to win, regardless of what you type. Again, we trace the code to see what is going on. We can see that after `open_hand()` is called by `game_21`, `if_continue` is then called. The user is asked whether they still want to play; if they enter `y`, `game_21()` is called again…okay, that works, I guess. But if they don’t, `end_game` is called, and here our troubles begin.

The `end_game` function returns a string that looks like it was meant to be `print`ed instead. Then, back inside of `if_continue`, that string, the return value of `end_game`, is neither assigned to anything or returned, so it has no effect. Since there is no `return` statement in that branch, `if_continue` returns `None`. We’re now back to where `if_continue` was called…but its value itself gets thrown away, and `status` is not modified. Since status never gets set to `False` within this branch, the original `game_21` loop continues indefinitely, unless you choose to hit (draw) and your hand busts (i.e. its over 21), in which case that codepath has a `status = False`, which breaks the loop.

I’m guessing that you meant to make that last `status = False` at the level directly under the `while` loop, rather than at the level of the `else` branch, so it runs whenever your hand isn’t under 21 and you don’t choose to hit, and terminates the game. That’s a common mistake, even sometimes among experienced programmers. So, to fix this problem, you’d move `status = False` up one indentation level, and now everything works! One final tweak: as implied before, you probably want to make `end_game` print the message rather than return it (i.e. `print(f"Game over, your money is \${money}.")`), so the user actually sees it.

To note, the recursive design is really not the best for this, as if you play a large number of games, functions keep getting added to the stack and it eventually goes too many levels deep, resulting in a `RecursionError`. A better design, if you were to implement this for real, would be to use an outer `while` loop handling the `if_continue` and calling the `game_21` function.

One thing that was helpful in my testing calling `random.seed(SEED)` first thing. Setting `SEED = None` produces a random game, while setting `SEED = 42` (or any integer) produces deterministic behavior, the same every time…very useful for testing, and you can vary the seed to try out different behavior.

By the way, you might not be at an advanced enough point in your development, but this might be a good idea to use a class to encapsulate the state of the game, instead of passing all the different variables around, which is more complex, harder to follow, more work and a greater chance of bugs.

One final note…technically speaking, only a ten and an ace on the first hand is a blackjack, and true blackjacks pay out at 3:2 instead of 1:1 of the bet. Otherwise, a 21 is just a 21, and is treated the same as any other card value. As a small challenge, how might you handle that in the code?

Cheers, and may the odds ever be in your favor!

By the way, in case you’re stuck or the above is unclear, here’s a copy of your code with just the above tweaks applied. I also took the liberty of one small additional modification: instead of returning `acc` in every code path of `open_hand`, I instead just assign the result of `open_hand` to `acc` in the first and return `acc` at the very end. This avoids a lot of duplicated code and the resulting bug potential.

``````import random

# Set SEED to None for a random game, or to an integer for a deterministic one
SEED = None

random.seed(SEED)

# ---- INITIALIZATION ----
# Cards

the_card = {
"2" : 2,
"3" : 3,
"4" : 4,
"5" : 5,
"6" : 6,
"7" : 7,
"8" : 8,
"9" : 9,
"10": 10,
"J" : 10,
"Q" : 10,
"K" : 10,
"A" : 11,
}

# Initial account

initial = int(input("How many dollars you bring to the table?  \$"))

# ---- Functions ----
# ---- Betting function ----

def bet(acc):
''' Betting function '''
while True:
your_bet = int(input("How many you willing to bet?  \$"))
if your_bet > acc:
continue
else:
return your_bet

# ---- Draw cards ----
def dealing(cards):
''' Dealing card function. Return card lists and their value. '''
print("Game start.")
your_card = [random.choice(list(cards.keys())) for i in range(0, 2)]
your_val =  sum(cards[i] for i in your_card)
dealer_card = [random.choice(list(cards.keys())) for i in range(0, 2)]
dealer_val = sum(cards[i] for i in dealer_card)
return your_card, your_val, dealer_card, dealer_val

'''Draw another card '''
new_card = random.choice(list(cards.keys()))
hand.append(new_card)
new_val = sum(cards[i] for i in hand)
return hand, new_val

# ---- Compare hands ----
def open_hand(hand, hand_val, dealer_hand, dealer_val, acc, bet, cards):
''' Checking both hands' cards value. Return user money amount.'''
if dealer_val <= 17:
# Triggered when the dealer's hand is under 17.
print("Dealer's hand value is under 17. Dealer will draw another card.")
acc = open_hand(hand, hand_val, new_dealer_hand, new_dealer_val, acc, bet, cards)
elif hand_val > 21 and dealer_val > 21:
# Will be triggered when your hand is more than 21.
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print("Both hands are over 21. It's draw.")
elif hand_val > 21 and dealer_val < 21:
# Will be triggered if user hand is over 21 and dealer's hand is under 21.
acc -= bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You lose. Your money now is \${acc}.")
elif hand_val < 21 and dealer_val > 21:
# Will be triggered if
acc += bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You win! Your money now is \${acc}.")
elif hand_val < 21 and dealer_val < 21:
# Triggered when both hands are under 21.
# Whoever have higher hand, wins.
if hand_val < dealer_val:
acc -= bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You lose. Your money now is \${acc}.")
elif hand_val > dealer_val:
acc += bet
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print(f"You win! Your money now is \${acc}.")
elif hand_val == dealer_val:
print(f'Your hand is {hand} with value {hand_val}.\nDealer cards are{dealer_hand} with value {dealer_val}.')
print("Both hands are same. It's draw.")

return acc

# --- Control if user will stop or continue.
def if_continue(money, cards):
still_play = input("Do you want to play again? Press y to continue, or any other keys if you want to stop:  > ").lower()
if still_play == "y":
game_21(money, cards, status=True)
else:
end_game(money)

# ---- End game ----
def end_game(money):
print(f"Game over, your money is \${money}.")

# ---- The body of program ----
# ---- Combining all other functions ----

# ---- Indirect recursion to game_21() from if_continue() ----
def game_21(money, cards, status):
''' Core game program.'''
put_bet = bet(money)
print("Let's begin the game.")
hand, hand_val, dealer_hand, dealer_val = dealing(cards)
print(f"Your hand is {hand} with value {hand_val}.\nDealer's first card is {dealer_hand[0]} with value {cards[dealer_hand[0]]}.")
# Need one more recursive here.
while status:
if hand_val < 21:
draw_more = input("Do you want to draw again? Press y to continue, or any other keys if you want to stand:  > ").lower()
if draw_more =="y":
print(f"Your hand is {hand} with value {hand_val}.\nDealer's first card is {dealer_hand[0]} with value {cards[dealer_hand[0]]}.")
continue
else:
money = open_hand(hand, hand_val, dealer_hand, dealer_val, money, put_bet, cards)
if_continue(money, cards)
elif hand_val == 21:
money += put_bet
print("Blackjack!")