So, a while ago I started a project for educational and entertainment purposes on creating a Python based interaction story.
I wrote the code snippet below but I'm confused about something. (Bear in mind I'm new to Python so if it's something simple don't be rude please) In the snippet below I can call the function before I write it. For example, the first one I call upon is "path_1_1()" which then prints the path_1_1() function below. I'm confused as to why I can't do this in other Python programs and scripts? Because it gives me a NameError and says the variable is not defined.
def meadow():
print("You are in a tall grassy meadow\n\
there is lots of grass all around you\n\
however there are 2 paths\n\
the Train Rail Path\n\
or the Forest Path..")
meadow_choice = input("Which path will you choose? : ")
if meadow_choice =="Train Rail" or meadow_choice == "Train Rail Path" or meadow_choice == "train rail" or meadow_choice == "train rail path":
print("You've chosen the Train Rail Path")
path_1_1()
elif meadow_choice =="Forest Path" or meadow_choice == "forest path" or meadow_choice == "Forest" or meadow_choice == "forest":
print("You've chosen the Forest Path")
path_1_2()
else:
print("Woops, that's not a choice, try again.")
meadow()
# Train Rail Path 1.1
def path_1_1():
print("You walk up the train rail, cautious of\n\
oncoming trains. As you look around you spot a\n\
Small Note, and a Large hole..")
path_1_1_choices =input("Which would you like to investigate, the Small Note, or the Large Hole? : ")
if path_1_1_choices == "Small Note" or path_1_1_choices == "small note":
print("You've chosen to investigate the Small Note")
path_2_2_1()
elif path_1_1_choices == "Large Hole" or path_1_1_choices == "large hole":
print("You've chosen to investigate the Large Hole")
path_2_1_1()
else:
print("Woops, that's not a choice, try again.")
path_1_1()
Simply put, when you just define a function, you can call another function that comes later in the code.
However, when you call and execute the function, the called function should be pre defined.
def function_a():
function_b()
return 0
def function_b():
pass
This is valid.
function_a()
def function_a():
pass
This raises NameError, since function_a() was called before it was defined.
Python reads line one by one so when you call a fonction before writing it raises a NameError. However defining a fonction using def don't run any code, it only indicates to Python that the function exists and can be call further. So you can define all your functions first and, after that, call them without worrying about the order in which they are called.
Related
I'm taking the example from the book "Conceptual Programming with Python".
Some introduction to the problem that the code is aimed at solving:
Example: Creating a Knowledge Base
As a second example for OOP, we are going to implement a simple guessing game! But this game will be able to learn from experience, that is, you will be able to teach the program as you play. For this example, we will create a knowledge base of animals. The user will think of an animal, and the computer will have to figure out which animal it is by asking you (sensible) questions about that animal; the answer will be either yes or no. If it fails to guess correctly the animal, the program will ask you what would be a sensible question to be able to find the right solution next time!
Example 1: The computer only knows how to distinguish between a bird or a cat depending on whether it has 4 legs or not. That is, the initial knowledge base only contains these two animals and one single question.
You: Think of a cat.
Computer: Does it have 4 legs?
You: Yes
Computer: Were you thinking of a cat?
You: Yes
Computer: I knew it !! Let's keep playing! I am good at this!
Once again, this follows a tree structure! Depending on whether the user answers yes or no, the computer will ask a different question, or will provide an answer!
Example 2: You teach the computer a new question.
You: Think of a dog.
Computer: Does it have 4 legs?
You: Yes
Computer: Were you thinking of a cat?
You: No
Computer: What animal were you thinking of?
You: Dog
Computer: What is a question to distinguish between dog and cat?
You: Does it bark?
Computer: For a dog, what should be the answer?
You: Yes
Now for the pickle fragment:
Files need to be always opened first; then manipulate them (for example loading their content into a data structure), and finally close them when they are no longer in use. So, we are going to try to open a file called animal.kb in which we will save the tree. The first time we open the file, it will be empty, so we will create our previous knowledge base. To do so, we will use a try-except structure. Why? Whenever we try to open a file that doesn’t exist, this will create an exception FileNotFoundError. We can simply catch it and create the knowledge base ‘manually’. Then we let the user play, and we keep updating the knowledge base kb. At the end of the program, when the user doesn’t want to play anymore, we ‘dump’ the information contained in kb on the file “animal.kb”.
After the 2nd try with opening the "animal.kb" knowledge base the error appears:
```
Do you want to play? y
Traceback (most recent call last):
File "...\ConceptualPython02\knowledgeBase.py", line 71, in <module>
kb = kb.play()
AttributeError: 'NoneType' object has no attribute 'play'
Process finished with exit code 1
```
That's the problematic code:
import pickle
class Knowledge:
pass
class Question(Knowledge):
def __init__(self, text, if_yes, if_no):
self.text, self.if_yes, self.if_no = text, if_yes, if_no
def play(self):
if ask(self.text):
self.if_yes = self.if_yes.play()
else:
self.if_no = self.if_no.play()
return self
class Answer(Knowledge):
def __init__(self, text):
self.text = text
def play(self):
if ask("Were you thinking of a {} ? ".format(self.text)):
print("I knew it!")
return self
# here we got it right, # so we simply return the
# Answer node as it is.
else:
newanimal = input("What animal were\ "
"you thinking of? ")
newquestion = input("What is a question "
"to distinguish between {} and {} ?"
.format(self.text, newanimal))
# but in case we didn't know the animal
# we need to modify the node adding # the appropriate question and# what to do
# ifyes and if no
if ask("For {} , what should be the answer? ".format(newanimal)):
return Question(newquestion,
Answer(newanimal), self)
else:
return Question(newquestion,
self, Answer(newanimal))
def ask(q):
while True:
ans = input(q + " ")
if ans == "y":
return True
elif ans == "n":
return False
else:
print("Please answer y or n!")
try:
file = open("animal.kb", "rb")
kb = pickle.load(file)
file.close()
except FileNotFoundError:
kb = Question("Does it have 4 legs?", Question("Does it bark?",
Answer("dog"), Answer("cat")), Answer("bird"))
while True:
if not ask("Do you want to play?"):
break
kb = kb.play()
file = open("animal.kb", "wb")
pickle.dump(kb, file)
file.close()
Of course, also, it doesn't cache the new questions about the animals as it should have.
play should always return a Knowledge instance (or inherited from it). But when in Question.play the call to ask returns True, you don't return anything:
def play(self):
if ask(self.text):
self.if_yes = self.if_yes.play()
else:
self.if_no = self.if_no.play()
return self
So change the indentation of return self so it is always executed.
I'm trying to write a text game with what I've learned so far, but I ran into a problem.
When I input 1, it's fine, but when I input 2, it gives me the following error:
Traceback (most recent call last):
File "ex36.py", line 39, in <module>
start()
File "ex36.py", line 27, in start
if choice == 1 or "closer" in choice:
TypeError: argument of type 'int' is not iterable
I realize that the function idf(): is totally unnecessary as I can just treat the input 2 as a string without converting it into an integer. This fixes the program. However, I would like to know why this error occurs when I try to change the input into a string, and why it only works for 1 and not 2.
I'm sorry for this stupid question; I'm very new to programming and trying to teach myself python. Thanks for the support, everyone.
Update 1:
I think I see the problem now, as pointed out by two answerers below. The problem happens when the program tries to check "closer", which is a string, in choice, which is an integer.
I would like the if statements to accept both integers and strings. How would I do this? Any advice to modify my program?
from sys import exit
print("""Welcome to my text game! You may input numbers (e.g. 1, 2, 3) to
navigate or type in your choice.""", "\n")
def bad_news(reason):
print(reason)
exit(0)
#IDs if str or int
def idf(check):
if check.isdigit() == True:
converted = int(check)
return converted
else:
return check
def start():
print("You're walking home down a dark road, in a dark and gloomy night.")
print("There's someone going the same way as you.")
print("'It's quite dark and dangerous,' you think. 'Better do something.'")
print("Will you: 1. Follow him/her closer? or 2. Keep distance?")
choice1 = input("> ")
choice = idf(choice1)
if choice == 1 or "closer" in choice:
bad_news("""The person gets spooked and you got cops called on you.
Now you have a restraining order. Great!""")
elif choice == 2 or "distance" in choice:
alley()
else:
print("The input is invalid; please try again.")
start()
def alley():
print("Now in alley.")
start()
Your idf converts any digit strings into numbers and then you try to check if a string is in the integer.
Your best option is to just always return strings from idf (or remove this function altogether) and check if choice == "1"
Your main problem is this line:
if choice == 1 or "closer" in choice:
If choice is not 1 you cannot check "closer" in choice without potential errors, because choice can still be an integer (e.g. the number 2) and an integer is not iterable.
It is best to keep the program simple and also to keep the return type from the input() function, i.e. the string. The string method is_digit() already exists and you can also apply it directly to the return value. In my opinion, there is no need to write another function that checks this. Then the function can also return two different data types. This complicates the code unnecessarily.
The string method is_digit() already does all the magic that is essential to your problem. It allows you to check if a user has entered a number or a text. You only need to explicitly convert the potential number to an integer (int(choice)) if you want to save the number as an integer.
Since you have several cases that you need to handle differently, you will also need to write some if-statements. So you could simply do something like this, for example:
if not choice.isdigit():
if "closer" in choice:
# do something
elif "whatever" in choice:
# do something
elif choice == "1":
# do something
elif choice == "2":
# do something
...
You can save one indentation by using the logical and-operator, but this is more a matter of taste, e.g.:
if not choice.isdigit() and "closer" in choice:
...
If you just want to react to certain return values, you can even do it without the is_digit() function, at all. But you mentioned in the question that you still want to distinguish between numbers and text (at least that's how I understood it). Because then you always get a string back and this string is always iterable. The error from above would simply not occur.
Most of it you have already figured out yourself, but the answer should also have added value for other readers. Therefore I have revised the answer again and hopefully clarified the problem better.
Over the past few months I've been attempting to create a text-based Zork-style game as a project to teach myself Python.
Thanks to the wonderful people here at stackoverflow and plenty of youtube video's, I've made decent progress. The issue I'm currently dealing with is towards the bottom of the code, in the Bag class.
As you will see below in my bag class, I have a method called Take. This method originally was not within the bag class, it was in the section at the bottom that reads user commands.
class Bag:
def __init__(self, inventory):
self.inventory = inventory
def addToInventory(self, item):
for key in list(Location.room.roominv.keys()):
self.inventory.append(Location.room.roominv[key])
del Location.room.roominv[key]
def SearchRoom(self):
if Location.room.roominv:
for item in list(Location.room.roominv.keys()):
print("you find a", item)
else:
print("You don't find anything")
def NoneHere(self, command):
print("You can't find a", command)
def Take(self, command):
for key in list(Location.room.roominv.keys()):
if Location.room.roominv[key].name == command.split()[1]:
bag.addToInventory(key)
print('you take the', key)
def CheckTake(self):
if Location.room.roominv and command.split()[1] in Location.room.roominv:
self.Take(command)
else:
self.NoneHere(command.split()[1])
def CheckInv(self):
for item in list(bag.inventory):
print("Your bag contains:", item.name)
player = Player("Jeff", 100)
bag = Bag([])
Location = Location('introd')
command = ' '
while command != "":
command = input('>>> ')
if command in Location.room.exits:
Location.travel(command)
elif command == 'look':
Location.RoomDesc()
elif command == '':
print('You have to say what it is you want to do!')
command = '#'
elif command == 'search':
bag.SearchRoom()
elif command.split()[0] == 'Take':
bag.CheckTake()
elif command == 'Inventory':
bag.CheckInv()
else:
print('Invalid command')
I was advised to separate the logic of user commands from the rest of the game so I moved it to a specified class.
Before I did this however, the game would have no problem picking up only specific items from a rooms inventory. Now it picks up all.
(Code defining each room will be posted at bottom, I've been importing it from a separate .py file)
Currently, only one of my rooms contains more than one Item. The "inside cottage" room, which contains "Ornate_Key" and "Knife".
Here's the strange thing. If I try to take the Ornate_Key, it picks it up fine (Also picks up the Knife though).
However if I try to take the Knife, I receive an error with this traceback
Traceback (most recent call last):
File "C:/Users/Daniel/Python 3.6/Scripts/PythonPR/Flubbo'sModuleTest.py", line 156, in <module>
bag.CheckTake()
File "C:/Users/Daniel/Python 3.6/Scripts/PythonPR/Flubbo'sModuleTest.py", line 130, in CheckTake
self.Take(command)
File "C:/Users/Daniel/Python 3.6/Scripts/PythonPR/Flubbo'sModuleTest.py", line 122, in Take
if Location.room.roominv[key].name == command.split()[1]:
KeyError: 'Ornate_Key'
I've spent about 6 hours tinkering with this code, going back to older versions and comparing this current one to ones where I wasn't hitting this issue, and I can't figure out why this suddenly started happening.
I am very new to coding in general, so I'm very fuzzy on architecture/the fundamentals of things. Does anyone have any idea what is causing this issue?
At the very bottom of this page I will post a section of code from an older version that is not experiencing this problem.
Considering this post is already very long, I might as well post an example game to demonstrate exactly what is happening.
>>> look
You are in a forest, you can hear wildlife all around you. There seems to be a clearing in the distance.
{'Search the ground', 'Go North'}
>>> search
you find a Sword
>>> Take Sword
you take the Sword
>>> n
moving to clearing
You are in a clearing surrounded by forest. Sunlight is streaming in, illuminating a bright white flower in the center of the clearing. To the South is the way you entered the forest. A well worn path goes to the East. In the distance a harp can be heard.
{'Go East', 'Take flower', 'Go south'}
>>> e
moving to forest path
You begin walking down a well beaten path. The sounds of the forest surround you. Ahead you can see a fork in the road branching to the South and East.You can smell smoke coming from the South, and can hear a stream to the East
{'Go East', 'Go West', 'Go South'}
>>> e
moving to stream
You come upon a relaxing stream at the edge of the woods. It looks like there is something shiny in the water. To your South is a rickety looking shack, to your West is the forest path you came down
{'Go West', 'Go South'}
>>> Take Rusty_Key
you take the Rusty_Key
>>> s
moving to shack
In front of you is a shack, possibly used as an outpost for hunting. It looks dilapidated.
{'Go North', 'Go South'}
>>> s
moving to inside shack
The inside of the shack is dirty. Bits of ragged fur are scattered about the floor and on a table against the back wall.A sharp looking knife is on the table. There is an ornate key hanging on the wall by a string.
{'Take Key', 'Take Knife', 'Go North'}
>>> search
you find a Knife
you find a Ornate_Key
>>> Take Ornate_Key
you take the Ornate_Key
>>> Inventory
Your bag contains: Sword
Your bag contains: Rusty_Key
Your bag contains: Knife
Your bag contains: Ornate_Key
>>>
Any particular reason why you have a Take method in Bag? It seems totally redundant, considering all you want to do is add that item if it exists in a dictionary. Try this:
def CheckTake(self):
key = command.split()[1]
if Location.room.roominv and key in Location.room.roominv:
bag.addToInventory(key)
print('you take the', key)
else:
self.NoneHere(key)
Also, your code is inconsistent. In some places, you access globals directly, while in other places, you redundantly pass it to a function. I would strongly advice you to take this to Code Review and get your act together once you get this working.
I am working on some code for my game and I am having an issue. I apologize in advance if this is hard to understand. The first section works fine. There is a large amount of code so I pasted it into codepad.org for easier sharing. Here's the link; http://codepad.org/kT8szBb2
Lines 108 and 142 are supposed to work together. I've tried different things like adding in this:
if True:
to try and reposition the indent level but for whatever reason it doesn't seem to work. Any suggestions work; I'm willing to try anything even if it means re-writing the entire segment. Thanks in advance.
Okay, I think I've found the issue. You don't quite understand how indentation works. You have code that looks like this:
if a:
if b:
if c:
A()
else:
B()
else:
C()
This isn't how Python works. Python works with the following structure:
if a:
A()
elif b:
B()
elif c:
C()
I'd really like to know where that mistake in understanding came from, because this is some extremely messy code.
I took the liberty of refactoring your code to be sane.
def weaponsel():
swep = None
#again, I'm not really sure where this is coming from or what you're doing with it
#so it's hard to say if you should be saving the contents of swep before you run
#the function, possibly to return if you select /return/ at the first prompt?
while swep is None:
print "What weapon would you like to use?"
if weapondict["s1"] == None:
print "Error #1: No weapons in the backpack. Contact me (Karatepig) at /hashed out for security/ and make me aware of this error."
print "I can return you to the beginning of this checkpoint or I can end the game. Type /return/ to return or /end/ to end."
er1=raw_input()
if er1.lower() == "end":
import sys
sys.exit()
elif er1.lower() == "return":
return None
else:
print "Sorry, I don't understand."
er1d()
for weapon in ['s1','s2','s3','s4','s5','s6','s7','s8']:
if weapondict[weapon]:
print("The weapon {} is available".format(weapondict[weapon]))
# as a side note, this can probably also be:
## for weapon in weapondict.values():
## print("The weapon {} is available".format(weapon))
# but that depends on what weapondict looks like!
# It should be easy to expand to "types" of weapons, as well
# using something e.g.
## weapondict = {"Rusty Sword":Sword(dmg=3), "Sharpened Spear":Spear(dmg=7)}
# and testing for type(Sword) or type(Spear) based on player class or etc.
# but you'd need to build classes for this to work, e.g.
## class Weapon(object):
## def __init__(self,dmg=1):
## self.dmg = dmg
##
## class Sword(Weapon):
## self.type = "Sword"
## self.dmgType = "Slashing"
##
## class Spear(Weapon):
## self.type = "Spear"
## self.dmgType = "Thrusting"
# then you can have slashing do more damage to lightly armored targets and
# thrusting do more damage to heavily armored targets and etc. Even writing
# methods to attack characters based on their equipped weapons. This is a
# PRIME example of where OOP will get you big results fast!
weapon=raw_input()
if weapon.lower() not in weapondict.values():
print "Sorry, I don't understand that.\n\n"
continue
print("You have selected the {}".format(weapon))
swepd1 = raw_input("Is that what you want? ")
if swepd1.lower() in ("y","yes"): swep = weapon
If you have any questions, don't hesitate to ask. I haven't actually tested this, so syntax errors may abound. I'm fairly certain it works as intended, however. As a side note -- where does weapondict come from? It's not in your code anywhere and it's likely that this function can't see it (unless you defined it earlier as global weapondict.)
There is really no need for all those if's. You should absolutely be using a for loop.
weapons = { 1:'sword', 2:'mace', 3:'bow'}
for wep in weapons:
print('the {} is available'.format(weapons[wep]))
outputs:
the sword is available
the mace is available
the bow is available
Been using easygui in python to make gui boxes for various different system applications for windows over the past few days. What I want to know is, why doesn't my code work? It looks the same as the code that was working before? The program is a game launcher, and I shall be making one identical to this but working (with different, LAN compatible games) for people to select the game from the list. Looked on stack overflow and found somebody with a similar issue, however I don't believe his resolution would apply to my script. Will post code below,
thank you,
Matthew
import easygui as eg
import os
def menu():
msg="Please select a game:"
title="Select Game!"
choices=["Batman Arkham City", "Tomb Raider", "TESV Skyrim", "State of decay - Early Access", "Critical Strike Portable", "Terraria", "Doom", "Halo","Minecraft"]
reply=eg.buttonbox(msg,title,choices)
if choices=="Batman Arkham City":
batman()
elif choices=="Tomb Raider":
tomb()
elif choices=="TESV Skyrim":
skyrim()
elif choices=="State of decay - Early Access":
state()
elif choices=="Critical Strike Portable":
crs()
elif choices=="Terraria":
terr()
elif choices=="Doom":
doom()
elif choices=="Halo":
halo()
elif choices=="Minecraft":
minecraft()
def batman():
os.system('"C:\Program Files (x86)\Black_Box\Batman Arkham City\Binaries\Win32\BatmanAC.exe"')
menu()
def tomb():
os.system('"C:\Program Files (x86)\Tomb Raider\TombRaider.exe"')
menu()
def skyrim():
os.system('"C:\Program Files (x86)\The Elder Scrolls V Skyrim\SkyrimLauncher.exe"')
menu()
def state():
os.system('"C:\Games\State of Decay Early Access\StateOfDecay.exe"')
menu()
def crs():
os.system('"C:\Program Files (x86)\CS Portable\a.exe"')
menu()
def terr():
os.system('"C:\Games\Terraria\Terraria.exe"')
menu()
def doom():
os.system('"C:\DOOMWADS\doom.wad"')
menu()
def halo():
os.system('"C:\Program Files (x86)\Halo Combat Evolved\Halo.exe"')
menu()
def minecraft():
os.system('"C:\Games\minecraft.exe"')
menu()
menu()
Stack overflow has messed up my indents! Please assume these are correct as they are.
It's the backslashes that are causing the problem. Python interprets these itself, e.g. \n is a new line character.
Change them either to forward slashes, or to double backslashes.
os.system('"C:/DOOMWADS/doom.wad"')
I have fixed it.... Sorry for wasting anybodies time. The backslashes are fine because they are in '""', however in the menu section is "if choices=="game"" whereas it should be, "if reply=="game"". Thanks for the help! :)