local variable 'door_open' referenced before assignment - python

I am very new to python. I am trying to learn by making a text based game.
In the gameplay I am wanting a variable, door_open, to begin false but once unlocked door_open = True. The next time the function is called a different message would be displayed.
door_open =False
def house():
print "The door is locked"
plan = raw_input(">")
if plan.lower() == 'use key':
inventory.remove("House Key")
door_open = True
print "You are inside"
start() ## this just returns to the starting point outside
if door_open == True:
print "You walk inside"
exit(0) ## using this until I develop it more
else:
print "Door is locked. Do you want to use your key (Y/N)?"

When Python sees an assignment to a variable in a function, when the variable is not marked as global (or nonlocal), it treats the variable as a new local variable.
Thus Python is treating the original code similarly to
door_open = False
def house():
if plan.lower() == 'use key':
door_open_LOCAL = True # only assigned here
if door_open_LOCAL == True: # whoops, might not be assigned yet!
pass
(That is, the local variable shadows the global variable, I've emphasized the point above by giving the local variable a different name entirely.)
See also:
Why some Python variables stay global, while some require definition as global
Use of "global" keyword in Python

If you want to write to a variable in the global scope you need to use the global keyword:
door_open = False
def house():
global door_open
print "The door is locked"
# ...
Also, please read and follow PEP8, the Python style guide. For example. you shouldn't use if foo == True: but if foo:

You are trying to track status of your game and got a bit lost in where is the status noted:
This is typical situation for using a class:
game.py:
import sys
class Game():
def __init__(self):
self.door_open = False
self.inventory = ["House Key", "Snow Gun", "Swiss army knife"]
def unlock_house(self):
assert "House Key" in self.inventory, "`House Key` must be present in inventory"
self.inventory.remove("House Key")
print "Click, Click"
self.door_open = True
def fireup(self):
print "Bufff"
self.inventory = []
def main():
game = Game()
while True:
# report current status
if game.door_open:
print "You are in the house!"
print "You are the King"
sys.exit(0)
else:
print "The door is locked!"
# ask for further plans
print "What is your further plan?"
plan = raw_input(">")
# change the situation
if plan.lower() == "use key":
game.unlock_house()
elif plan.lower() == "set up file":
game.fireup()
if __name__ == "__main__":
main()
You can see the split between the class Game, which is able to track the status and even do some changes of it, and the main piece of code, where you try to use the game somehow.
The class Game is sort of idea, how your notes about game status could look like.
Then in game = Game() you instantiate the class, get an object, and start doing real changes to the status.
Having status information stored in well designed and quite well isolated object is very convenient, you know, where to find what you need.

Related

Menu for a turn based game

Our teacher has assigned us an assignment for doing a turned based game.
This only included name.strip() but this does not prompt player to input unique name:
def start():
print("\nNew game started...Setting up Player 1's team...\n")
for total_characters in range (1,4):
name = input('Enter a unique name for unit #'+str(total_characters)+'==> ')
if not name.strip():
print('Cant be blank name,please provide a unique name')
return start()
else:
role_selection()
def role_selection():
for total_characters in range (1):
role = input('Select unit #'+str(total_characters)+' type: (W)arrior, (T)anker, or Wi(Z)ard ==> ')
total_characters+=1
if role.upper() == 'W':
pass
elif role.upper() == 'T':
pass
elif role.upper() == 'Z':
pass
else:
print("I don't understand what are you typing.")
return role_selection()
There are things that doesn't make sense :
You have the exact same function twice :
def set_up(team_size)
def set_up(name)
You are doing :
for total_units in range(team_size):
[...]
invalid = True
[...]
while invalid: # Infinite loop
set_up() # What's this function ?
As you can see from the comments in the code above, you never set invalid to False, leading to the infinite loop.
Note: My recommendation is that you should check out some tutorial on python before moving on coding a complex project, because your homework is not that easy.
Edit :
From the new code you've posted, you could do something like this :
def new_game():
names = []
for unit_id in range(1,4):
print(f'Enter a unique name for unit #{unit_id} ->')
empty = True
while empty:
name = input('>')
if name == "":
continue
empty = False
if name in names:
print('Unit name must be unique.')
else:
print('Name looks good!')
names.append(name)
python menu
At first glance, this stood out to me:
if not name.strip():
print('Unit name could not be blank.')
invalid = True
Remember in python the indentation matters. You are setting invalid to True regardless of the if condition. Further down you have a while loop that checks it.
The other if conditions have invalid=True inside the condition. Plus you don't have invalid=False anywhere as far as I see, so you'll get an error if you don't declare it somewhere so it's always on the path before the while.
this doesn't seem like a specific problem, more an ask for general guidance for going about this kind of problem?
One thing to note is that your above script only uses functions (which store behaviour) whilst really for something like the turn based game, you need to store behaviour (attacks etc) and information (how much health is left etc).
I won't write the script for you, but here's an example of how you might define an rpg like entity, capable of attacking, being attacked by another entity etc:
class Entity:
"""Abstract class for any combat creature"""
def __init__(self, name, health):
self.name = name
self.health = health
self.is_alive = True
def attack(self, other):
dmg = random.randint(7, 12)
other.be_attacked(dmg)
def be_attacked(self, dmg):
self.health = self.health - dmg
if self.health <= 0:
self.die()
def die(self):
self.is_alive = False
def check_life(self):
return self.is_alive
You can then initialise a bunch of these to make up the 'team' you where talking about:
one_person = Entity('Lee', 34)
another_person = Entity('Someone Else', 21)
etc. Hope that helps a bit. Good luck with your project and have fun!

Understanding Classes and __Call__ Methods Py 2.7

Okay I have been running my forehead into some code and I have determined I am just taking shots in the dark at this point. For some reason getting classes to click has been a real pain in the butt and I'm thinking if I used my own personal code it might make sense. So here is a little script game I wrote up to experiment. (Its pretty rough I'm fairly new): https://github.com/Villagesmithy/Zombies-.git
I feel that to understand what I'm doing wrong I have to have it pointed out to me in my own code. There are plenty of questions similar to mine I'm sure but they were going over my head sadly.
The error I'm getting is this:
Attribute Error: Combat instance has no __Call__ method
And here is the class that I think is the problem:
class Combat:
def battle(self):
print start.player, " has: ", hp, "."
print "The zombie has: ", zombie.zom_health()
time.sleep(1)
if haf == True:
total_damage = hero_attacks * hero_damage
zombie.zom_health() - total_damage
print "You strike the zombie for %d damage!" %(total_damage)
print "The zombie's health is %d" %zombie.zom_health()
return zombie.zom_health()
time.sleep(3)
elif haf == False:
total_damage = zombie.zom_damage()- hero.hero_armor()
if total_damage > 0:
total_damage - hp
return hp
print "A zombie shambles through the baricade and damages you!"
print "You lost %d hp! Your hp is now: %d" %(total_damage, hp)
combat_loop()
time.sleep(3)
elif total_damage <= 0:
print "A zombie lurches at you but misses!"
time.sleep(3)
combat_loop()
else:
z.zom_killed()
def initv(battle):
bat = battle()
hero_init = random.randint(1,20)
zom_init = random.randint(1,20)
if hero_init >= zom_init:
#global haf Ignoring for now
haf = True
print "You attack first!"
bat.battle()
elif hero_init < zom_init:
#global haf
haf = False
print "The zombie attacks!"
bat.battle()
def start(): #The only fucking code that works
global zombies
zombies = random.randint(20,30)
arm = random.sample(armor,1)
wea = random.sample(weapon, 1)
player = raw_input("What is your name?")
print player, ",\n"
print "Your colony is under attack by %s zombies!" %(zombies)
print "Hold them off so the others can escape."
print "You are wearing an ", arm, "and weilding a", wea
time.sleep(3)
def combat_loop():
combat_call = Combat()
while zombies > 0:
combat_call.initv()
if zombies <= 0:
print "You held off the zombies off long enough to escape!"
print "With the way clear you grab your belongings and follow suit."
print "The end!"
sys.exit()
Now if you are saying Gee this kiddo has no clue what he is doing you would be correct! I'm just hoping you guys can assist me in making this click. The whole program might need to be burned at the stake I don't know. Any help you can give would be very helpful.
I'm assuming initv is actually a method of Combat, but you forgot to have it take self as a parameter, giving it a parameter named battle instead.
When you call bat.initv(), it's passing self as battle (self is a name of convention; the first positional parameter of a method is self whatever you decide to call it). So when you do bat = battle() in initv, it's the same thing as doing self(), that is, trying to treat an instance of your class as a callable.
From what I can tell, the real goal is to call the battle method, so the definition and first line of initv should be:
def initv(self):
bat = self.battle()
which passes self under the standard name, then calls the battle method on it. It's a little unclear what the battle method returns (it seems to return None implicitly on two code paths, and whatever zombie.zom_health() returns on the third code path, which has a sleep in it that never happens thanks to the return preempting it), but this code has a number of issues, and it's quite hard to identify what "correct" behavior would be.
For the record, the error almost certainly complained about the lack of a __call__ method, not __Call__; the special method that lets instances of a class act as callables themselves is all lower case.
In combat_loop you define combat_call as Combat(). Right now combat_call is an instance of Combat. Then in your while loop, you say combat_call.initv(). Since combat_call is an instance of Combat, that is a shortcut for Combat.initv(combat_call). That is, combat_call is the only argument given to initv(). In initv(), you take one argument: battle. In this case, battle is the same thing as combat_call which is an instance of Combat. You then say bat = battle(). battle is already an instance of Combat, so you are trying to call an instance. Some instances can be called, but to do that, they need to define a __call__ method. Your class did not, so there is an error. I think that instead of taking battle as an argument, take self. Then define bat as self.battle().
One more thing, I don't think the zombie.zom_health() - total_damage line will work unless you implement the __sub__ method in your Zombie class. Even then I think it would only work if Zombie and Hero each had the same parent class say: 'Human'. But I hope not. 'Someone' would have to test that...I need some sleep first before I test more fixes for your Hero class. :) Perhaps Zombie - Hero might work. This example re-worked from your Zombie class works fine when the objects (ie Z1 and Z2) are both from the Zombie class. So...Hero.total_damage() might work just as well. I don't know for sure yet.
import random
class Zombie:
def __init__(self, zom_health=None):
self.__zom_health = None
if zom_health:
self.__zom_health = zom_health
else:
self.randomize_zom_health()
self.__zom_damage = 0
def randomize_zom_health(self):
zom_hp = random.randint(20,35)
if zom_hp <= 0:
print zom_killed
self.__zom_health = 0
else:
self.__zom_health = zom_hp
def __sub__(self, Other):
self.__zom_health -= Other.zom_damage() #or Other.total_damage()?
def zom_health(self):
return self.__zom_health
def zom_damage(self, damage=None):
if damage: self.__zom_damage = damage
return self.__zom_damage
>>> reload(Zombie)
>>> z1 = Zombie.Zombie(20)
>>> z2 = Zombie.Zombie(zom_health=30)
>>> z1.zom_health()
20
>>> z2.zom_health()
30
>>> z2.zom_damage(z2.zom_health())
30
>>> z1 - z2
>>> z1.zom_health()
-10
>>>

Program pauses and waits for user to hit return before finishing function

There is a while loop which constantly allows user input using the raw_input keyword, that input is then passed to a function called commands() which then routes the input to the command that the user specified. In this instance, Whenever I call function movement in my code, it makes the user press enter in the function and won't process the data until the user does hit enter.
Furthermore, it appears the program is returning part of code in function movement() back to the commands() function and thus is gives me the following glitch:
target leo2
Successful lock on Leo2
launch
Your mobile suit shifts as you begin moving towards Leo2 at coordinates [300, 100, 100]Invalid command
Type 'commands' for a list of valid commands
Distance from suit is: 200
As you can see, in the middle of the function it is trying to run the main whileloop from the main module, but it then goes back to running the function in the middle. Why does this occur, and how can I fix it?
My code:
Gundam2.py
import math
class Mobilesuits:
#class global variables/methods here
instances = [] #grid cords here
names=[]
def __init__(self,armor,speed,name,description,cockpit_description,\
radar_range, coordinates):
Mobilesuits.instances.append(self)
Mobilesuits.names.append(name)
self.armor=armor
self.speed=speed
self.name=name
self.description=description
self.cockpit_description=cockpit_description
self.radar_range=radar_range
self.coordinates=coordinates
#Intrinsically links mobile suits objects to their names.
#I want to be able to access each object directly through the name
def update_names(self):
Mobilesuit_names_instances_dictionary={}
for i in range(len(Mobilesuits.instances)):
Mobilesuit_names_instances_dictionary[Mobilesuits.names[i]] =Mobilesuits.instances[i]
return Mobilesuit_names_instances_dictionary
def can_detect(self, other):
for own_coord, other_coord in zip(self.coordinates, other.coordinates):
if abs(own_coord - other_coord) > self.radar_range:
return False
return True
def radar(self):
for other in Mobilesuits.instances:
if other is not self and self.can_detect(other):
print "%s detected at %s" % (other.description, other.coordinates)
def movement(self, target, official_target, currentmobilesuit):
print("Your mobile suit shifts as you begin moving towards %s at coordinates %s" %(target,official_target.coordinates))
distance = (currentmobilesuit.coordinates[0] - official_target.coordinates[0],\
currentmobilesuit.coordinates[1] - official_target.coordinates[1], \
currentmobilesuit.coordinates[2]-official_target.coordinates[2])
calculation=0
for i in distance:
calculation += abs(i)
print("Distance from suit is: %s" % (calculation))
#Make speed calculation based on distance away and speed of suit
#Errors:
#target without a suit gives error
maingundam.py
from Gundam2 import Mobilesuits
import thread
import time
#Main Variable/Object declarations:
Leo1=Mobilesuits(100,100,"Leo1","leo desc","dockpit desc",100,[100,100,100])
Leo2=Mobilesuits(100,100,"Leo2","leo desc","dockpit desc",100,[300,100,100])
Leo3=Mobilesuits(100,100,"Leo3","leo desc","dockpit desc",100,[100,150,100])
currentmobilesuit=Leo1
mobile_suit_names_list=currentmobilesuit.update_names()
#Main Function declarations
def commands(user_input,currentmobilesuit, mobile_suit_names_list):
#radar
if user_input == "radar":
currentmobilesuit.radar()
#Commands overview
elif user_input == "commands":
print("Command list:\nradar\ntarget (object)")
#Target objects
elif user_input.startswith("target"):
try:
global target
target=user_input.split()[1].title()
if mobile_suit_names_list[target]:
global official_target
official_target=mobile_suit_names_list[target]
print("Successful lock on %s" % (target))
except:
print("space object '%s' not detected" %(target))
elif user_input=="launch":
# try:
if official_target:
thread.start_new_thread(currentmobilesuit.movement, (target, official_target, currentmobilesuit))
# except:
# print("No target is selected")
#command not found
else:
print("Invalid command\nType 'commands' for a list of valid commands")
def active_delay(action, delay):
time.sleep(delay)
action()
#Main execution: paused
while True:
mobile_suit_names_list=currentmobilesuit.update_names()
commands(raw_input(),currentmobilesuit,mobile_suit_names_list)
Your second problem is that, if your user just hits Return, the return from raw_input is an empty string "", which is not handled by command(). You could fix this by adding the lines:
elif user_input == "":
pass
Your first problem is due to the way raw_input works; it always waits for user input followed by Return and is not designed for being used for continuous control input. This is trickier to fix. You might be better using a library designed for this sort of thing (continuous keyboard control), like pygame.

Global variables declared within function are still considered local

I am trying to write the program battleship. I have two gameboard matrices: one for player, one for computer. These are defined outside of main because I want them to be global variables because several functions manipulate/read them. I am using Python 2.6.1.
#create player game board (10x10 matrix filled with zeros)
playerBoard = [[0]*10 for i in range(10)]
#create computer game board (10x10 matrix filled with zeros)
computerBoard = [[0]*10 for i in range(10)]
Then I define the main function.
#define main function
def main():
global playerBoard
global computerBoard
#keepGoing is true
keepGoing = True
#while keepGoing is true
while keepGoing:
#call main menu function. Set to response.
response = mainMenu()
#if response is 1
if response == "1":
#begin new game
#call clearBoards function
clearBoards()
#call resetCounters function
resetCounters()
#call placeShips function (player)
playerBoard = placeShips(playerBoard, "player")
#call placeShips function (computer)
computerBoard = placeShips(computerBoard, "computer")
#call guessCycler function
guessCycler()
#if response is 2
if response == "2":
#keepGoing is false
keepGoing = False
Despite my declaration of global playerboard and global computerBoard within main PyScripter still says those are local variables. I don't understand this. How can I make sure they are global?
Documents I have already looked at:
Using global variables in a function other than the one that created them
Changing global variables within a function
http://www.python-course.eu/global_vs_local_variables.php
I definitely think you should reconsider if you need them to be global - You don't .-)
The cheap way, is do declare your stuff and the pass them on as parameters to the function
def MyFunc(board1):
print board1
board1 = "Very weak horse"
MyFunc(board1)
The real way to do it is to create a class and then access them using self
class MySuperClass():
def __init__(self):
self.horse = "No way up"
def myRealCoolFunc(self):
print self.horse
uhhh = MySuperClass()
uhhh.myRealCoolFunc()

local variable referenced before assignment error

I'm working on a text based game in python. And I ran into a little problem. One of my class methods tells the program what to do when an enemy attacks. Here is the code
def attack(self, method):
user_health_old = user_health
self.method = method
probs = random.randrange(1,100)
if method == "Knife":
while user_health > 0:
if probs in range(1,70):
attackWin = True
break
else:
attackWin = False
pla = player()
pla.healthRem(7)
if user_health < 1:
death()
elif method == "Hands":
while plaHealth > 0:
if probs in range(1,40):
attackWin = True
break
else:
attackWin = False
pla.healthRem(7)
if user_health < 1:
death()
pla = player()
pla.attackStatus()
player() is the class, and this method is attack()
attackStatus() is a little after chunk that will tell the player what happened in the battle, here's the code for that
def attackStatus(self):
if attackWin == True:
print "You Win, this time"
time.sleep(2)
if user_health < user_health_old:
print "But you're victory did come at a cost"
time.sleep(2)
print "You're new health value is: ",user_health
time.sleep(3)
And a couple more lines of code, Instead of running the whole program, atm I just have it set up to run this module,
pla = player()
pla.attack("Knife")
The problem comes when I run the program. It opens the console, but immediately quits. It doesn't output any information either. I'm not quite sure what's going on, if someone could help me, I'd appreciate it a lot.
In the else block of your first function when you use pla.healthRem(7) you are referencing pla before it has been assigned a value. From looking at the other code, it looks like you want to add pla = player() just above this line (or possible put pla = player() at the very beginning of the function).
Edit: After seeing your comment, this is definitely related to your use of pla in the function attack(). If you have assignment to a name anywhere in a function, it is considered a local variable everywhere in that function. This means that you can never access the global name pla if you have pla = player() in the function body. You can fix this in one of the following ways:
Take all pla = player() lines out of your function, this will allow any lookup of pla to find the global variable.
Put global pla at the very top of your function, the global statement will cause every assignment to that name to modify the global version of the variable instead of creating a new local variable.
Stop trying to use a global, when you need your player instance just use pla = player() inside of the function.

Categories