Send Mail To:Steve Register

Inventory Abstraction - Kenneth Power.
This article was originally written for Darren Hebden's RLNews

An Inventory tracks Items in Storage. Items are anything you deem 
trackable: gloves, clubs, food, coins, flowers. Storage is anything
defined as able to hold items: chests, sacks, hands, buildings.

A basic Inventory does not care about extraneous fluff, such as 
container limitations. Keeping inventory implementation basic enables 
code reuse as discussed later.

Throughout this paper, psuedo code is used to model examples. The 
examples use the following sample item definition:

   define Item:
      variable Name

Tracking items
There are two basic ways to track items. First is with a simple list, 
rather like writing a list of groceries. Lists usually are FIFO (First 
in First out) or LIFO (Last in First out). Other ways may exist. Indeed 
in some languages, there are very exotic forms of lists. A trouble with 
lists is the retrieval of information from the middle of the list. The 
list is examined from either end until the information is located.

Our example simple list:

   list define Inventory

Second is through a more complex scheme wherein more than the item is 
tracked. Also tracked is information about the item. This is so items 
may be grouped. Grouping has its pro's and con's like anything else. 
Use of grouping, for example, allows easier display of items (in my own 
opinion of course). In a way, grouping is a more esoteric list form, 
using such things as dictionaries, maps and other terms. 

In this example, the items are tracked by their name.  Example:

   list define Inventory:
      list define itemNames
      keyedList define qtys

Add items
What use is an Inventory if you are not able to add items to it? Thus,
the first function we define is the add() function. 

In basic form, add() only wants to place the passed item to the list:

   List define Inventory:
      define add(item):
         insert item

With the complex form, add() is more detailed. Questions are raised: 
is the item already in inventory - this might affect or tracking 
feature-? Did I/you make an adjustment to the tracking feature if 
necessary? Note how this is done in the example:	

list define Inventory:
   list define itemNames
      keyedList define qtys
      define add(item):
         if item is in me.itemNames       #do I exist?
            then me.qtys.key(item.name) = +1
         if item is not in me.itemNames   #I don't exist
            then me.qtys.key.add(item.name)
            me.qtys.key(item.name) = +1 

Remove items
The remove() function is really the opposite of add(). Find the item 
passed and remove it from the list. Let's examin it with our two 
pseudo codes.

Of course, this example doesn't take into consideration language 
dependent behavior (C - did you fix your pointers?). Thus the 
abstraction is the same for add():

   List define Inventory:
      define remove(item):
         delete item

Here, as in the complex add() function, more work needs accomplished. 
We not only find the item, but we make certain our tracking feature 
is adjusted accordingly.

   list define Inventory:
      list define itemNames
      keyedList define qtys
      define remove(item):
      if item is in me.itemNames
         then me.qtys.key(item.name) = -1
         if me.qtys.key(item.name) == 0
            then me.qtys.delete(item.name)
      if item is not in me.itemNames
         then error

At the completion, our example would show the proper quantity for the 
item. Plus, if we have no more item in inventory, no quantity or 
listing will exist.

Display items (opt.)
This is listed as optional, because you may not code Inventory display 
as part of your Inventory. It may be in your UI code. However, during 
testing, having a simple Inventory Display feature is very useful.

Like always, the simplest way is the list method. Merely iterate the 
list, printing each item: 

   List define Inventory:
      define display():
         For each item in Inventory:
            print item.name

Because of our tracking feature, we need print item and qty. Otherwise 
uncertainty will exist. The only added feature of the complex over 
simple is the header printed "Item         Qty". 

   List define Inventory:
      define display():
         print "Item     Qty"
         for each item in me.itemNames:
            print item, me.qtys.key(item.name)

Possible benefits
For developers using OO style languages (C++, SmallTalk, Java, etc), 
having a simple Inventory Object lets you easily include it in other 
areas of the game. Containers (combinations of Items and Inventory), 
Buildings (Structure and Inventory), and more can be made. Of course 
non-OO languages(C, Pascal, Basic) can use Inventory as functions in 
other parts of the game. The point is: once you define what a basic 
inventory will be in your game, find how to use it in more areas.

An Example
Here is an example inventory, using the Python language. I find Python 
to be a grat language for prototyping. It is easy to spot errors, fix 
them, etc. Then you may easily recode in any other language.

The Item Object - 
class Item:
   def __init__(self, name):	#object constructor
      self.name = name
The Inventory Object -
class Inventory:
   def __init__(self):
      self. NameList = []	#a list of item names
      self.qtys = {}		#a dictionary of item quantities
      self.contents = []	#a list of actual items
   def add(self, itemList = []):
      for item in itemList:
         #Check item is self
         if item is self:
            print 'Cannot store', item,' in itself!'
         if item in self.contents:
            print 'You already have this item!'
            #Check if item is in nameList
            if item.name in self.NameList:
               #merely insert
               self.qtys[item.name] = self.qtys[item.name] + 1
            #otherwise add to nameList
               self.qtys[item.name] = 1
   def remove(self, item):
      #does item exist?
      If item not in self.contents:
         print item.name, 'is not in your inventory'
         self.qtys[item.name] = self.qtys[item.name] - 1
         if self.qtys[item.name] == 0:
            del self.qtys[item.name]
   def display(self):
      #let's show everything!
      Print "Item\tQty"
      for item in self.NameList:
         print item,"\t", self.qtys[item]

More Info
For information about Python, visit: http://www.python.org
Please send all comments, queries, and error notifications to the author.
Written by: Ken Power email: uncle_wiggly@bigfoot.com
Version: 1.0 Date: Oct. 25, 1999
Copyright 2001 Steve Register.