I'm writing a text-based RPG for a class, and am stuck on a code predicament...
from tkinter import *
...
runOnce = False
nextRun = False
"""Main Loop"""
while True:
#New Game initialize
if you.rName == None and runOnce == False:
log("What is your name, adventurer?", eventLog)
runOnce = True
if you.rName != None and nextRun == False:
log(f'What is your profession, {you.rName}?', eventLog)
nextRun = True
#keypresses
playerInput.bind("<Return>", keyPress)
playerInput.bind("<FocusIn>", focusIn)
top.update()
top.update_idletasks()
What I have currently works, but there are a ton more if-statement type situations that need responses before continuing to the next statement. The loop is to continuously update the GUI as the game is run.
How can I code the something that needs a response once within a while loop efficiently?
Seeing the clarification, I agree with the comments these type of actions shouldn't belong in the loop. These should be all data that are collected prior to the main game loop.
If you're looping through these to validate for inputs, you can have separate loops instead:
while not you.name:
you.name = input('Enter name: ')
# some additional validation text if necessary...
while not you.job:
you.job = input('Enter job: ')
# some additional validation text if necessary...
while True:
# main game loop requiring you.name, you.job
Another approach is a bit contrived. You can pre-define these functions before your mainloop and create a RunOnce class to only execute these functions once:
class RunOnce(object):
def __init__(self, func):
self.func = func
self.ran = False
def __call__(self):
if not self.ran:
self.func()
self.ran = True
# once the function has been called,
# flip self.ran so it won't fire again.
# Decorate your functions with this special class
#RunOnce
def get_name():
you.name = input('Enter Name: ')
#RunOnce
def get_job():
you.job = input('Enter Job: ')
And then when you are in the main game loop:
while True:
get_name() # this will run once
get_job() # this too will run once
get_name() # this won't run anymore
get_job() # this neither.
The benefit of this approach is it gives you the flexibility to re-run the function if necessary:
get_name() # get_name.ran becomes True
get_name.ran = False # reset the flag
get_name() # this will run again.
I'd still say it's much better to just restructure your code so that things that only need to be captured once stay outside the main loop.
Try checking your parameters for null before using them. If you're looking for user input you can do this:
userInput = None
while True:
userInput = input("Do the thing only once") if userInput is None else userInput
...
Related
I am trying to figure out a way to change a global variable from False to True if function rope is called. With my existing code, what could I add to make this possible?
Also, the global variable that exists is called inventoryRope, and it starts off as False.
Here is my code:
def rope():
print("You pick up the rope.")
command = input("Type CONTINUE to carry on.")
if command == "CONTINUE":
nextScene()
Need to use global:
inventoryRope = False
def rope():
global inventoryRope
print("You pick up the rope.")
command = input("Type CONTINUE to carry on.")
if command == "CONTINUE":
inventoryRope = True
nextScene()
Your actual goal here appears to be tracking inventory and making its state accessible from multiple functions. In that context, your approach will work, but it doesn't scale well to arbitrary numbers of inventory items.
inventory_rope = False
def pick_up(item):
if item == "rope":
inventory_rope = True
def use(item):
if (item == "rope") and inventory_rope:
print("Used rope")
Note: Because it looks like you're keeping this very simple, I'm using strings for items here. There are certainly better ways to do this.
There are many potential ways to handle inventory; one would be to simply create a list to handle whatever the player picks up.
inventory = []
def pick_up(item):
print("You picked up", item)
inventory.append(item)
def use(item):
print("Used", item)
inventory.remove(item)
You could instead inherit from the built-in list type, if you want to build in additional/different behaviors.
class Inventory(list):
def append(self, item):
print("You picked up", item)
super().append(item)
inventory = Inventory()
inventory.append("rope")
Another possibility would be making the inventory an attribute of your player object, if there are other things the player can do that would make sense to build into a class.
class Player(object):
_inventory = None
def __init__(self):
# Note: Don't use mutable objects as default arguments
self._inventory = []
def pick_up(self, item):
print("You picked up", item)
self._inventory.append(item)
player = Player()
player.pick_up("rope")
I keep getting an infinite loop in this class and this time it's definitely coming from .ExNote() but I have no idea why. I've simplified it down so here you go:
class Note():
x=[0]
y=[0]
def __init__(self, Num, staff, note, notetype):
self.staff = staff
self.note = note
self.notetype = notetype
self.Num=Num
def ExNote(self):
screen.blit(self.notetype, (self.x[self.Num], self.y[self.Num]))
print('exnote')
going = True
while going:
screen.fill(white)
Note(0, '0R', '40', EthnoteIMG).ExNote()
returrns
exnote
exnote
exnote.....
Why doesn't it stop after doing the function once?
"while going:" is a loop. It needs to be set to False inside the loop to stop it. With just the code you've shown, the while loop isn't needed and could be removed altogether. You said you're only showing a portion of the code, so I'll leave it up to you to decide when the loop should stop.
class Note():
x=[0]
y=[0]
def __init__(self, Num, staff, note, notetype):
self.staff = staff
self.note = note
self.notetype = notetype
self.Num=Num
def ExNote(self):
screen.blit(self.notetype, (self.x[self.Num], self.y[self.Num]))
print('exnote')
going = True
while going:
screen.fill(white)
Note(0, '0R', '40', EthnoteIMG).ExNote()
if <your stop condition>:
going = False # You need to set going to False inside your loop to get it to stop
OR
going = <some function that returns false when appropriate>
Please insert a break function at the end of your code
while True:
Bla blabla
Blablabla
break
That should kill it
I have a problem with functions. I want to know is it possible to when calling function second time program jump to that line of code, just like in assembler where would I use jmp directive. So when I firstly call decision_function after that some calc_funcs are done ( it doesn't matter what they do ), but when I call that same decision function second time I don't get execution of calc funcs.
def main():
decision_function()
calc_function1()
calc_function2()
calc_function3()
decision_function(reqierment)
def decision_function(reqierment=None):
if reqierment is None:
do this
else:
do that
I prefer using setting a variable to true:
skipper = true
def main():
decision_function()
calc_function1()
calc_function2()
calc_function3()
decision_function(reqierment)
def decision_function():
if skipper == True:
do this
skipper = False
else:
do that
I have an assignment where I need to create a stopwatch, but only for IDLE. Here's what I have so far, I'm not sure how to convert the times to normal time.
import time
start = 0
def stopwatch():
while True:
command = input("Type: start, stop, reset, or quit: \n")
if (command == "quit"):
break
elif (command == "start"):
start = time.time()
print(start)
stopwatch2()
elif (command == "stop"):
stopwatch()
elif (command == "reset'"):
stopwatch()
else :
break
def stopwatch2():
while True:
command = input("Type: stop, reset, or quit: \n")
if (command == "quit"):
break
elif (command == "stop"):
total = time.time() - start
print(total)
stopwatch()
elif (command == "reset'"):
stopwatch()
else:
break
stopwatch()
Thanks for your help!
You can use datetime.timedelta():
import datetime
print(datetime.timedelta(seconds=total))
For example:
In [10]: print datetime.timedelta(seconds=10000000)
115 days, 17:46:40
Think of it like this... Idle is really no different than coding in the interactive python interpreter which I do all the time (well, I use ipython).
Think of your stopwatch like an object. What functionality does it have? Things like start, stop, reset.
This may not be the most efficient way to solve the problem but here is what I would do.
>>> import time
>>> class StopwatchException:
pass
>>> class IsRunningException(StopwatchException):
pass
>>> class NotRunningException(StopwatchException):
pass
>>> class Stopwatch():
def __init__(self):
self._times = []
self._is_running = False
def start(self):
if self._is_running:
raise IsRunningException
self._is_running = True
tracker = {
'start': time.time(),
'stop': None,
}
self._times.append(tracker)
def stop(self):
if not self._is_running:
raise NotRunningException
tracker = self._times[-1]
# the dict is mutable, and tracker is a shallow copy
tracker['stop'] = time.time()
#print(self._times[-1])
self._is_running = False
def reset(self):
if self._is_running:
raise IsRunningException
self._times = []
def total(self):
if self._is_running:
raise IsRunningException
total = 0.0
for t in self._times:
total += t['stop'] - t['start']
return total
>>> s = Stopwatch()
>>> s.start()
>>> s.stop()
>>> s.total()
6.499619960784912
>>> s.reset()
>>> s.total()
0.0
To me, anytime you want to model a real world object or "thing", OOP makes the most sense. Heres a simple argument for each element of the program:
Classes
StopwatchException
Base exception class for the stopwatch class.
IsRunningException
Raised if the stopwatch is running when it should be stopped.
NotRunningException
Raised if the stopwatch is not running when it should be.
Stopwatch
This represents the actual stopwatch.
Stopwatch Class
init
A basic stopwatch class really only needs to instance variables. A variable that stores each start/stop time (allows them to be computed later) and a variable that stores the "state" of the stopwatch (on/off or running/stopped).
start
First we need to make sure the stopwatch isn't already running.
Then we need to set it's state to running and store the time in self._times.
I chose to use a local variable and store each time pair as a dictionary with the keys 'start' and 'stop'. I chose a dictionary because it is mutable. You could also have a list with index 0 being the start time and index 1 being the stop time. You cannot use a tuple for this since tuples are immutable.
Also, the "temporary" variable is not necessary but I used it for readability.
stop
First we need to make sure the stopwatch is actually running.
Then we set the state as 'stopped' (using our boolean self._is_running) and store our stop time, similar to what we did with start. I think it doesn't matter whether you set the boolean at the beginning or the end, although I chose to set it at the beginning of the start function and the end of the stop function so that the times would not include the time needed to update a boolean variable (even though it's a trivial task, it could be much more complex in more complex programs).
reset
Make sure the stopwatch isn't running
Set self._times to be an empty list.
total
Make sure the stopwatch isn't running.
Optional: You can stop the stopwatch here if it's running, but I prefer to raise an exception.
Iterate through each list item in self._times and calculate the difference between stop and start.
For an online course in python, I'm making a basic text-based adventure game in python.
Right now, I have a rudimentary inventory system that works through booleans for if the user has an object or not, and integers for limited items, such as ammo and whatnot.
Here is the code for the inventory system
def Inventory(self): #The inventory for the game. I don't know how to program it properly, so this is my testing ground.
#This will hold the boolean values to if the player has the items or not. Another will be used to show the user the items
street_clothes = False
pistol = False
ammo = 0
phone = False
And this is the code where I am trying to modify the inventory function above
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
Attempting to run this program gets me this error:
python ex43.py
File "ex43.py", line 78
self.Inventory(CR97) = True
SyntaxError: can't assign to function call
Is there something else I'm supposed to do? I'm very new to python, and this is my first project on my own.
Here is the entire code, for reference
from sys import exit #allows the program to use the exit(1) code
from random import randint #allows the program to use a random number
class Game(object):
#quotes that pop up if the person dies, and also defines the start and self variables
def __init__(self, start):
self.quips = [
"You lose!"
]
self.start = start
def Inventory(self): #The inventory for the game.
#This will hold the boolean values to if the player has the items or not.
street_clothes = False
pistol = False
ammo = 0
phone = False
#this function launches the game, and helps with the room transfer
def play(self):
next = self.start
while True:
print "\n---------"
room = getattr(self, next)
next = room( )
#if the user dies, or fails at the game, this is the function that is ran
def death(self):
print self.quips[randint(0, len(self.quips)-1)]
exit(1)
#Welcome screen to the game
def welcome_screen(self):
print " place holder"
return 'intro_screen'
#Intro screen to the game
def intro_screen(self):
print "place holder"
action = raw_input("> Press any key to continue ")
return 'Eric_Apartment'
#Eric's apartment
def Eric_Apartment(self):
print "type in grab your gun"
action = raw_input("> ")
if action == "grab":
self.Inventory(CR97) = True
# self.CR97_ammo += 15
# print CR97_ammo
# print self.CR97_ammo
exit(1)
a_game = Game("welcome_screen")
a_game.play()
That's an amazingly perverse way to go about it. Why are you using a function to store data?
Just have a player object, with an inventory array.
I'd recommend using objects to model items too. Good use for for a class hierarchy. COuld have a base Item, with Subclasses SingleItem and StackableItem, etc.
Instead of using a function, try using a class -
class Player:
def __init__(self):
self.street_clothes = False
self.pistol = False
self.ammo = 0
self.phone = False
def give_street_clothes(self):
self.street_clothes = True
# etc
But personally, instead of using each item as a boolean, I'd use a list of items:
class Player:
def __init__(self):
self.inventory = []
# add code for ammo/pistol
def has_item(self, item):
return item in self.inventory
def give_item(self, item):
self.inventory.add(item)
def remove_item(self, item):
self.inventory.remove(item)
# etc