SimulaE - Model Update
Labels: Algorithms, Design, Objects, OOP, Python, Refactoring, SimulaE
Labels: Algorithms, Design, Objects, OOP, Python, Refactoring, SimulaE
Labels: Development, Dynamic Languages, Engineering, Guido van Rossum, Matz, Objects, OOP, Python, Ruby, Smalltalk, Static Typing
Labels: AOP, contracting, Development, Dynamic Typing, Engineering, Functional Programming, OOP, PHP, Rapid Application Development, Salaried, Static Typing, Website Design
The solution was sitting right in front of me all along, the standard library's 'pickle' module (or alternatively the 'cpickle' variation for speed's sake). Due to the issues presented in a virtual world simulation the topic of object blueprints and simulation population ease come to mind rather quickly. The best environment for manipulating these new objects will ultimately be via the methods provided by both the the SimulaE package (SimulaE.loadObject(filename_to_load, [optional_load_path]) as well as at the code level in the parent class in SimulaE.SimulaeObject.saveObject(filename_to_save_as, [optional_save_path]).
By going this route it has been realised that a simple loop and/or script-like routine could be used to create all the generic object templates needed for design platforms, and for customisation all one need do is use the loadObject routine, make the necessary alterations and re-save said item as a more concrete, concise and specialised object, named appropriately of course.
Let us take an example of a room type object (more simply put, a larger container object). We'll use simple constructor information here for the sake of staying on focus. We're going to make a 4 metre x 5.5 metre dining room, with a 2.75 metre ceiling, simple called "Dining Room". It will be empty sans an already pickled to storage butler object we've created for the sake of this example (whose filename is simply "butler_jeeves"), though future discussions on SimulaE's container and stack loading methods are forthcoming.
import SimulaE
generic_dr = SimulaE.SimulaeObject(name='Dining Room', width=4.0, length=5.5, height=2.75)
generic_butler = SimulaE.loadObject('butler_jeeves')
generic_dr.addToContents(generic_butler)
generic_dr.saveObject('diningroom4x5.5x2.75wButler')
We now have a pickled version of this generic dining room of the aforementioned dimensions, sporting its own copy of Jeeves the butler, saved to a physical file on whatever storage device we're set to utilise. We can overwrite said object by simply saving the item with the filename as it exists on whatever storage device is in use. There is also the flexibility of specifying alternative file save and load paths with allow for multiple parallel simulations and/or individuals to work in safe separate but equal spaces, very much along the lines of a Unix mentality. Another advantage by working in this manner is being able to create a path full of simulation objects and copy said path en masse for alternative and/or backup purposes.
Either way you slice the pickle (module), it proves to be quite (ful)filling, making one feel quite (programmatically) satisfied.
I've always found it interesting reading about coding paradigms and what not, including ways in which a coder can increase his/her throughput. Meaning, how can I quickly pump out software that is easily portable, eloquently written, and easily maintainable by either myself and/or someone else one, two, six, eighteen months down the road?
I've found out the answer to that question, in the form of a language, and it isn't Perl, Java, or Ruby. Simply put, it is Python by Guido van Rossum. The language that I loved to hate for so long due to what was perceived as a nasty control freak mentality regarding white space sensitivity, and the "lack" of freedom of being able to use {'s, ('s, ['s and ;'s anywhere I wanted.
I've been using Python as mentioned previously in this blog for both large professional projects as well as certain other miscellaneous personal object-focused projects of mine and about two years ago when accepting a one-off project outside of my normal employment environment, I decided to try utilising my new favourite language for professional work. I must say that it was indeed a very simple program that I could've easily written in Perl, but no where as cleanly as in Python. The standard python libraries/classes included with every distribution (including as a stock install on my OS X 10.3 Panther equipped Apple Macintosh G3 iBook which I was using at the time) made it a clear choice (at least to attempt).
The premise of the program was amongst the most simplest of tasks. The client has a Microsoft Frontpage created website with a form. It currently points to no where because the individual doesn't know anything about capturing form data, so that's where I come in. All that is wanted is for all of the fields to be commingled into an e-mail to be fired off every time someone submits that form. He doesn't initially even want data format and/or content checking, but I inquired anyway (I'm not some code monkey who doesn't try to analyse what the non-coder *really* needs/wants/means).
The code itself took a matter of about 15 minutes to write down, organise and test. The code is more than fifty-percent blank lines and/or comments. Using the standard smtplib and cgi libraries/classes, this turned out to be an absolute breeze. The advantage of easily stepping through the dictionary (hash) produced by the cgi.FieldStorage() method was a cinch thanks to the built-in cgi.has_key() and cgi.value() methods.
While this is hardly an example of actual RAD, or any detailed work for a language such as Python, it does give a simple real world example of why I will continue to push for the use of this language. What I wrote worked the first time I wrote it, without any errors. I reads like pseudo-code and it was enjoyable to write because it flowed so easily from my mind into Python's very natural syntax. I used to espouse Perl for such things, but in comparison, I find it difficult to think that I held Perl in such regards for natural syntax.
This doesn't mean that I'm a one language only person. Much of the application infrastructure I've produced at my current (and previous) employers' establishments I design and implemented in Perl on a multitude of Linux and/or FreeBSD boxes. This has changed as I've moved to a FreeBSD centric platform layout, with the intent of someday using Python as the shining star for any medium to large implementations, and let it share the small jobs with a mix of Perl and Bash scripts.
I only wish I could get others to give Python a fair shot as it truly is one of those languages that deserve a second look, it may just changed your entire perspective on how you code.
Labels: BSD, CGI, Coding, Design, Java, Objects, OOP, Perl, Python, Ruby
Now that a little over a week and a half has passed at my new place of employment, I find that I understand my new environment enough to make some observations in a not-too-specific manner out of respect for my new employer.
First let me state that all of the existing software is a product of its environment and that it all functions as it was intended. That being said I’m able to say with a clean conscience (after that little preamble) state that the code was ... lacking.
I am thankful for this to some degree. For one thing it provided an opportunity for employment at a place I enjoy with some very intelligent individuals who all seem to have their own special abilities and areas of expertise.
More importantly though, I’m thankful because it places me in a situation I find most mentally stimulating. It makes me re-think an entire existing architecture and being that I have held the role of Software Architect (amongst others) for much of my professional life, it is all the more appropriate.
It is one thing to walk into an new environment with a clean slate in which one may design to their heart’s content, yet another wholly different situation when the software exists in a production environment of one form or another. There are so many more facets with which to deal when the database structure and all of the depending software is tightly build upon that aforementioned code base.
The whole point of this rambling is that my first week and a half has passed and while I have done much more with the database redesign, class design, object handlers, etc., I have finally been able to enjoy that great feeling which comes when turning previously un ‘strict’able perl code into a fully compliant piece of code. I might also add that I ensured the code conformed within the guidelines of Damnian Conway’s “Perl Best Practices” book, which while a little different than my own manner of laying out perl code, is wonderful none the less.
Now that this honeymoon is over, I can move onward and upward to greater code causes to champion, and based upon the intents of the owner of the company I don’t doubt that there will be a wonderful logic requiring plethora of future projects for which I am to contend. I only hope that others out there are as lucky in their endeavours.
Labels: Bad Design, Best Practices, Coding, Methodology, Objects, OOP, Perl, Strict
In many cases these ‘games’ were written in the low level language du jour, such as ‘C’, or in a custom language like ‘Inform’ (in the case of Infocom games) which was also written in ‘C’. There were languages which would’ve been better suited for these games/simulations, object oriented programming (OOP) languages such as Smalltalk, or Objective-C, but they were generally not utilised as such.
Recently in my workplace I have been developing full time (for the past couple of years) in Python, a wonderful high level language written by Guido van Rossum which is a proper OOP language. As such, the idea of writing a simulation (not a ‘game’ per se) which would allow me to focus on not only how one would model the real world (to a certain depth), but as an exercise in python objects.
While the code is still an ongoing side work of mine, I am posting the parent class (SimObject) and it’s associated child classes (Place, Person and ExitObject) for review by those who are interested. Please note that due to certain conflicts with word press, proper pythonic indentation (required for execution) is not established as such.
#####################################################
# S I M O B J E C T C L A S S #
#####################################################
class SimObject:
object_master_list=[]
def __init__(self,
object_id = 0,
object_sku = 0,
short_description = 'Short Description',
long_description = 'Long Description',
name = 'Name',
weight = 1.0,
height = 1.0,
width = 1.0,
depth = 1.0,
visible = True,
closed = False,
contents = [],
stack = []):
self._object_id = object_id
self._object_sku = object_sku
self._short_description = short_description
self._long_description = long_description
self._name = name
self._weight = weight
self._height = height
self._width = width
self._depth = depth
self._visible = visible
self._closed = closed
self._contents = []
self._stack = []
self.object_master_list.append(self)
def getObjectById(self,object_id):
for sim_objects in self.object_master_list:
if sim_objects.getObjectId() == object_id:
return sim_objects
def addContents(self,item):
try:
self._contents.append(item)
return True
except:
return False
def addToStack(self,item):
try:
self._stack.append(item)
return True
except:
return False
def isVisible(self):
return self._visible
def getContents(self):
return self._contents
def getShortDescription(self):
return self._short_description
def getLongDescription(self):
return self._long_description
def getSize(self):
return "Object Details ... Weight: %.2f, Height: %.2f, Width: %.2f, Depth: %.2f" % (self._weight, self._height, self._width, self._depth)
def getHeight(self):
return self._height
def getWeight(self):
return self._weight
def getName(self):
return self._name
def getObjectId(self):
return self._object_id
def getDescription(self):
output = ''
vowels = ['a','e','i','o','u']
if isinstance(self,Place):
short_desc = self.getShortDescription()
output += short_desc + "\n"
underline = ''
for i in str(short_desc):
underline += '-'
output += underline + "\n"
output += self.getLongDescription() + '\n'
for thing in self.getContents():
if isinstance(thing,ExitObject):
if thing.isVisible():
output += "There is "
if thing.getName()[0].lower() in vowels:
output += 'an '
else:
output += 'a '
output += thing.getName() + ' '
output += 'here.\n'
else:
### Don't short exits if they are just cardinal directions (_visible=False)
pass
itemlist = []
peoplelist = []
for thing in self.getContents():
if isinstance(thing,SimObject) and not isinstance(thing,ExitObject) and not isinstance(thing,Person):
itemlist.append(thing.getName())
elif isinstance(thing,Person):
peoplelist.append(thing.getName())
if len(itemlist) == 1:
output += "There is "
if itemlist[0][0].lower() in vowels:
output += 'an '
else:
output += 'a '
output += itemlist[0] + ' '
output += 'here.\n'
if len(itemlist) >= 2:
total_items = len(itemlist)
item_counter = 1
output += "There are "
for items in itemlist:
if items[0].lower() in vowels:
output += 'an '
else:
output += 'a '
if item_counter <= total_items-2:
output += str(items)+', '
elif item_counter == total_items-1:
output += str(items)+' and '
else:
output += items + " "
item_counter += 1
output += "here.\n"
output += "\n"
if len(peoplelist) == 1:
output += str(peoplelist[0]) + " is here.\n"
elif len(peoplelist) > 1:
item_counter = 1
total_items = len(peoplelist)
for items in peoplelist:
if item_counter <= total_items-2:
output += str(items) + ', '
elif item_counter == total_items-1:
output += str(items) + ' and '
else:
output += str(items)
item_counter += 1
output += "are here.\n"
elif isinstance(self,Person):
output += self.getName()
output += "is standing before you!\n"
elif isinstance(self,ExitObject):
if self.isVisible():
output += str(self.getShortDescription()) + '\n'
else:
output += "You see nothing out of the ordinary here.\n"
else:
output += "unsure of type!\n"
output += "\n"
return output
#####################################################
# P L A C E C L A S S #
#####################################################
class Place(SimObject):
def __init__(self,
object_id = 0,
object_sku = 0,
short_description = 'Short Description',
long_description = 'Long Description',
name = 'Name',
weight = 1.0,
height = 1.0,
width = 1.0,
depth = 1.0,
visible = True,
contents = []):
SimObject.__init__(self,
object_id=object_id,
short_description = short_description,
long_description = long_description,
name = name,
weight = weight,
height = height,
width = width,
depth = depth,
visible = visible,
object_sku = object_sku,
contents = [])
#####################################################
# P E R S O N C L A S S #
#####################################################
class Person(SimObject):
def __init__(self,name="",object_id=0,object_sku=0):
SimObject.__init__(self,name=name,object_id=object_id,object_sku=object_sku)
self._hp = 100
def isAlive(self):
if self._hp > 0:
return True
else:
return False
def getStats(self):
print "%s has %i hit points remaining!" % (self._name,self._hp)
def receiveHit(self,damage="0"):
self._hp = self._hp - damage
if self._hp <>
self._hp = 0
def attack(self,enemy):
if type(enemy) == type(self):
attempt_roll = randint(1,10)
if attempt_roll > 5:
enemy.receiveHit(attempt_roll)
else:
print "Missed %s!" % str(enemy._name)
else:
print "Cannot attack %s" % str(enemy)
def getSize(self):
return "Person Details ... Weight: %.1f, Height: %.1f, Width: %.1f, Depth: %.1f" % (self._weight, self._height, self._width, self._depth)
def move(self,destination):
#### add test against dimensions. person WxD must be greater than Exit HxW, and the smallest Person dimension much be
#### smaller than the smallest Exit dimension
destination_room = self.getObjectById(destination)
destination_room.addContents(self)
#####################################################
# E X I T C L A S S #
#####################################################
class ExitObject(SimObject):
def __init__(self,visible=False,name="ExitName",short_description='Exit',
long_description='an Exit',aliases=(), destination=0,object_id=0,object_sku=0, weight=1.0,width=1.0,height=1.0,depth=1.0,closed=False):
"""Aliases are lists of names by which this exit can be referenced"""
SimObject.__init__(self,visible=visible,name=name,short_description=
short_description, long_description=long_description,object_id=object_id,
object_sku=object_sku, weight=weight,width=width,height=height,depth=depth,
closed=closed)
self._aliases = aliases
self._destination = destination
def getDestination(self):
return self._destination
def getAliases(self):
return self._aliases
Labels: BBS, C/C++, Code Sample, Guido van Rossum, MUD, Objects, OOP, Python, SimulaE, Zork