[Tutor] While loops

Alan Gauld alan.gauld at btinternet.com
Sat Jul 7 18:31:20 CEST 2012


On 07/07/12 13:57, myles broomes wrote:
>
> I am currently coding a 'text-based adventure game', and im having a bit
> of trouble with while loops. Here is my code so far:

What kind of trouble? You have lots of while loops, which loop?
And do you get an error message? If so please post it - all of it!

And looking through the code I see lots of potential problems, I have 
highlighted a few below...


> class Main_char(object):
>          def __init__(self,name,invent,level=1,health=30,mana=15):
>          def __str__(self):
>          def town_chcklist(self):
>                  self.checklist=[]
>                  if town1==YES:
>                          self.checklist.append(town1)

This method always resets the list to empty and then appends town1.
You should move the initialisation of the list into __init__.
But I see no variable defined anywhere called 'town1' so you would
get an error... Fortunately you don't seem to call this method
so no error is ever produced...

> def use_item(item):
>          """Allows the player to use an item in their inventory."""
>          if item in ('Medicine','Mana Potion'):
>                  Hero.invent.delete(item)
>                  if item=='Medicine':
>                          Hero.hp+20

Here you add 20 to the value of Hero.hp but then throw thecresult away.
I suspect you mean:

Hero.hp += 20

>                  elif item=='Mana Potion':
>                          Hero.mana+10

What is Hero.mana? It does not appear in the MainChar class definition?
And as above I suspect you mean += 10?

> class Shopkeep(object):
>          def __init__(self,inv,money):
>                  self.inv=inv
>                  self.money=money
>          def __str__(self):
>                  print("Shopkeeper: Here are my wares: \n\n",self.inv+".
> \nI currently have",self.money,"gold.")
>
> #Create the towns/ dungeons/ areas in different functions
> def Embark():

Its usual practice to keep names startingt with a captial letter for 
Clas names. It gets confusing for the reader(whio might be you!)  otherwise.


>          """The town in which the player begins there journey."""
>          if emb_town_visit==None:
>                  print("Welcome to Embark Town",player_name+"!")
>          while True:
>                  print("\n",hero)
>                  print("""
>                          \n\t\tPress '1' to exit the town...
>                          \n\t\tPress '2' to enter the local shop...""")
>                  user_choice=input('What would you like to do?: ')
>                  if user_choice=='1':
>                          shop()
>                          continue

You don't need 'continue' here because the loop drops out here anyway.
But it won't do any harm.

>                  elif user_choice=='2':
>                          if emb_town_visit==None:
>                                  print("\n\t\t\tAs you leave your home
> of Embark Town, you look back with a smile, then proceed North.")
>                          break
>                  else:
>                          print("\n\t\t\tThat is not a valid choice...")
>                          continue

same here. You only need continue if you are jumping out of the code 
block back to the start of the loop. It should be a relatively rare 
event in well structured code!


>          #The player has visited Embark Town
>          if emb_town_visit==None:
>                  emb_town_visit=YES
>          #Exit the function and change the players location variable
>          return 'Left Embark Town',emb_town_visit

This is broken due to some bad use of variable names.

You define emb_town_visit as a global with value None.
First time into Embark you test the global and if its set to None you 
*create a new local variable* of the same name. You then return it to 
main which sets *yet another local variable* of the same name to YES.
But this does not change the global so next time you visit Embark the 
global is still None and you go through the same routine. If you want to 
use the global value (and especially if you want to change it) you must 
specify it as global in each function . But frankly it should probably 
not be global anyhow since you want to track that per player so it 
should really be part of your character class.

> def shop():
>          """A generic shop that is placed in each town."""

In that case it should probably be a class and you create an instance in 
each town.

>          while True:
>                  print("""
>                  \nWhat would you like to do?
>                  \nPress '1' to buy an item...
>                  \n...Press '2' to exit the shop...
>                  \n...Or press '3' to ask about the town...""")
>                  user_choice=input(">")
>                  if user_choice=='1':
>                          print("Shopkeeper: Goodbye.")
>                          break
>                  elif user_choice=='2':
>                          print(emb_shopkeep)
>                          user_choice=None
>                          print("Enter the name of the item you would
> like to purchase.")
>                          user_choice=title(input(">"))

Its better to use different variable names for the choices.
It makes debugging easier if you can check the values of the original 
choice as well as the item selection.

>                          for item in emb_shopkeep.inv:
>                                  if user_choice in emb_shopkeep.inv:
>                                          message=handle_pur(user_choice)
>                                          print(message)
>
> emb_shopkeep.inv.delete(user_choice)
>                                  else:
>                                          print("\n\t\t\tThat is not a
> valid choice!")

At this point I was losing interest.... so I skipped down a bit...

 > ....
> emb_shopkeep=Shopkeep(shopkeep_invent,shopkeep_mny)
>
> #Player creation loop
> while True:
>          print("""
>          \nValiant hero, your quest begins here in Embark Town.""",
>          time.sleep(200),
>          """\nWhere you will go is up to you...""",
>          time.sleep(200),
>          """\nHow your adventure will pan out is up to you...""",
>          time.sleep(200),
>          """\nBut first...""",
>          time.sleep(200),
>          """\n...tell me your name...""")
>          player_name=title(input(">"))
>          print("\nAh! So your name is",player_name,"- a truly heroic name!")
>          Hero=Main_char(player_name,player_invent)
>          break

Because of the break this will only execute once, so there is no point 
in having a loop.

Also you create your player by passing in player_invent - a list with 
the single value 30.

This will create a reference inside the object to the global value so 
all changes will affect the global. With only one player as here its not 
a problem nbut if you ever try to creaste multiple players they will all 
share and modify the same invent list. Probably not what you want. Get 
used to defining data where it needs to be, either global or in the 
class. Then you could just pass in the integer value 30 and get the 
__init__ to create the list (if indeed it needs to be a list, it seems 
you only ever use the single element!

> def main():
>          """Main game loop."""
>          while True:
>                  if plyr_location=='Embark':
>                          plyr_location,emb_town_visit=Embark()
>                  elif plyr_location=='Left Embark Town':
>                          return

In practice this doesn't need to be a loop since you only have one 
player so it actually resolves to:

def main():
     plyr_location,emb_town_visit=Embark()

which creates two local variables, set them to the return value of 
Embark() and exits. It has no effect on the global values defined at the 
top.

I would strongly recommend you start again and remove all code that you 
are not executing. And build up the code one class/function at a time 
and test it as you go so that you always have working code before adding 
anything new.

You can do this by starting with the high level framework with
a menu and calls to create the character (but the class is still
empty at this point) and then define the class. Then add the
shopkeeper call and empty shopkeeper class, then define the new class, 
and so on.

Alternatively start with the  class definitions - one at a time - and 
write specific test code to check that the class works. Then once the 
classes work add the outer control code.

Either approach works and different programmers prefer different 
approaches. But in either case add code incrementally only after getting 
the previous code working. This keeps the code clearer and makes 
debugging simpler (because the bug is almost certainly in the new
code you added!)

Finally, I'd look to move much more of your data and functions into the 
classes. For example embark could be a method of a player - the first 
thing he does after being created. Similarly shop() could be a player 
method:

Hero = Mainchar(name, value)
Hero.embark()
Hero.shop()

Now, what was the problem with the while loop?

>   For some reason that I cant figure out, the 'Player creation loop'
> wont run when I try to run the program.

As I point out above its not really a loop...
But try adding some print statements to see how far it does get?
Send us any error messages...

HTH,
-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/





More information about the Tutor mailing list