[Edu-sig] simula.run(40) error

Kirby Urner urnerk@qwest.net
Mon, 03 Mar 2003 19:36:30 -0800


At 07:40 PM 3/4/2003 -0500, reavey wrote:
>errors running your simulation program.
>python 2.2

<<SNIP>>

>I like your program.
>beautiful idea. what is this error?
>thanks
>re-v

Yes, I see I made a very common mistake, in my run() loop:

        for i in creatures.values():
            i.randevent()

The problem here is creatures.values() gets run once, and then
i iterates over this snapshot of what's in the creatures
dictionary.

However, in the meantime, randevent(i) may result in the death
and removal of a given creature.  So when i iterates over the
snapshot to the next value, it may no longer exist.

There are several ways to fix this.  One is to do a confirmation
check inside the loop, skipping i.randevent() if the creature is
no longer present:

        for i in creatures.values():
            if i in creatures.values():
                i.randevent()


Another approach is to simply catch the error and go on as if nothing
had happened:

        for i in creatures.values():
            try:  i.randevent()
            except:  pass

I like this 2nd solution better because it doesn't keep trying to
find i in what in principle could be a long list.

This wasn't the only bug however.  Inside the Creature class was the
following:

         if self.stomach == 0:  # 3 strikes and you're out
             print self.name + " starves"
             self.die()  # <--- oops!

But die() is not a method -- should have been self.dies()

Lastly, what's true about this ecosystem is it's predisposed to
kill everyone off.  As written, I don't think you'll ever get
anywhere close to 100 cycles.  To get any new creatures you have
to already have some -- they don't wander in from "off stage" as
it were.  So once everything is dead, there's not going to be
further activity.

Rather than show all the no-activity ticks, it makes more sense
to just quit the simulation once all the creatures are gone:
So my run() method now looks like this:

def run(n):
     setstage()
     for j in range(n):  # time spans
        print "--- Tick %s -------" % j
        for i in creatures.values():
            try:  i.randevent()         # skip the dead
            except:  pass
        if len(creatures.values())==0:  # all dead
            break

Thanks for pointing out the error of my ways.  If you like this
little simulation, you could have some fun tweaking it.  Perhaps if
death were not randomly invoked, such that only being eaten or
starving caused it, maybe along with some dying of old age pegged
to a life clock -- e.g.

if self.lifespan>50:
     self.dies()

-- then creatures would persist long enough to keep this ecosystem
steady state or growing.

Kirby