[Tutor] my newbie program
Magnus Lycka
magnus@thinkware.se
Wed Nov 27 07:32:02 2002
At 22:37 2002-11-26 -0600, david wrote:
> > Pass start room in as a parameter instead of using a global.
>
>not sure what you mean here though.
Ok. This can be made in a number of different ways.
In designing classes, don't make the mistake to think that
your objects should be as stupid as the corresponding real
world object. An object-oriented potato WOULD peel itself!
I see a map as an object that knows where rooms are. It's not
just a dict, it's a class, and a user of a class should never
rely on a certain implementation, only on a public interface.
If you decide to move your data from a dictionary to a table
in an SQL database, you should only have to change the map
class. The interface should be able to stay the same.
It's your program, and there are many good ways to do it, but
for instance you could have something similar to:
class Map:
def __init__(self):
self.__grid={} # Don't assume global startroom
def addRoom(self, room, x, y):
if self.__grid.has_key((x,y)):
raise KeyError, "Location occupied"
self.__grid[(x, y)] = room
def getRoom(self, x, y):
return self.__grid[(x, y)]
def getLocation(self, room):
for coord, aRoom in self.__grid.items():
if room == aRoom:
return coord
raise KeyError
class Room:
def __init__(self, map, x=0, y=0):
self.__map = map
map.addRoom(self, x, y)
def dig(direction):
...
class Actor:
def __init__(self, room):
self.currentRoom = room
...
def dig(self, direction):
if not 'shovel' in self.inventory:
print "Nothing to dig with"
return
self.currentRoom.dig(direction)
def act():
...
if verb == dig:
self.dig(
def main()
map = Map()
startroom = Room(map)
me = Actor(startroom)
while 1:
me.act()
See? You don't have any reliance on global varaibles any
longer. If you extend the game to several levels and use
one map per level,
I'm not sure the Map class has an appropriate name. It's
not a map such as a map that the adventurer might find on
the floor in a room. It's a dynamic object that is updated
in real time and actually defines the arena we play in.
Maybe CaveSystem or something similar is a better name.
It also depends on what it should be used for. If this
class handles all inter-room issues, such as digging, it
certainly is much more than a map.
Every class should capture one, and only one key abstraction.
Keep related data and behaviour in the same place.
So the Room class should have intelligence and behaviour.
Your Actor class might have a method "dig()", but this
method should probably just call a dig-method in the Room
it's digging in. (Or should there possibly be wall, floor
and ceiling objects? No, Let's forget that for now.)
It's the Room, not the actor who has a location and walls
etc. The logic for digging through walls seems to belong in
Room since the object it to reach or create a new Room. If
you had to calculate how tired the Actor would become from
digging, or check whether he has a shovel, this should happen
in the Actor class of course. But with the Actor dig method I
suggested above, you could first check for the shovel, if you
find it you can call the self.currentRoom.dig(), and finally
you can calculate fatigue. In a more advanced setting, you
would pass the best (or all available) digging implements
(hands, shovel, excavator) to the dig() method of the Room
object.
> > UnboundLocalError: local variable 'verb' referenced before assignment
>
>i saw this error and i don't know how to fix it. putting a break after the
>print didnt work. i don't understand what a return statement would do.
Break just gets you out of a loop. Return exits a function call.
This is what you want, right? You see, in Python you can have
exits from your function in several places by using several
return statements with different return values. (If break could
be used to exit a function, it would mean that this exit point
couldn't return a value back to the caller, and often you want
to do that.) Of course, another option is to raise an exception.
That means that a special type of value, an exception object is
received by the caller.
Some people think that multiple returns are bad form, and some
languages don't even allow it. Your options are basically:
def f():
if x:
return xx
if y:
...
return yy
if z:
...
...
return zz
...
...
return qqq
or
def f():
if x:
returnValue = xx
elif y:
...
returnValue = yy
elif z:
...
...
returnValue = zz
else:
...
...
returnValue = qqq
return returnValue
In my opinion, the first style is usually easier to follow. The
else block in the second style will usually be big, and the less
indentation the better. Also, with the first style, it's clear
when you get to a return statement that this condition leads to
no more actions. In the second style, for whatever scenario you
follow, you have to follow the function to the last line to be
sure that you understand what happens.
>i see what you mean obiwan but i have never heard of jacobson.
:) He's one of the "Three Amigos" who run Rational Corp which
produces Rational Unified Process (a lot of very expensive Word
templates) and Rational Rose (a OO modelling tool) etc. The three
amigos merged their OO Design methods and notations, and apart
from RUP, they also created Unified Modeling Language (UML).
Ivar Jaconson, an old Ericsson employee, is the mastermind behind
a concept called "Use Cases". It was the central part of his
"Objectory Process" that later became RUP. Use Cases use Actors
as a central concept. Jacobson is also an advocate of so-called
entity classes and control classes. Like many others, I feel that
this kind of division often causes developers to loose the
benefit of Object-Oriented thinking. For as book on how to use
objects in a uniform way, without this ugly division in controllers
and entities, read Arthur Riel's "Object-Oriented Design Heuristics".
> elif map.grid.has_key(newcoord):
> print "already a room there, digging an exit"
> self.location.exits[directions[dirobj]]=map.grid[newcoord]
Stuff like this DO NOT belong in the actor class. It probably belongs
in a remaned Map class... CaveSystem? UnderGround?
--
Magnus Lycka, Thinkware AB
Alvans vag 99, SE-907 50 UMEA, SWEDEN
phone: int+46 70 582 80 65, fax: int+46 70 612 80 65
http://www.thinkware.se/ mailto:magnus@thinkware.se