I'm new to python, and I'm building a game to teach myself python. This game will have a number of lessons, with questions and answers; the user will gain and lose points depending on the validity of their answers.
I'm using dictionaries to store the questions and answers that will be asked in each lesson.
I want to display and check the keys and values of the dictionary only at specific points (e.g. after the user enters a command). To do this, I imagined that I could create functions containing the dictionaries and then pass them to a main function when needed.
But when I run the code below, I get the following error: AttributeError: 'function' object has no attribute 'iteritems'
So I have two questions:
I tried removing the dictionary from within a function, and it works
just fine then. Is there any way (or reason) to make it work within
a function?
Is it possible to use just one dictionary and check the keys and values of a section of it at certain points?
Here's my code so far. Any advice whatsoever would be much appreciated!
points = 10 # user begins game with 10 pts
def point_system():
global points
#help user track points
if 5 >= points:
print "Careful. You have %d points left." % points
elif points == 0:
dead("You've lost all your points. Please start over.")
else:
print "Good job. Spend your points wisely."
def lesson1():
#create a dictionary
mydict = {
"q1":"a1",
"q2":"a2"
}
return mydict
def main(lesson):
global points
#get key:value pair from dictionary
for k, v in lesson.iteritems():
lesson.get(k,v) # Is the .get step necessary? It works perfectly well without it.
print k
user_answer = raw_input("What's your answer?: ")
#test if user_answer == value in dictionary, and award points accordingly
if user_answer == v:
user_answer = True
points += 1 #increase points by 1
print "Congrats, you gained a point! You now have %d points" % points
point_system()
elif user_answer != v:
points -= 1 #decrease points by 1
print "Oops, you lost a point. You now have %d points" % points
point_system()
else:
print "Something went wrong."
point_system()
main(lesson1)
and the code that works:
points = 10 # user begins game with 10 pts
#create a dictionary
lesson1 = {
"q1":"a1",
"q2":"a2"
}
def point_system():
global points
#help user track points
if 5 >= points:
print "Careful. You have %d points left." % points
elif points == 0:
dead("You've lost all your points. Please start over.")
else:
print "Good job. Spend your points wisely."
def main(lesson):
global points
#get key:value pair from dictionary
for k, v in lesson.iteritems():
lesson.get(k,v) # Is the .get step necessary? It works perfectly well without it.
print k
user_answer = raw_input("What's your answer?: ")
#test if user_answer == value in dictionary, and award points accordingly
if user_answer == v:
user_answer = True
points += 1 #increase points by 1
print "Congrats, you gained a point! You now have %d points" % points
point_system()
elif user_answer != v:
points -= 1 #decrease points by 1
print "Oops, you lost a point. You now have %d points" % points
point_system()
else:
print "Something went wrong."
point_system()
main(lesson1)
You're calling main() with the lesson1 function and not the result of the lesson1 function (which is a directory).
You should write:
main(lesson1())
By the way, lesson1 must also return the created directory for this to work:
def lesson1():
#create a dictionary
mydict = {
"q1":"a1",
"q2":"a2"
}
return mydict
You passed a function that returns a dictionary so you should call the function first to get the dictionary. So you may modify your code so that main accepts dictionary (the code actually expects a dictionary):
main(lesson1())
If you really would like to pass a function then you should modify you main to execute function first to get the dictionary:
def main(lessonFunc):
global points
lesson = lessonFunc()
#get key:value pair from dictionary
for k, v in lesson.iteritems():
but the first option is probably better. You could also pack a lesson into an object.
Related
I'm new to python so don't be surprised it if is something really basic, but i've been trying to write this code that asks math questions and then saves the scores in order to show them again at the start of the loop, but it doesn't save the scores. what should i change?
this is the code
scores = []
names = []
while True:
f = open("highscore.txt", "r")
for line in f:
line = line.strip("\n")
line = line.split(" ")
names.append(line[0])
scores.append(int(line[1]))
print(f.read())
for pos in range(len(names)) :
print(pos + 1, names[pos], scores[pos])
f.close()
score = 0
print("hello, welcome to maths game")
print("\nQuestion 1: what is 2 x 2 x 2?")
answer = int(input("your answer >"))
if answer == 8:
print("correct")
score = score + 1
print("your score is ", score)
else:
print("incorrect")
print("the score is ", score)
print("\nQuestion 2: what is 34 x 2?")
answer = int(input("your answer >"))
if answer == 68:
print("correct")
score = score + 1
print("your score is", score)
else:
print("incorrect")
print("the score is", score)
name = input("what is your name?")
position = 0
for compare_score in scores :
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
f = open("highscore.txt", "w")
for pos in range (len(names)):
f.write(names[pos] + " " + scores[pos])
it doesn't give any kind of error message, just loops back and doesn't save the names, neither the scores
You have a for-loop on scores that adds a new item to the scores list at each iteration. The for-loop will never reach the end of the list because there is always 'one more'.
Alain T.'s answer has already stated the root cause that you experienced. The loop never stops which appears to you as "freezing" since there are no outputs or indicators that you (as a user/developer) see that the loop still runs. So actually nothing freezes here .. it just runs forever.
For that reason I wanted to add a short note how to drill down the problem your own the next times..
Keyword here is clearly: debugging.
Debugging means: "finding out what your code does while being executed"
A very simple but (at least for small programs) quite effective approach is using one or more print() statements. These can be used to display the value of variables, the property of an object or just some statement like print("I am before the loop") to know where execution runs/stops..
A possible would be: (look at the print statements)
while True:
print("in while") #<-- this one
...
print("before loop") #<-- this one
for compare_score in scores :
print("in loop") #<-- this one repeats....
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
print("After loop") #<-- never see this one
f = open("highscore.txt", "w")
for pos in range (len(names)):
f.write(names[pos] + " " + scores[pos])
Running your program again should print out:
in while
before loop
in loop
in loop
in loop
in loop
in loop
in loop
in loop
...
and so on... So what you know now is:
Everything before the loop at least executed
The loop runs forever .
So now it would be time to dig a little deeper inside the loop.
Most interesting would be to examine the variable on which the loop exit
depends.
In your case that is the length of the scores list:
for compare_score in scores:
So the loop runs until there are no more scores to compare left in the scores list.
So it might be a good idea to print() the length of the list to check if and how it decreases until there are no more scores to compare.
So add something like this:
Check the two print() statements containing len(scores)
for compare_score in scores:
print("in loop")
if score < compare_score:
position = position + 1
scores.insert(position, score)
names.insert(position, name)
scores = scores[:5]
names = names[:5]
print(len(scores)) #<--- this one
# or a bit nicer as f-string:
print(f"len score: {len(scores)}") #<--- this one
print("After loop")
Both are displaying the length of the scores list.
The former one just does it a little nicer.
There is a lot more to debugging. Many tools like VSCode, Pycharm, etc. support a lot more sophisticated methodologies to step through code, set breakpoints, inspect objects and variables ..
But for small ans simple projects and when the focus is on learning, instant feedback and repeating. At least to my mind. Print() debugging gives you a lot of insight in a very easy and simple manner.
Oh, and if you read until here:
"Welcome to the community" Just jokin', welcome !! ;)"
I have already tried many times to fix this problem, but I can not solve it in any way. I use an index as a variable to move the user to the next question, but after correct and incorrect answers, the index remains at 0.
score = 0
index = 0
num = 0
user_answer = ""
correct = ""
#------------------------------------------------------------------------------
question_list = ["What is this shape? \na. Square \nb. Triangle \nc. Oval \nAnswer: ", "What color is this shape? \na. Blue \nb. Red \nc. Orange \nAnswer: "]
answer_list = ["a", "c"]
def AnswerCheck (num):
global user_answer, correct, index, score
while (correct == "false"):
user_answer = input(question_list[num])
if (user_answer == answer_list[num]):
score += 1
print("Correct!")
print("Score:", score)
print("\n")
else:
print("Incorrect!")
print("Score:", score)
print("\n")
index = index + 1
while index < len(question_list):
correct = "false"
AnswerCheck(index)
So the reason why the index isn't moving is that you are not letting it execute. first things first, you need to define the variables outside of the functions then make a function called main. main will be the driver function that you will be using to execute the rest of the code. the next step is to place the index += 1 outside of the while loop, then and a return call at the end of the function to push the results back to main. finally, you need to stop the while loop that is asking the question, to do this add break. this will stop the while loop from executing if the correct answer is entered.
question_list = ["What is this shape? \na. Square \nb. Triangle \nc. Oval \nAnswer: ", "What color is this shape? \na. Blue \nb. Red \nc. Orange \nAnswer: "]
answer_list = ["a", "c"]
user_answer = ""
correct = "false"
index = 0
score = 0
def AnswerCheck (num):
global index, score, user_answer, correct
while (correct == "false"):
user_answer = input(question_list[num])
if (user_answer == answer_list[num]):
score += 1
print("Correct!")
print("Score:", score)
print("\n")
break
else:
print("Incorrect!")
print("Score:", score)
print("\n")
index = index + 1
return
def main():
while index < len(question_list):
correct = "false"
AnswerCheck(index)
main()
This code ran fine on my side let me know if you have any questions.
You have this condition while (correct == "false"), correct variable has the value "false" from the start, and you don't change correct value anywhere in the body of your loop. Hence you will never exit the loop.
And about index remaining always zero. It's not true. You can print the value of index before user_answer = input(question_list[num]) and you will see that it has changed.
The problem is that you are passing the index as an argument here AnswerCheck(index). Because int is a primitive type in Python the value of the index variable will be copied and stored in the num variable inside your AnswerCheck function.
Because of that, num in AnswerCheck and index from the outside scope are not connected in any way. That means changing index will not change the value of num and vice versa.
And you will never exit AnswerCheck as I mentioned in the beginning, so your num variable will never change, and you stuck with the num being 0.
There are a lot of ways to solve it. The most simple ones are changing your while condition or updating the num variable, depending on the desired results.
Also, consider avoiding using global it's not the best practice and can lead to some difficulties debugging. You can read more reasoning about it.
I'm working on a simple text-based trivia game as my first python project, and my program won't terminate once the score limit is reached.
def game(quest_list):
points = 0
score_limit = 20
x, y = info()
time.sleep(2)
if y >= 18 and y < 100:
time.sleep(1)
while points < score_limit:
random.choice(quest_list)(points)
time.sleep(2)
print("Current score:", points, "points")
print("You beat the game!")
quit()
...
It looks like the points variable is not increased. Something like this might work in your inner loop:
while points < score_limit:
points = random.choice(quest_list)(points)
time.sleep(2)
print("Current score:", points, "points")
I'm assuming that quest_list is a list of functions, and you're passing the points value as an argument? To make this example work, you'll also want to return the points from the function returned by the quest_list that's called. A perhaps cleaner way to build this would be to return only the points generated by the quest. Then you could do something like:
quest = random.choice(quest_list)
points += quest()
Unless points is a mutable data structure, it won't change the value. You can read more about that in this StackOverflow question.
I am having trouble with assigning values to vars and then accessing the values. For example:
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = raw_input()
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = 100Mb # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is " + tSize + "."
Traceback returns:
Traceback (most recent call last):
File "./ranSongList.py", line 87, in <module>
print "\nYou want to create a random song list that is " + tSize + "."
NameError: name 'tSize' is not defined
I have read up on python variables and they do not need to be declared so I am thinking they can be created and used on the fly, no? If so I am not quite sure what the traceback is trying to tell me.
By the way, it appears as though python does not offer 'case' capabilities, so if anyone has any suggestions how to better offer users lists from which to choose options and assign var values I would appreciate reading them. Eventually when time allows I will learn Tkinter and port to GUI.
Your if statements are checking for int values. raw_input returns a string. Change the following line:
tSizeAns = raw_input()
to
tSizeAns = int(raw_input())
This should do it:
#!/usr/local/cpython-2.7/bin/python
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = int(raw_input())
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is {}.".format(tSize)
BTW, in case you're open to moving to Python 3.x, the differences are slight:
#!/usr/local/cpython-3.3/bin/python
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print("\nHow much space should the random song list occupy?\n")
print("1. 100Mb")
print("2. 250Mb\n")
tSizeAns = int(input())
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print("\nYou want to create a random song list that is {}.".format(tSize))
HTH
In addition to the missing quotes around 100Mb in the last else, you also want to quote the constants in your if-statements if tSizeAns == "1":, because raw_input returns a string, which in comparison with an integer will always return false.
However the missing quotes are not the reason for the particular error message, because it would result in an syntax error before execution. Please check your posted code. I cannot reproduce the error message.
Also if ... elif ... else in the way you use it is basically equivalent to a case or switch in other languages and is neither less readable nor much longer. It is fine to use here. One other way that might be a good idea to use if you just want to assign a value based on another value is a dictionary lookup:
tSize = {"1": "100Mb", "2": "200Mb"}[tSizeAns]
This however does only work as long as tSizeAns is guaranteed to be in the range of tSize. Otherwise you would have to either catch the KeyError exception or use a defaultdict:
lookup = {"1": "100Mb", "2": "200Mb"}
try:
tSize = lookup[tSizeAns]
except KeyError:
tSize = "100Mb"
or
from collections import defaultdict
[...]
lookup = defaultdict(lambda: "100Mb", {"1": "100Mb", "2": "200Mb"})
tSize = lookup[tSizeAns]
In your case I think these methods are not justified for two values. However you could use the dictionary to construct the initial output at the same time.
Initialize tSize to
tSize = ""
before your if block to be safe. Also in your else case, put tSize in quotes so it is a string not an int. Also also you are comparing strings to ints.
I would approach it like this:
sizes = [100, 250]
print "How much space should the random song list occupy?"
print '\n'.join("{0}. {1}Mb".format(n, s)
for n, s in enumerate(sizes, 1)) # present choices
choice = int(raw_input("Enter choice:")) # throws error if not int
size = sizes[0] # safe starting choice
if choice in range(2, len(sizes) + 1):
size = sizes[choice - 1] # note index offset from choice
print "You want to create a random song list that is {0}Mb.".format(size)
You could also loop until you get an acceptable answer and cover yourself in case of error:
choice = 0
while choice not in range(1, len(sizes) + 1): # loop
try: # guard against error
choice = int(raw_input(...))
except ValueError: # couldn't make an int
print "Please enter a number"
choice = 0
size = sizes[choice - 1] # now definitely valid
You forgot a few quotations:
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = raw_input()
if tSizeAns == "1":
tSize = "100Mb"
elif tSizeAns == "2":
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is " + tSize + "."
I have been set some homework to make a word guessing game, I have got it to work for the most part but at this point I am using random.choice and that command allows the same string to repeat more than once. I need to know how to use random.randint in this instance.
""" This is a guessing game which allows the person operating the program to
guess what i want for christmas"""
import random
sw = ("Trainers")
print ("In this game you will have 10 chances to guess what I want for christmas, you start with 10 points, each time you guess incorrectly you will be deducted one point. Each time you guess incorrectly you will be given another clue.")
clue_list = ["They are an item of clothing", "The item comes in pairs", "The Item is worn whilst playing sport", "The item is an inanimate object", "The item can be made of leather","They come in differant sizes", "They have laces", "can be all differant colours", "the item has soles", "Im getting it for christmas ;)"]
def guessing_game(sw, clue_list):
x = 10
while x<=10 and x > 0:
answer = input("What do I want for christmas?")
if sw.lower() == answer and answer.isalpha():
print ("Good Guess thats what I want for christmas")
print ("You scored %s points" % (x))
break
else:
print ("incorrect, " + random.choice(clue_list))
x -=1
if x == 0:
print ("You lost, try again")
guessing_game(sw, clue_list)
for your implementation I think you want:
clueint = random.randint(0, len(clue_list))
print("incorrect, " + clue_list[clueint])
to ensure the same clues are not displayed twice declare an empty list to store guess in OUTSIDE the function definition:
clues_shown = []
and then add each int to the list:
def showClue():
clueint = random.randint(0, len(clue_list))
if clueint in clues_shown:
showClue()
else:
clues_shown.append(clueint)
print("incorrect, " + clue_list[clueint])