0

I've tried running the following code through Spyder and an online IDE, but neither one fully completes the program. It either times out or just refuses to run.

import random
from pprint import pprint

petri_dish = []

class Species:
    def __init__(self,total,name,life,attack,defense,move,location):
        area = 1000
        self.total = 100
        self.name = name
        self.life = self.total - (random.randint(1,100))
        self.attack = self.total - (random.randint(1,100))
        self.defense = self.total - (random.randint(1,100))
        self.move = self.total - (random.randint(1,100))
        self.location = [random.randint(1,area),random.randint(1,area)]
    def relocate(self):
        x_move_add = random.randint(self.location[0], self.location[0] + self.move)
        x_move_minus = random.randint(self.location[0] - self.move,self.location[0])
        y_move_add = random.randint(self.location[1], self.location[1] + self.move)
        y_move_minus = random.randint(self.location[1] - self.move,self.location[1])
        self.location = [random.randint(x_move_minus,x_move_add),random.randint(y_move_minus,y_move_add)]
        for n in range(2):
            if self.location[n] > 1000:
                self.location[n] = 1000
            elif self.location[n] < 0:
                self.location[n] = 0
    def fight(self,enemy):
        while self.life > 0 and enemy.life > 0:
            self.life = (self.life + self.defense) - enemy.attack
            enemy.life = (enemy.life + enemy.defense) - self.attack
        else:
            if self.life > enemy.life:
                print 'Species #' + str(enemy.name) + ' was eaten!'
                self.attack = self.attack + enemy.attack
                self.life = 100
                petri_dish.remove(enemy)
            else:
                print 'Species #' + str(self.name) + ' was eaten.'
                enemy.attack = enemy.attack + self.attack
                enemy.life = 100
                petri_dish.remove(self)
    def target(self):
        for z in petri_dish:
            if z.location != self.location:
                if (z.location[0] in range(self.location[0] - self.move, self.location[0] + self.move)) and (z.location[1] in range(self.location[1] - self.move, self.location[1] + self.move)):
                    self.fight(z)

for n in range(20):
    petri_dish.append(Species(0,n,0,0,0,0,0))

def show():
    for z in petri_dish:
        print z.location,z.move

def move_around():
    for x in petri_dish:
        x.relocate()
        x.target()

while len(petri_dish) > 1:
    move_around()

for x in petri_dish:
    pprint(vars(x))

Any idea as to what's going on? This was working earlier, but now it's broken. As you can probably tell, this program is a really, really simple petri dish simulator populated by some remarkably unintelligent cells.

Bonus Question: Are infinite loops bad for your computer? I've hit a few of them and I don't want to risk harming my machine in any way, shape, or form.

userNaN
  • 506
  • 1
  • 5
  • 13
  • For the bonus question, infinite loops will result in 100% cpu usage (or one core of the cpu). If your computer's fans are clogged, it might result in overheat. However, most modern computers shut down if the cpu gets too hot. – Laur Ivan Oct 02 '13 at 13:55
  • 2
    `(self.life + self.defense) - enemy.attack` - weird fomula. If `defence` is greater than `attack` unit will heal each turn. So, if `self` and `enemy` have defence rate greater than other's attack rate they will always increase health. Infinite loop. – J0HN Oct 02 '13 at 14:07
  • Kind of nitpicky, but in the __init__, `self.location` should be a tuple. Lists are generally used as a homogenous collection (only one type). Tuples should be used when the order inside the tuple matters, such as Cartesian coordinates (x,y). You could even get fancy and use a `namedtuple`(!) which would enable you to do `self.location.x` and `self.location.y`. The tradeoff being that when you need to modify the location you make a new tuple each time. – gregb212 Oct 02 '13 at 14:09
  • If you press CTRL + C during execution, python will tell you in which line it was stopped. For me it is always 29 and 30 where you calculate self.life and enemy.life. Printing them out immediately shows that both are increasing over time. – Milo Oct 02 '13 at 14:14

2 Answers2

2

mostly, it's because your algorithm can generate inputs which aren't constrained using the algorithm you chose.

first, random.randint(1,100) will produce a number between 1 and 100. you are really using 100 - randint(1,100) though, which will occasionally produce 0s. if you get two items with move=0, neither one can actually move to engage the other one, so your loop will never exit. maybe just use self.move = random.randint(1,100) instead etc. (same is true for life and other things).

there are other constraints which are also invalid - take these lines:

self.life = (self.life + self.defense) - enemy.attack
enemy.life = (enemy.life + enemy.defense) - self.attack

there are two issues with this. one, if x.defense > y.attack, you are actually adding life to the object. you probably want to saturate this at the initial value of self.life (or 100 if you really want healing).

second, even if you do that, you can have a case like this: self.attack = 20 self.defense = 30 enemy.attack = 20 enemy.defense = 30

which is basically a pillow fight :) since attack is always less than defense, neither life is actually ever going to go down, and this loop will run forever. you might want to introduce a random element here.

Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
  • One more quick question: Why am I getting an error that `petri_dish.remove(self) ValueError: list.remove(x): x not in list` sometimes? – userNaN Oct 02 '13 at 14:44
  • 1
    almost guaranteed to be because you are modifying the list while iterating over it. see http://stackoverflow.com/questions/1207406/remove-items-from-a-list-while-iterating-in-python?lq=1. basically, this simple algorithm won't work though. you'll either need to change the way you loop, or mark dead enemies for deletion (and ignore on subsequent moves) and actually process deletions after completing a loop. – Corley Brigman Oct 02 '13 at 15:01
0

You should be a bit more specific on what "refuses to run" and "times out" mean.

My understanding would be that "times out" means that you got an infinite loop. Your program runs the risk of executing forever, if cells don't encounter each other to have a fight().

I'd do several changes to the program:

  • move the target() as a normal function with one (or two) parameters. This way, the Species class won't depend on the global population array.
  • do a pprint after each iteration, not at the very end. If the above (cells randomly running around) holds, the pprint at the end won't get to be executed.
  • change the while condition to end either at len() == 1 or after a maximum number of iterations

HTH,

Laur Ivan
  • 4,117
  • 3
  • 38
  • 62