I cannot figure out why I am getting AttributeError - python

Dear StackOverflow Angels,
I am really stuck with this problem. I am coding a game and am receiving this error message:
File "VillageGame.py", line 120, in <module>
launch.play()
File "VillageGame.py", line 84, in play
returned_scene = future_scene.enter()
AttributeError: 'NoneType' object has no attribute 'enter'
Here is the code referenced by the error message:
class Room(object):
def __init__(self):
pass
def enter(self):
pass
class Road(Room):
def __init__(self):
pass
def enter(self):
pass
def walk(self):
print "It is afternoon and you stand by the road of Red Village. It is a small, run-down place"
print "with a few shops and houses. A few residents walk about. It is a quiet place. You need"
print "to steal gold to feed yourself and pay your rent. This evening you need to steal 800 gold"
print "to pay your rent and bills. There are houses and businesses in the area. But plan carefully,"
print "the police here are armed and the locals are suspicious of odd outsiders like yourself. But"
print "you can fight, which should help. In front of you is a food shop, a weapons shop,"
print "a medicine shop, a bank and an inn. Where would you like to go first?"
return 'road'
def run(self):
pass
class Engine(object):
def __init__(self, game_map):
self.game_map = game_map
def play(self):
future_scene = self.game_map.launch_scene()
last_scene = self.game_map.next_scene('finished')
while future_scene != last_scene:
returned_scene = future_scene.enter()
future_scene = self.game_map.next_scene(returned_scene)
# make sure you run the last scene
future_scene.enter()
class Map(object):
rooms = {
'road' : Road(),
'food_shop' : FoodShop(),
'weapons_shop' : WeaponsShop(),
'medicine_shop' : MedicineShop(),
'bank' : Bank(),
'inn' : Inn(),
'death' : Death(),
'finished' : Finished()
}
def __init__(self, start_scene):
self.start_scene = start_scene
def next_scene(self, returned_scene):
val = Map.rooms.get(returned_scene)
return val
def launch_scene(self):
return self.next_scene(self.start_scene)
firstscene = Map('road')
launch = Engine(firstscene)
launch.play()
Apologies if this seems like a code dump, but I don't know what parts are relevant to the error message and what parts are not linked to it - because I am quite new to this you see.
If anyone has any insight into what code I could cut out of this message, I Would appreciate that too. It would help me to keep future questions shorter.

The problem is Map('road') returns None. I think what you're trying to do is this.
map = Map('road')
firstscene = map.launch_scene()
launch = Engine(firstscene)
launch.play()
But even that is not quite right. The line below doesn't instantiate a map.
val = Map.rooms.get(returned_scene)
If you want to call a function of the Map class from within that class, use the self keyword such as self.rooms.get(returned_scene).

This line returned_scene = future_scene.enter() expects a return value but you haven't implemented anything in the methoddef enter(self) (you're just pass-ing it, and so it returns a None object.

Related

Can you call an instance of a class while creating the instance of a class?

I am trying to code a text adventure. I created a class for objects within the rooms. In the following code,
self.door = Object("Door", "There is an {} door in the north.".format("closed" if self.door.openstate == False else "open"), True, False, door_text, True, False)
I wish to immediately detect whether the door is open or closed, and change the description accordingly. I know that the above code is definitely wrong, but is there a way to do so?
I’m not a 100% sure if this will answer your question but I think you need to create a separate class for your door. I’m assuming you have some class for the room like this:
class Room:
def __init__(self):
self.door = your_code_here
What you may need to do is to create a door class like:
class Door:
def __init__(self, door_state):
self.door_state = door_state
#property
def door_text(self):
door_state = 'open' if not self.door_state else 'closed'
return f"There is an {door_state} in the north"
And then your Room class will look like the following
class Room:
def __init__(self):
self.door = Door(False)
Finally if you run something like
r = Room()
print(r.door.door_text)
You should see the correct outputted value.

Passing a class to another class (Python)

I'm having some trouble with classes at the minute, and I not sure of how to solve my problem. I've read the docs and I can't connect anything said there with the problem I'm having.
I'm trying to make some simple classes for a game. I have a Weapon class and a Person class. I'm trying to pass a Weapon to the Person class (I hope this makes sense), so that the Person (Bob) can use the weapon. I'm having trouble accessing the methods and attributes in the Weapon class. I've considered making Person a child class of Weapon so that it can call the method easily, but that doesn't seem intuitive to me . . .
class Weapon:
def __init__(self, weapon_name, weapon_damage):
self.weapon_name = weapon_name
self.weapon_damage = weapon_damage
def display_weapon_name(self):
print('Weapon Name: %s' %self.weapon_name)
class Person:
def __init__(self, person_name, health, ranged_weapon):
self.person_name = person_name
self.health = health
Weapon.ranged_weapon = ranged_weapon
def display_person_info(self):
print('Name: %s' %self.person_name)
print('Ranged Weapon :%s' %Weapon.display_weapon_name)
def ranged_attack(self, ranged_weapon, target):
target.health -=ranged_weapon.weapon_damage
print("Weapon: %s" %ranged_weapon.weapon_name)
print(target.person_name + "'s Health: "+str(target.health))
pistol = Weapon("Pistol", 40)
bob = Person("Bob", 100, pistol)
bob.display_person_info()
Running this gives me:
Name: Bob
Ranged Weapon :<function Weapon.display_weapon_name at 0x02E23030>
Running:
bob.ranged_attack(pistol, bob)
Gives:
Weapon: Pistol
Bob's Health: 60
My questions are, am I passing the Weapon object correctly to the Person class? It seems weird writing Weapon.ranged_weapon in _init__ rather than self.ranged_weapon.
How can I get the display_weapon_info to show the string 'Weapon Name: Pistol', rather than the reference? It seems to work when I call it in ranged_attack, but not in the display info.
Really appreciate any help I can get with this. Apologies if a similar question has been asked before, but I couldn't find anything I could relate to my issue.
Rich
Person doesn't actually need to reference the Weapon class directly; it just needs to save a reference to whatever is passed as the ranged_weapon argument and know what it can do with that object. The code implicitly assumes that ranged_weapon is an instance of Weapon, but will work with any object that is suitably similar to an instant of Weapon.
class Person:
def __init__(self, person_name, health, ranged_weapon):
self.person_name = person_name
self.health = health
self.weapon = ranged_weapon
def display_person_info(self):
print('Name: %s' %self.person_name)
# display_weapon_name already calls print; but
# you probably don't need this method at all.
self.weapon.display_weapon_name()
# Instead, do this (actually, you already do this
# in ranged_attack())
# print('Weapon: %s' % self.weapon.weapon_name)
def ranged_attack(self, ranged_weapon, target):
target.health -= self.weapon.weapon_damage
print("Weapon: %s" % self.weapon.weapon_name)
print(target.person_name + "'s Health: "+str(target.health))
def display_person_info(self):
print('Name: %s' %self.person_name)
print('Ranged Weapon :%s' %Weapon.display_weapon_name)
Looking at this function, the compiler sees the following:
Line 1: A function named display_person_info with the parameter self.
Line 2: Print "Name: " and then print the name of self
Line 3: Print "Ranged Weapon: " and then Weapon.display_weapon_name.
In line 3, the compiler, rather than printing the weapon name, it is printing the function display_weapon_name itself! What you need to do instead is replace Line 3 with this:
print('Ranged Weapon :%s' %self.weapon.display_weapon_name())
That way, the value returned by the function is printed, rather than the function's pointer.

Class keeps repeating itself, despite having a 'return' statement for the next class

This is my first attempt at learning a programming language... Ok, so I'm working on exercise 45 of Learn Python the Hard Way, trying to create a text-based game. I've had a few roadblocks thus far, but this one is by far the most frustrating. I have borrowed (and tweaked) the author's code from a previous exercise, but that only allowed the use of one function, enter(), per class, where I want to use up to five. To do this, I tried to implement a try/exception tree that would run additional functions if they were there, and if not, move on to the next classs.
What happens is, my first class, IntroScreen(), runs fine, it points to Bedroom() fine, and then Bedroom repeats itself over and over again, even though I have a command to return to Kitchen(), the next class.
I have added a #comment# in the code below to show where I think it's messing up. If you know how to fix this, please help; or, even better, if you have a better way of doing this then that would be fantastic. I've been told that this looks kinda screwy.
I can provide my entire code if that helps; please, someone help me out!
class Map(Scene):
scenes = {
'introscreen': IntroScreen(),
'bedroom': Bedroom(),
'kitchen': Kitchen(),
'fourth': Fourth()
}
def __init__(self, start_scene):
self.start_scene = start_scene
def next_scene(self, scene_name):
return Map.scenes.get(scene_name)
def opening_scene(self):
# i think this is the part that's screwing up!!!
return self.next_scene(self.start_scene)
class Engine(object):
def __init__(self, scene_map):
self.scene_map = scene_map
def play(self):
current_scene = self.scene_map.opening_scene()
while True:
print "----------"
try:
next_scene_name = current_scene.enter()
except:
try:
next_scene_no2 = current_scene.second()
except:
try:
next_scene_no3 = current_scene.third()
except:
try:
next_scene_no4 = current_scene.fourth()
except:
try:
next_scene_no5 = current_scene.fifth()
except:
break
current_scene = self.scene_map.next_scene(next_scene_name)
In your Map class, are you wanting to create a new instance of Map?
def next_scene(self, scene_name):
return Map.scenes.get(scene_name) <----
def opening_scene(self):
# i think this is the part that's screwing up!!!
return self.next_scene(self.start_scene)
When debugging, use print statements as an easy way to see what is happening, e.g.
def next_scene(self, scene_name):
tmp_scene = Map.scenes.get(scene_name)
print('in Map:next_scene - tmp_scene = ', tmp_scene)
return tmp_scene

OOP how to get the script (a game) to run

I'm trying to get my head around OOP using Python. Working through a tutorial I've been instructed to make a game. Earlier in the tutorial I made a game just using functions but now I have to use classes and objects.
Once I have my objects though I'm unsure how the get things moving. Here's the gist of it:
class Scene(object):
def enter(self):
pass
class Scene1(Scene):
def enter(self):
print "Some text describing the scene bla bla"
def someFunction that requires raw_input and a correct answer
return 'Scene2'
class Scene2(Scene):
def enter(self):
print "Some text describing the scene bla bla"
def someFunction that requires raw_input and a correct answer
return 'Scene3'
class Scene3(Scene):
def enter(self):
print "Some text describing the scene bla bla"
def someFunction that requires raw_input and a correct answer
return 'finished'
There is more to the game than that, but I've just slimmed it right down to the part I'm having trouble with.
The tutorial author hints to use another object to run the game, an "engine" that would start with scene1 and run through each scene till 'finished' is returned. He does have an example but it was for a more complex game so I've tried to ignore it. I think he wants us to use a while loop though.
But while what? What logic could I use to run the game? I'm a learner as you may be able to tell. Do I have all of the objects that I need? What would be the best way to "run" the game?
Create a Class that holds all scenes. The run() method loops through all scenes and execute the enter() method, which will output both: the print and the return string.
class Engine:
def __init__(self):
self.scenes = [Scene1(),Scene2(),Scene3()]
def run(self):
for s in self.scenes:
print s.enter()
Engine().run()
Output:
Some text describing the scene bla bla
Scene2
Some text describing the scene bla bla
Scene3
Some text describing the scene bla bla
finished
Update to comment: do it more dynamically:
class Scene1(Scene):
def enter(self):
print "Some text describing the scene bla bla"
raw = raw_input("Guess the number")
if raw != "42":
return 'Scene1'
return 'Scene2'
class Engine:
def __init__(self):
self.scenes = {'Scene1':Scene1(),'Scene2':Scene2(),'Scene3':Scene3()}
def run(self):
next = 'Scene1'
while True:
if next == 'finished':
break;
next = self.scenes.get(next).enter()
Engine().run()
the arguments about this being a good canidate for oop not withstanding
class Scene1:
def enter(self):
print "a scene"
raw_input("Hit Enter To Go To Next Scene")
return "Scene2"
class Scene2:
def enter(self):
print "a different scene"
raw_input("Hit Enter To Go Back:")
return "Scene1"
class Engine:
def __init__(self,sceneDict,startKey=1):
self._scenes = sceneDict
self._current = self._scenes[startKey]
def run(self):
while True:
next_scene_key = self._current.enter()
self._current = self._scenes[next_scene_key]
Engine({"Scene1":Scene1(),"Scene2":Scene2()},"Scene1").run()
is one example of many ways to accomplish this
First of all, if you see a lot of repeated code in an OOP-project, you most probably are working against OOP and not with it.
Don't make a new class for each scene, but one object (of scene) for each.
This is a very simple example of how to implement your "engine" (last 4 lines):
#! /usr/bin/python3
class Scene:
def __init__(self, name, welcome, question, answer):
self.name = name
self.welcome = welcome
self.question = question
self.answer = answer
self.connected = []
def connect(self, otherScene):
self.connected.append(otherScene)
def enter(self):
print(self.welcome)
while (input(self.question + ' ') != self.answer): pass #raw_input in py2
if not self.connected: return
print('\nWhither goest thou?')
for i, scene in enumerate(self.connected):
print('{}: {}'.format(i, scene.name))
while True:
try:
i = int(input('? '))
return self.connected[i]
except ValueError:
print('I understand thee not.')
except IndexError:
print('I understand thee not.')
#creating the scenes
house = Scene('A house',
'Thou art standing in front of a house. The landlord talketh to thee.',
'What is the top speed of an uncharged swallow?',
'42')
bridge = Scene('A bridge',
'Thou seest a bridge guarded by a knight clothèd in black.',
'What is the capital of England?',
'London')
swamp = Scene('A swamp',
'Thou enterst a foul morast and a witch eyeballth thee.',
'What is the capital of Azerbaijan?',
'Baku')
castle = Scene('A castle',
'The castle! A fair maiden greeteth thee.',
'What is my name?',
'Peach')
#connecting the scenes
house.connect(bridge)
house.connect(swamp)
bridge.connect(castle)
swamp.connect(castle)
#the game engine
nextScene = house
while nextScene:
nextScene = nextScene.enter()
print('Fin')

How can I use a returned value to open up another instance of a class in Python?

I am on exercise 43 doing some self-directed work in Learn Python The Hard Way. And I have designed the framework of a game spread out over two python files. The point of the exercise is that each "room" in the game has a different class. I have tried a number of things, but I cannot figure out how to use the returned value from their initial choice to advance the user to the proper "room", which is contained within a class. Any hints or help would be greatly appreciated.
Apologies for the poor code, I'm just starting out in python, but at my wit's end on this.
Here is the ex43_engine.py code which I run to start the game.
from ex43_map import *
import ex43_map
import inspect
#Not sure if this part is neccessary, generated list of all the classes (rooms) I imported from ex43_map.py, as I thought they might be needed to form a "map"
class_list = []
for name, obj in inspect.getmembers(ex43_map):
if inspect.isclass(obj):
class_list.append(name)
class Engine(object):
def __init__(self, room):
self.room = room
def play(self):
# starts the process, this might need to go inside the loop below
next = self.room
start.transportation_choice()
while True:
print "\n-------------"
# I have tried numerous things here to make it work...nothing has
start = StartRoom()
car = CarRoom()
bus = BusRoom()
train = TrainRoom()
airplane = AirplaneRoom()
terminal = TerminalRoom()
a_game = Engine("transportation_choice")
a_game.play()
And here is the ex43_map.py code
from sys import exit
from random import randint
class StartRoom(object):
def __init__(self):
pass
def transportation_choice(self):
print "\nIt's 6 pm and you have just found out that you need to get to Chicago by tomorrow morning for a meeting"
print "How will you choose to get there?\n"
print "Choices: car, bus, train, airplane"
choice = raw_input("> ")
if choice == "car":
return 'CarRoom'
elif choice == "bus":
return 'BusRoom'
elif choice == "train":
return 'TrainRoom'
elif choice == "airplane":
return 'AirplaneRoom'
else:
print "Sorry but '%s' wasn't a choice." % choice
return 'StartRoom'
class CarRoom(object):
def __init__(self):
print "Welcome to the CarRoom"
class BusRoom(object):
def __init__(self):
print "Welcome to the BusRoom"
class TrainRoom(object):
def __init__(self):
print "Welcome to the TrainRoom"
class AirplaneRoom(object):
def __init__(self):
print "Welcome to the AirplaneRoom"
class TerminalRoom(object):
def __init__(self):
self.quips = [
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
]
def death(self):
print self.quips[randint(0, len(self.quips)-1)] # randomly selects one of the quips from 0 to # of items in the list and prints it
exit(1)
Instead of returning a string try returning an object, ie
if choice == "car":
return CarRoom()
It might be a good idea to make a Room class, and derive your other rooms from it.
The Room base class can then have a class variable which automatically keeps track of all instantiated rooms.
I haven't thoroughly tested the following, but hopefully it will give you some ideas:
# getters.py
try:
getStr = raw_input # Python 2.x
except NameError:
getStr = input # Python 3.x
getStr.type = str
def typeGetter(dataType):
def getter(msg):
while True:
try:
return dataType(getStr(msg))
except ValueError:
pass
getter.type = dataType
return getter
getInt = typeGetter(int)
getFloat = typeGetter(float)
getBool = typeGetter(bool)
def getOneOf(*args, **kwargs):
"""Get input until it matches an item in args, then return the item
#param *args: items to match against
#param getter: function, input-getter of desired type (defaults to getStr)
#param prompt: string, input prompt (defaults to '> ')
Type of items should match type of getter
"""
argSet = set(args)
getter = kwargs.get('getter', getStr)
prompt = kwargs.get('prompt', '> ')
print('[{0}]'.format(', '.join(args)))
while True:
res = getter(prompt)
if res in argset:
return res
.
# ex43_rooms.py
import textwrap
import random
import getters
class Room(object):
# list of instantiated rooms by name
ROOMS = {}
#classmethod
def getroom(cls, name):
"""Return room instance
If named room does not exist, throws KeyError
"""
return cls.ROOMS[name]
def __init__(self, name):
super(Room,self).__init__()
self.name = name
Room.ROOMS[name] = self
def run(self):
"""Enter the room - what happens?
Abstract base method (subclasses must override)
#retval Room instance to continue or None to quit
"""
raise NotImplementedError()
def __str__(self):
return self.name
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.name)
class StartRoom(Room):
def __init__(self, name):
super(StartRoom,self).__init__(name)
def run(self):
print textwrap.dedent("""
It's 6 pm and you have just found out that you need to get to Chicago
by tomorrow morning for a meeting! How will you get there?
""")
inp = getters.getOneOf('car','bus','train','airplane')
return Room.getroom(inp)
class CarRoom(Room):
def __init__(self,name):
super(CarRoom,self).__init__(name)
class BusRoom(Room):
def __init__(self,name):
super(BusRoom,self).__init__(name)
class TrainRoom(Room):
def __init__(self,name):
super(TrainRoom,self).__init__(name)
class PlaneRoom(Room):
def __init__(self,name):
super(PlaneRoom,self).__init__(name)
class TerminalRoom(Room):
def __init__(self,name):
super(TerminalRoom,self).__init__(name)
def run(self):
print(random.choice((
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
)))
return None
# create rooms (which registers them with Room)
StartRoom('start')
CarRoom('car')
BusRoom('bus')
TrainRoom('train')
PlaneRoom('airplane')
TerminalRoom('terminal')
.
# ex43.py
from ex43_rooms import Room
def main():
here = Room.getroom('start')
while here:
here = here.run()
if __name__=="__main__":
main()

Categories

Resources