# This is an advanced guess the number game.

import random # The very basis of this game.
import math # Used for math.ceil
import time # Used to split up long portions of text.


def errorDisplay(errorCode):

    if errorCode == '1':
        print('\n\n\nError ' + errorCode + ': unlockedGameModes outside of expected scope on line 36.')


def introDisplay(): # Displays the intro and defines the playerName Var. at the local scope, then returns it to the global scope.

    print('The Guessing Game! Now with more frustration!')
    playerName = input('\nHello, what is your name? ')
    print('\nWelcome ' + playerName + '.')
    print('\nThis is a guessing game with three different modes. It also has two different')
    print('settings for each mode. The range in which you will be guessing numbers, and')
    print('the amount of attempts you will have. You will be playing through the modes in')
    print('order, but you can always return to a game mode you\'ve unlocked.')
    #time.sleep(5)

    if playerName == 'Debug':
        gameModesUnlocked = input('\nEnter number of modes unlocked. <1/2/3> ')
        gameModesUnlocked = int(gameModesUnlocked)
    else:
        gameModesUnlocked = 1

    return playerName, gameModesUnlocked


def playGame(playerName, gameModesUnlocked):

    if gameModesUnlocked == 1:
        print('\nOk, ' + playerName + ' Let\'s play the first mode: Simple guessing game!')
        gameMode = 1
    elif gameModesUnlocked >= 2 and gameModesUnlocked <= 3:
        print('\nOk, ' + playerName + ' choose which mode you would like to play.')
        gameMode = chooseGameMode(gameModesUnlocked)
    else:
        errorCode = '1'
        return errorCode        

    rangeLower, rangeHigher, rangeLowerString, rangeHigherString = chooseRange()

    attemptsNumber, attemptsNumberString = chooseAttempts()


def chooseGameMode(gameModesUnlocked): # Here the player chooses the game mode and the chosen mode is returned to the playGame function.

    if gameModesUnlocked == 2:
        tempInput = input('The simple guessing game, or the one with addition and subtraction? <1/2> ')
        while True:
            if len(tempInput) == 1 and tempInput in '12':
                gameMode = int(tempInput)
                break
            else:
                tempInput = input('Please choose 1 or 2. ')
    elif gameModesUnlocked == 3:
        tempInput = input('The simple guessing game, the one with addition and subtraction, or the one with more advanced maths? <1/2/3> ')
        while True:
            if len(tempInput) == 1 and tempInput in '123':
                gameMode = int(tempInput)
                break
            else:
                tempInput = input('Please choose 1, 2 or 3. ')
    else:
        errorCode = 0
        gameMode = 0

    confirmGameMode(gameMode)

    return gameMode


def confirmGameMode(gameMode): # Used to confirm that the player is happy with their game mode setting. Called during the chooseGameMode function.

    if gameMode == 1:
        print('\nYou have chosen mode the first: Simple guessing game.', end=' ')
    elif gameMode == 2:
        print('\nYou have chosen mode the second: Guessing game with addition and subtraction.')
    elif gameMode == 3:
        print('\nYou have chosen mode the third: Guessing game with more advanced maths.')

    tempAnswer = input('Continue? <y/n> ').lower()
    while True:
        if tempAnswer == 'y':
            print('Ok!')
            return gameMode
        elif tempAnswer == 'n':
            print('\nThen, let\'s try again.')
            tempAnswer = '' # Dear daniweb: Kind of proved it "Please make a valid choice. <y/n>"
            gameMode = chooseGameMode(gameModesUnlocked)
        elif len(tempAnswer) != 1 or tempAnswer not in 'yn':
            tempAnswer = input('Please make a valid choice. <y/n> ').lower()


def chooseRange(): # Here the player chooses between 3 pre-set ranges to play in, or defines their own, via the defineRange function.

    rangeLower = 1
    rangeLowerString = '1' # These are defined here in the case that the user does not make their own range, they are the same in the three pre-sets.

    chosenRangeType = input('\nWould you like a small, medium or large range? Or perhaps you\'d like to\nchoose the range yourself? <s/m/l/*> ').lower()
    while True:
        if chosenRangeType == 's':          # This section accepts input for the pre-sets and break out of the main input loop, ending this function.
            rangeHigher = 20
            rangeHigherString = '20'
            break
        elif chosenRangeType == 'm':
            rangeHigher = 40
            rangeHigherString = '40'
            break
        elif chosenRangeType == 'l':
            rangeHigher = 60
            rangeHigherString = '60'
            break
        elif len(chosenRangeType) != 1:     # This section handles erroneous input for the main input loop.
            chosenRangeType = input('Please enter a single character. <s/m/l/*> ').lower()
        elif chosenRangeType not in 'sml*':
            chosenRangeType = input('Please make a valid choice. <s/m/l/*> ').lower()
        elif chosenRangeType == '*':        # Finally, this section handles the users that would like to define their own range. New input loop, and error handling.            
            tempAnswer = input('Really choose your own range? <y/n> ').lower()
            while True:
                if tempAnswer == 'y':
                    rangeLower, rangeHigher, rangeLowerString, rangeHigherString = defineRange()
                    break                    
                elif tempAnswer == 'n':
                    print('Then, let\'s try again.')
                    rangeLower, rangeHigher, rangeLowerString, rangeHigherString = chooseRange()
                elif len(tempAnswer) != 1:
                    tempAnswer = input('Please enter a single character. <y/n> ').lower()
                elif tempAnswer not in 'yn':
                    tempAnswer = input('Please make a valid choice. <y/n> ').lower()
            break

    confirmRange(rangeLowerString, rangeHigherString)

    return rangeLower, rangeHigher, rangeLowerString, rangeHigherString


def defineRange(): # Called when the player decides to define their own range to play in. Called during the chooseRange function.

    tempInput = input('Please choose the lower number in your range. (1-9980) ')
    while True:
        try:
            rangeLower = int(tempInput)
        except ValueError:
            tempInput = input('Please enter a number between 1 and 9980. ')
        if rangeLower in range(1,9981):
            rangeLowerString = str(tempInput)
            break
        else:
            tempInput = input('Please enter a number between 1 and 9980. ')

    tempInput = input('Please choose the higher number in your range. (20-10000) ')
    while True:
        try:
            rangeHigher = int(tempInput)
        except ValueError:
            tempInput = input('Please enter a number between 20 and 10000. ')
        if rangeHigher in range(20,10001):
            if rangeLower >= rangeHigher:
                tempInput = input('The second number should be higher than the first, try again. (20-10000)')
            else:
                rangeHigherString = str(tempInput)
                break
        else:
            tempInput = input('Please enter a number between 20 and 10000. ')

    return rangeLower, rangeHigher, rangeLowerString, rangeHigherString


def confirmRange(rangeLowerString, rangeHigherString): # Used to confirm that the player is happy with their range setting. Called during the chooseRange function.

    tempAnswer = input('\nYour range will be between ' + rangeLowerString + ' and ' + rangeHigherString + '. Are you happy with this? <y/n> .').lower()
    while True:
        if tempAnswer == 'y':
            print('Ok!')
            return rangeLowerString, rangeHigherString
        elif tempAnswer == 'n':
            print('Then, let\'s try again.')
            rangeLower, rangeHigher, rangeLowerString, rangeHigherString = chooseRange()
        elif len(tempAnswer) != 1:
            tempAnswer = input('Please enter a single character. <y/n> ').lower()
        elif tempAnswer not in 'yn':
            tempAnswer = input('Please make a valid choice. <y/n> ').lower()



########################################################################  PROGRAM  START 

playerName, gameModesUnlocked = introDisplay()
errorCode = playGame(playerName, gameModesUnlocked)
errorDisplay(errorCode)

This is my code for a guess the number game I'm making. I'd just been working on the confirmGameMode function and was testing it when I came up with some strange output:

You have chosen mode the second: Guessing game with addition and subtraction.
Continue? <y/n> y
Ok!
Please make a valid choice. <y/n> y
Ok!

Would you like a small, medium or large range? Or perhaps you'd like to
choose the range yourself? <s/m/l/*>

Just after the line of code that prints 'Ok' is a return keyword, so why is my output displaying output from further down that while loop? And more than that, how can it be displaying code from different if/elif statements, when it's not meant to be possible for a variable to be in two states?

steps to reproduce: compile and run, choose name 'Debug', choose unlocked modes '2', choose mode '2', continue? 'n', choose mode '2', continue? 'y', make a valid choice 'y'.

There are several lines where the code prints ok. Line count?
" Please make a valid choice. <y/n>" is scattered all over the place. How do you know which one is called?!
Run the code with idle debug mode to see what is happening.

The stack is too deep. You make main->playgame->choosegamemode->confirmgamemode(->choosegamemode) stack.

This game should fit into maximum 30 lines of relevant code.

The problem is solely with the choosegamemode/comfirmgamemode recursion. Feel free to comment out all the other function calls to prove that. After sleeping on it I think I might be able to see what's going on here.

choosegamemode is called, it calls confirmgamemode, which calls choosegamemode, which then calls confirmgamemode, this cycle could continue forever with the right input.

the problem is, the previous functions wont stop until their recursive functions have been closed.

This means that there are instances of a function running a single local variable in multiple states, this is a logical deduction based on the output. Now I think it would be better to return data to the global(main) scope and check its state from there, using the information to decide whether to recall a function.

Example:

while confirmedGameMode == '':
    gameMode = chooseGameMode(gameModesUnlocked) # Returns the choice.
    confirmedGameMode = confirmGameMode(gameMode) # Confirm breaks out of the while, otherwise rerun loop.

This could be run from main, and it would fix the problem of local variables bleeding through recursive functions.

But in the case where you don't want to return every single function to the main, how would you get around this?

Thanks Tony, I will look at you example. I rewrote my code to avoid the (semantic?) bug:

# This is an advanced guess the number game.

import random # The very basis of this game.
import math # Used for math.ceil
import time # Used to split up long portions of text.


def introDisplay(): # Displays the intro and defines the playerName Var. at the local scope, then returns it to the global scope.

    print('The Guessing Game! Now with more frustration!')
    playerName = input('\nHello, what is your name? ')
    print('\nWelcome ' + playerName + '.')
    print('\nThis is a guessing game with three different modes. It also has two different')
    print('settings for each mode. The range in which you will be guessing numbers, and')
    print('the amount of attempts you will have. You will be playing through the modes in')
    print('order, but you can always return to a game mode you\'ve unlocked.')
    #time.sleep(5)

    if playerName == 'Debug':
        gameModesUnlocked = input('\nEnter number of modes unlocked. <1/2/3> ')
        gameModesUnlocked = int(gameModesUnlocked)
    else:
        gameModesUnlocked = 1

    return playerName, gameModesUnlocked


def chooseGameMode(playerName, gameModesUnlocked): # Here the player chooses the game mode and the result is returned to the global scope.

    if gameModesUnlocked == 1:      # Ends this function if there is no possible choice to be made.
        print('\nOk, ' + playerName + ' Let\'s play the first mode: Simple guessing game!')
        gameMode = 1
        return gameMode

    print('\nOk, ' + playerName + ' choose which mode you would like to play.')
    if gameModesUnlocked == 2:        
        tempInput = input('The simple guessing game, or the one with addition and subtraction? <1/2> ')
        while True:
            if len(tempInput) == 1 and tempInput in '12':
                gameMode = int(tempInput)
                break
            else:
                tempInput = input('Please choose 1 or 2. ')
    elif gameModesUnlocked == 3:
        tempInput = input('The simple guessing game, the one with addition and subtraction, or the one with\nmore advanced maths? <1/2/3> ')
        while True:
            if len(tempInput) == 1 and tempInput in '123':
                gameMode = int(tempInput)
                break
            else:
                tempInput = input('Please choose 1, 2 or 3. ')
    else:
         print('Error!') # Work on error handling.

    return gameMode


def confirmGameMode(gameMode):# Used to confirm that the player is happy with their game mode setting. Result controls interaction with main loop 1.

    confirmedGameMode = int(gameMode) # Defined here to avoid repetition. Only returned in Escape #1/2.

    if gameModesUnlocked == 1:      # Ends this function if there is no possible choice to be made. Escape #1
        return confirmedGameMode

    if gameMode == 1:               # Reiterates to the chosen mode to the player.
        print('\nYou have chosen mode the first: Simple guessing game.', end=' ')
    elif gameMode == 2:
        print('\nYou have chosen mode the second: Guessing game with addition and subtraction.')
    elif gameMode == 3:
        print('\nYou have chosen mode the third: Guessing game with more advanced maths.')

    tempAnswer = input('Continue? <y/n> ').lower()
    while True:                     # Input loop.
        if tempAnswer == 'y':       # Returns and does break while loop in main. Escape #2
            print('\nOk!')
            return confirmedGameMode
        elif tempAnswer == 'n':     # Returns and does not break while loop in main. Escape #3
            print('\nThen, let\'s try again.')
            confirmedGameMode = None # Will not allow main loop to be broken.
            return confirmedGameMode
        elif len(tempAnswer) != 1 or tempAnswer not in 'yn':    # Bad input in 'tempAnswer', make new call and rerun input loop.
            tempAnswer = input('Please make a valid choice. <y/n> ').lower()

###### Start #####

playerName, gameModesUnlocked = introDisplay()

while True:     # Main loop 1: Game mode.
    gameMode = chooseGameMode(playerName, gameModesUnlocked)
    confirmedGameMode = confirmGameMode(gameMode, gameModesUnlocked)
    if confirmedGameMode != None:
        break

Now I'm just wondering how people feel about my use of the language, I only started Python 2 days ago and I don't really want to get into any bad habits.

This game should fit into maximum 30 lines of relevant code.

Maybe this game could fit into 30 lines of code, I sure as hell can't make it do so though, perhaps it would be helpful to give an example of how you condense 200 lines of code into 30. Bearing in mind that the game is only half finished.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.