# Magic ball 8 program

I was doing the Magic Ball 8 project (its task is to read a user's question and output a prediction) on Python and wrote the following code:

from random import choice
from time import sleep
list_of_predictions = [
"Undoubtedly", "It seems to me - yes", "It is not clear yet, try again", "Do not even think", "A foregone conclusion", "Most likely", "Ask later", "My answer is no",
"No doubt", "Good prospects", "Better not to tell", "According to my information - no", "You can be sure of this", "Yes", "Concentrate and ask again", "Very doubtful"
]
print('I am a magic ball, and I know the answer to any of your questions.')
sleep(1)
n = input("What is your name?\n")
print(f'Greetings, {n}.')
sleep(1)
def is_question(question):
return q[-1] == '?'
def is_valid_question(question):
return ('When' or 'How' or 'Where') not in q
again = 'yes'
while again == 'yes':
print('Enter a question, it must end with a question mark.')
q = input()
if is_question(q) and is_valid_question(q):
sleep(2)
print(choice(list_of_predictions))
elif not is_question(q):
print('You  entered not a question!')
continue
else:
print('You have entered a question that cannot be answered "yes" or "no"!')
continue
sleep(1)
again = input('Are there any other questions?\n')
sleep(1)
a = input('Come back if they arise!\n')


The main feature of my program is that it checks if the user has entered a question, which can be answered "yes" or "no". It also works with small delays in time (for realism). What do you think about my project implementation?

• You probably want to reject "Why?" and "Who?" questions as well... Dec 1 '21 at 7:57
• Yes, need to take this into account Dec 1 '21 at 7:58
• @TobySpeight - "Is the Who the greatest rock band of all time?" And now we see the difficulty of natural language processing. Dec 1 '21 at 19:00
• @ribs2spare - did you miss the not in that condition? Dec 1 '21 at 20:06
• Forgive me if I am missing some quirk of python, but don't the is_question and is_valid_question functions completely ignore their inputs as currently implemented? Dec 1 '21 at 22:21

I'd like to address how you handle the yes/no question controlling the loop.

The relevant sections of your code are:

again = 'yes'

while again == 'yes':
# [...]
again = input('Are there any other questions?\n')
# [...]


Almost all user input are interpreted as "no". However, the user has no information that they are supposed to enter the exact string yes for a positive answer. It is common for command line programs to accept y or n as an input, but neither of these will be treated as a positive answer in your code. Same for Yes, YES (different capitalization),  yes, yes  (leading/trailing white space), yeah, yep (different words), while all of these answer are clearly positives.

Similarly, inputs that are neither positive nor negative will be interpreted as negative, while the issue may be a typo or a misunderstanding of the answer expected. In this case, the input should be discarded, extra information given to the user on why, and the question asked again.

This makes handling this input much more complex, but as asking yes/no questions is very common in CLI programs, if you write a function that handles this, you only need to write it once and you are then free to reuse it many more times in the future.

My take on it is:

def get_yes_no_input(prompt):
POSITIVE_VALUES = ['yes',
'y',
'yeah',
'yep',
'true',
'1']
NEGATIVE_VALUES = ['no',
'n',
'nope',
'false',
'0']
while True:
print(prompt)
val = input('> ').strip().lower()
if val in POSITIVE_VALUES:
return True
if val in NEGATIVE_VALUES:
return False


• since the function is meant to be reusable, it takes the question text as an argument (prompt)
• it returns a boolean value, as it can be handled differently according to the use case, and they are easier to work with than strings
• it uses constant lists for positive and negative answer, from which you can easily add or remove values
• the input is stripped to handle potential leading/trailing white space
• the input is lowered to handle capitalization issues
• if the input is neither positive or negative, it is rejected and information about the expected answer is given

Use in your code would be something like this:

again = True

while again:
# [...]
again = get_yes_no_input('Are there any other questions?')
# [...]
$$$$

• Or more advanced way NLP can be used if you want more, happy coding ;) Dec 1 '21 at 11:15
• Thank you for the recomendations! Dec 1 '21 at 13:36
• Why did You capitalize lists of positive and negative values? I want the program to match the PEP-8, are list names allowed to be formatted like that? Dec 1 '21 at 13:41
• @vlados155 All caps are used for constants. Although PEP8 recommends this for module-level constants, there is no recommendations for method- or class-level constants, so I did what I thought was best. You are free to disagree with me and use another naming convention in this case, just be consistent throughout your own code. Dec 1 '21 at 13:56
• Thank You for the clarification. Dec 1 '21 at 14:04

What I see bad in your code is readability. It is good if you keep the proper spacing between each component.

If you have a long list, dict or tuple break it into lines.

list_of_predictions = [
"Undoubtedly",
"It seems to me - yes",
"It is not clear yet, try again",
"Do not even think",
"A foregone conclusion",
"Most likely",
"No doubt",
"Good prospects",
"Better not to tell",
"According to my information - no",
"You can be sure of this",
"Yes",
"Very doubtful"
]


or you can load the data from an external file if it is a very long one.

from random import choice
from time import sleep

list_of_predictions = [ ... ]

print('I am a magic ball, and I know the answer to any of your questions.')
sleep(1)

n = input("What is your name?\n")
print(f'Greetings, {n}.')
sleep(1)

def is_question(question):
return q[-1] == '?'

def is_valid_question(question):
return ('When' or 'How' or 'Where') not in q

again = 'yes'

while again == 'yes':
print('Enter a question, it must end with a question mark.')
q = input()

if is_question(q) and is_valid_question(q):
sleep(2)
print(choice(list_of_predictions))
elif not is_question(q):
print('You  entered not a question!')
continue
else:
print('You have entered a question that cannot be answered "yes" or "no"!')
continue

sleep(1)
again = input('Are there any other questions?\n')
sleep(1)

a = input('Come back if they arise!\n')
`

I think you can see the difference. Btw those are personal preferences it will be better if you read the Zen of python and PEP8

• Thanks for help! I will take into account your advices. Dec 1 '21 at 8:40
• Although The Zen of Python is written in a slightly humorous style, I will take it more seriously. Dec 1 '21 at 8:43
• More than the Zen of Python, I recommend reading PEP8, which explicitly give guidelines for the use of blank lines, among many other things. Dec 1 '21 at 11:21