## Fundamental misunderstanding The most significant problem with this code is a misunderstanding of functions. **Functions are not goto labels.** There is no reason for `getWords` and `game` to be mutually recursive. This leads to two problems: - Your functions are inflexible and not reusable. - If you play several rounds, the call stack gets deeper and deeper. Eventually, after many many rounds, the program can crash from stack overflow. **Each function should have a single purpose**, and it should be documented in a docstring. If a function is named "getWords", then I expect it to return a list of words, and do nothing more than that. It shouldn't pick a word and proceed to play an entire game based on it. A simple way to reorganize your code to use functions correctly might look like this: def get_word(): """ Ask the user for the difficulty level, and return a randomly selected word from the dictionary. """ # Do stuff …, then return word def game(true_word): """ Play a game of Mastermind, where the user needs to guess the given word. Return True if the user guesses correctly within 5 turns. """ for turn in range(5): … if user_input == true_word: print("Well done, you guessed correctly") return True … print(hint) print("Bad luck! The answer was: " + true_word) return False while True: game(get_word) But we can do even better by breaking down those two functions into even smaller functions. ## General remarks - Commenting nearly every line is mildly obnoxious. For example: > #num (used to store word lengths) is a random choice from the available lengths for the selected difficulty. That is just an indication that `num` is a vaguely named variable. A better choice would be `word_length` — and then you can eliminate the comment. > with open('wordbank.txt', 'r') as file: > #Open the file containing word-bank Python is quite readable. You don't have to tell me what the code obviously stated. - Please follow the PEP 8 official style guide, in particular the [naming conventions](https://www.python.org/dev/peps/pep-0008/#naming-conventions). Variables should be named like `true_word` or `user_input` unless you have a good reason to deviate. - Be suspicious of **global variables**, and avoid using them at all. Your `acceptedWord` list keeps growing, but never gets cleared. ## Specific suggestions The `difficulty` dictionary could be better formatted for readability. Its purpose is not immediately obvious, so I'd add a comment. import random # Possible word lengths for each difficulty level DIFFICULTY = { 1: [4, 5, 6], 2: [7, 8], 3: [9, 10, 11], 4: [12, 13], 5: [14, 15], } `getWords()` should be turned into a function that reads _and returns a data structure representing the entire file_, so that you don't have to re-read the file every time before starting a game. Asking for the difficulty level and selecting words are separate tasks that deserve to be in their own functions. def read_dictionary(filename='wordbank.txt'): """ Read the file, containing one word per line. Return a dictionary, keyed by word length. """ dictionary = {} for lengths in DIFFICULTY.values(): for length in lengths: dictionary[length] = [] with open(filename) as f: for line in f: word = line.strip().upper() dictionary.get(len(word), []).append(word) return dictionary def ask_level(): """ Ask the user for to select a valid difficulty level. """ allowable_levels = [str(level) for level in sorted(DIFFICULTY.keys())] prompt = "What difficulty would you like? ({}) ".format( ','.join(allowable_levels) ) while True: choice = input(prompt) if choice in allowable_levels: return int(choice) print("Invalid choice") def select_words(level, dictionary): """ Pick a sample of words from the dictionary with an appropriate length for the difficulty level. The number of words chosen is equal to the length of the chosen word. """ length = random.choice(DIFFICULTY[level]) return random.sample(dictionary[length], length) `game()` could be better named. Hard-coded arbitrary values are often better written as default parameters (`turns=5` in this case). There is no point in converting strings to lists. The `for` loop can be more elegantly written as a single expression. def mastermind_game(words, turns=5): """ Play a game of Worded Mastermind, where the user has to guess which of the words is correct within the specified number of turns. Return the number of remaining guesses (0 if the user lost, non-zero if the user won). """ print("Possible answers:") print('\n'.join(words)) word = random.choice(words) for remaining_guesses in range(turns, 0, -1): print("Guesses left: {}".format(remaining_guesses)) guess = input("Guess: ").upper() if guess == word: print("Well done, you guessed correctly") return remaining_guesses correct_letter_count = sum(g == w for g, w in zip(guess, word)) print("{} out of {} correct.".format(correct_letter_count, len(word))) print("Bad luck! The answer was: {}".format(word)) return 0 The main code should obviously give a high-level view of what the program does. In this case, it reads the dictionary once, then loops forever playing Mastermind games. It's customary to write `if __name__ == '__main__'` so that the code can be safely imported into other Python programs without immediately executing it. def main(wordlist_filename='wordbank.txt'): dictionary = read_dictionary(wordlist_filename) while True: level = ask_level() words = select_words(level, dictionary) mastermind_game(words) if __name__ == '__main__': main()