Search code examples
pythonif-statementappendnested-loopsbreak

Why does my nested loop repeat the same data and/or doesn't append all the things I want it to?


So I am trying to simulate an animal hunting and resting. When the animal rests it can either digest or stay the same based on probabilities. When the animal is hunting it can either hunt successfully or not or stay the same based on probabilities. I want the outer nested loop to be the number of animals and the inner loop be the lifespan. The animals has a lower gut limit when the animal is resting if it reaches that level it automatically starts to hunt. The animal also has an upper gut level during hunting and when it reaches it it rests. In addition if the animal reaches a negative state it breaks out of the loop and continues on to the next animal.

I am am trying to run the nested loop and I do not understand what is wrong! Please help me! I noticed when I printed the alist and I looked at the states, there would be weird jumps from like: [0,4,i ] to [1,2,i] and it just skipped over the belly being 4-1! So I want to start of with an initial state then append the list as conditions are met then, I tried to say if the belly reaches a negative I want to record that state then break out and start all over with the next animal .

I also need to count the number of times the animal was successful at hunting, not successful at hunting, times the belly digested, and the number of times the animal stayed the same. I haven't gotten this far yet

Thank you in advance! Sorry if it is confusing! I am not that great at python Here is my code:

import random
initial_state=[0,4,0]#means it is resting, at full belly of 4 these two values also vary 
alist=[initial_state]
died=0
u,s=2,4 #u is the lower belly limit, s is the upper belly limit
a,b,d=1/8,1/3,1/2 #a is the probability for not catching food, b is the probability for getting food,d is the probability for digestion these values also vary 
for i in range (100000):
    digest=random.random()
    hunts=random.random()
    for i in range(1,501):#each animal has a 500 lifespan
        digest=random.random()
        hunts=random.random()
        if alist[i-1][1]==-1:
            died+=1
            break #the animal died
        if alist[i-1][0]==0:#If the animal is resting
            if digest<=d:
                belly=alist[i-1][1]-1
                if belly <= u:
                    alist.append([1,belly,i])
                else:
                    alist.append([0,belly,i])
            if digest>1-d:#the animal remains the same 
                belly=alist[i-1][1]
                alist.append([0,belly,i])
        if alist[i-1][0]==1:#if the animal is hunting
            if alist[i-1][1]>=1:
                if hunts<=a:
                    belly=alist[i-1][1]-1
                    alist.append([1,belly,i])
                if a<hunts<=a+b:
                    belly=alist[i-1][1]+1
                    if belly==s:#if the animal has a full belly it rests
                        alist.append([0,belly,i])
                    else:
                        alist.append([1,belly,i])
                if hunts>a+b:
                    belly=alist[i-1][1]
                    alist.append([alist[i-1][0],belly,i])
            elif alist[i-1][1]==0:#When the belly is empty while hunting
                if hunts<=a:
                    belly=alist[i-1][1]-1
                    alist.append([0,belly,i])
                if a<hunts<=a+b:
                    belly=alist[i-1][1]+1
                    alist.append([1,belly,i])
                if hunts>a+b:
                    belly=alist[i-1][1]
                    alist.append([alist[i-1][0],belly,i])

Heading


Solution

  • I refactored your code massively. Changes include:

    • renaming everything so that we can see more clearly what variables actually represent.
    • getting rid of alist[i-1] since we are always looking at the previous state, -1 index is sufficient.
    • checking for belly and hunt states as high as possible to avoid redundant mentions of alist.
    • not storing i anymore because if we want it we can just use enumerate.
    • fixed the probabilities (you had too many checks, and a + b != 1 when you defined a and b were the probability of not catching and catching food respectively.

    Tested a few times, I did not notice the jump in belly level that you mentionned in the comments.

    
    # Imports.
    import random
    
    # Constants.
    ANIMALS = 3
    ANIMAL_LIFESPAN = 30
    BELLY_EMPTY = 2 # Will always trigger a hunt next cycle.
    BELLY_FULL = 4  # Will always trigger a rest next cycle.
    FOOD_CATCHING_PROBABILITY = 1/3
    DIGESTION_PROBABILITY = 1/2
    INITIAL_STATE = (False, BELLY_FULL) # The animal starts in resting position with a full belly.
    
    # Run the simulation.
    animals_states = {} # I use a dict here to save myself some `append`s.
    death_counter = 0
    
    for n in range(ANIMALS):
        states = animals_states[n] = [INITIAL_STATE]
    
        for _ in range(ANIMAL_LIFESPAN):
            # Grab previous states.
            is_hunting, belly = states[-1]
    
            if is_hunting:
                if random.random() <= FOOD_CATCHING_PROBABILITY:
                    belly += 1
                    hunt_next = False if belly == BELLY_FULL else True # Full belly is triggering a rest on the next cycle.
                else:
                    belly -= 1
                    hunt_next = True
    
            else: # If the animal is resting.
                if random.random() <= DIGESTION_PROBABILITY:
                    belly -= 1
                    hunt_next = True if belly <= BELLY_EMPTY else False # Empty belly is triggering a hunt on the next cycle.
                else: # The animal remains the same.
                    hunt_next = False
    
            states.append((hunt_next, belly))
    
            # The animal starved.
            if belly == 0:
                death_counter += 1
                break
    
    
    
    # For testing purpose.
    for n, states in animals_states.items():
        print(f"\nAnimal {n} states:")
        for is_hunting, belly in states:
            print(is_hunting, belly)
    
    

    Output:

    Animal 0 states:
    False 4
    False 4
    False 3
    True 2
    True 1
    True 2
    True 1
    True 0
    
    Animal 1 states:
    False 4
    False 4
    False 4
    False 3
    False 3
    True 2
    True 1
    True 2
    True 1
    True 0
    
    Animal 2 states:
    False 4
    False 4
    False 3
    True 2
    True 1
    True 0
    True 1
    True 0