pythonarrayssimulation

How can I prevent direction-biased sorting while moving values in a grid of tiles?


I am attempting to make a simple, flowing dynamic across a grid of tiles for a game. Essentially, I want water values of cells in the grid to flow to one adjacent cell each frame according to a couple rules.

Pls consult image below. I ran out of words trying to explain and include code. Context_img_bcIranoutofwords

Below is the flow function I am calling on the tiles. I apologize I know it's excessive to write it out like this, and if you have a way of consolidating I would love to hear it! I mainly did this to assure that I could see every single step because I cannot figure out why program is behaving how it is.

The question is: Is there something specifically about iterating through a list of lists like this that would cause the flow function to preferentially flow to the east? I have tried my best in the function to force a fair decision between the east and west tiles. And if it's not that, is there something else in this function that could be causing this?

(I have double checked that the adjacency functions are working as they should. So I know it has nothing to do with that.)

In the function "self.BLANK_adj" is a list assigned when finding adjacent squares. "self.BLANK_adj[0]" = the beta tile object. "self.BLANK_adj[_1]" = a check that is True if the adjacent square being looked

    def gravity_water_flow_nl_2(self):
        #self._adj [0] = beta tile
        #self._adj [1] = greater alt check

        flow_tile = self
        if self.water > FLOW:
            
            #S exists 
            if self.S_adj != None:

                #S not tall
                if self.S_adj[1] != True:

                    #S not full
                    if self.S_adj[0].water < (100-FLOW):
                        flow_tile = self.S_adj[0]

                    #S Full
                    else:

                        #S full, W exists
                        if self.W_adj != None:

                            #W not tall
                            if self.W_adj[1] != True:

                                #W not full
                                if self.W_adj[0].water < (100 - FLOW):

                                    #Check E
                                    #E exists
                                    if self.E_adj != None:

                                        #E tall
                                        if self.E_adj[1] == True:

                                            flow_tile = self.W_adj[0]
                                        
                                        #E not tall
                                        else:

                                            #E not full
                                            if self.E_adj[0].water <= (100 - FLOW):

                                                #E less water than self
                                                if self.E_adj[0].water < self.water:

                                                    #W less water than self
                                                    if self.W_adj[0].water < self.water:

                                                        if self.E_adj[0].water > self.W_adj[0].water:
                                                            flow_tile = self.W_adj[0]
                                                        elif self.E_adj[0].water < self.W_adj[0].water:
                                                            flow_tile = self.E_adj[0]
                                                        else:
                                                            flow_tile = random.choice([self.E_adj[0],self.W_adj[0]])
                                                    
                                                    #W equal or more than self
                                                    else:
                                                        flow_tile = self.E_adj[0]

                                                #E equal or more than self
                                                else:
                                                    flow_tile = self.W_adj[0]

                                    #E doesn't exist
                                    else:
                                        flow_tile = self.W_adj[0]
                                
                                #W full
                                else:

                                    #E exists
                                    if self.E_adj != None:

                                        #E not tall
                                        if self.E_adj[1] != True:

                                            #E not full
                                            if self.E_adj[0].water < (100-FLOW):
                                                flow_tile = self.E_adj
                                            
                                            #E full
                                            else:
                                                #N exists
                                                if self.N_adj != None:

                                                    #N not tall
                                                    if self.N_adj[1] != True:
                                                        
                                                        #N not full
                                                        if self.N_adj[0].water < (100 - FLOW):
                                                            flow_tile = self.N_adj
                                                    
                                        #E tall
                                        else:

                                            #N exists
                                            if self.N_adj != None:

                                                #N not tall
                                                if self.N_adj[1] != True:
                                                    
                                                    #N not full
                                                    if self.N_adj[0].water < (100 - FLOW):
                                                        flow_tile = self.N_adj      

                                    #E doesn't exist
                                    else:

                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj                   

                            #W tall
                            else:

                                #E exists
                                if self.E_adj != None:

                                    #E not tall
                                    if self.E_adj[1] != True:

                                        #E not full
                                        if self.E_adj[0].water < (100-FLOW):
                                            flow_tile = self.E_adj

                                        #E full
                                        else:
                                            #N exists
                                            if self.N_adj != None:

                                                #N not tall
                                                if self.N_adj[1] != True:
                                                    
                                                    #N not full
                                                    if self.N_adj[0].water < (100 - FLOW):
                                                        flow_tile = self.N_adj
                                                
                                    #E tall
                                    else:

                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj      

                                #E doesn't exist
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj  

                        #S full, W doesn't exist
                        else:

                            #E exists
                            if self.E_adj != None:

                                #E not tall
                                if self.E_adj[1] != True:

                                    #E not full
                                    if self.E_adj[0].water < (100-FLOW):
                                        flow_tile = self.E_adj
                                    #E full
                                    else:
                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj
                                            
                                #E tall
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj      

                            #E doesn't exist
                            else:

                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj  

                #S tall
                else:

                    #W exists
                    if self.W_adj != None:

                        #W not tall
                        if self.W_adj[1] != True:

                            #W not full
                            if self.W_adj[0].water < (100 - FLOW):

                                #Check E
                                #E exists
                                if self.E_adj != None:

                                    #E tall
                                    if self.E_adj[1] == True:

                                        flow_tile = self.W_adj[0]
                                    
                                    #E not tall
                                    else:

                                        #E not full
                                        if self.E_adj[0].water < (100 - FLOW):

                                            #E less water than self
                                            if self.E_adj[0].water < self.water:

                                                #W less water than self
                                                if self.W_adj[0].water < self.water:

                                                    if self.E_adj[0].water > self.W_adj[0].water:
                                                        flow_tile = self.W_adj[0]
                                                    elif self.E_adj[0].water < self.W_adj[0].water:
                                                        flow_tile = self.E_adj[0]
                                                    else:
                                                        flow_tile = random.choice([self.E_adj[0],self.W_adj[0]])
                                                
                                                #W equal or more than self
                                                else:
                                                    flow_tile = self.E_adj[0]

                                            #E equal or more than self
                                            else:
                                                flow_tile = self.W_adj[0]

                                #E doesn't exist
                                else:
                                    flow_tile = self.W_adj[0]
                            
                            #W full
                            else:

                                #E exists
                                if self.E_adj != None:

                                    #E not tall
                                    if self.E_adj[1] != True:

                                        #E not full
                                        if self.E_adj[0].water < (100-FLOW):
                                            flow_tile = self.E_adj
                                        #E full
                                        else:
                                            #N exists
                                            if self.N_adj != None:

                                                #N not tall
                                                if self.N_adj[1] != True:
                                                    
                                                    #N not full
                                                    if self.N_adj[0].water < (100 - FLOW):
                                                        flow_tile = self.N_adj
                                                
                                    #E tall
                                    else:

                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj      

                                #E doesn't exist
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj                   

                        #W tall
                        else:

                            #E exists
                            if self.E_adj != None:

                                #E not tall
                                if self.E_adj[1] != True:

                                    #E not full
                                    if self.E_adj[0].water < (100-FLOW):
                                        flow_tile = self.E_adj
                                    #E full
                                    else:
                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj
                                            
                                #E tall
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj      

                            #E doesn't exist
                            else:

                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj                             
                    
                    #W doesn't exist
                    else:

                        #E exists
                            if self.E_adj != None:

                                #E not tall
                                if self.E_adj[1] != True:

                                    #E not full
                                    if self.E_adj[0].water < (100-FLOW):
                                        flow_tile = self.E_adj
                                    #E full
                                    else:
                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj
                                            
                                #E tall
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj      

                            #E doesn't exist
                            else:

                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj

            #S doesn't exist
            else:

                #W exists
                if self.W_adj != None:

                    #W not tall
                    if self.W_adj[1] != True:

                        #W not full
                        if self.W_adj[0].water < (100 - FLOW):

                            #Check E
                            #E exists
                            if self.E_adj != None:

                                #E tall
                                if self.E_adj[1] == True:

                                    flow_tile = self.W_adj[0]
                                
                                #E not tall
                                else:

                                    #E not full
                                    if self.E_adj[0].water < (100 - FLOW):

                                        #E less water than self
                                        if self.E_adj[0].water < self.water:

                                            #W less water than self
                                            if self.W_adj[0].water < self.water:

                                                if self.E_adj[0].water > self.W_adj[0].water:
                                                    flow_tile = self.W_adj[0]
                                                elif self.E_adj[0].water < self.W_adj[0].water:
                                                    flow_tile = self.E_adj[0]
                                                else:
                                                    flow_tile = random.choice([self.E_adj[0],self.W_adj[0]])
                                            
                                            #W equal or more than self
                                            else:
                                                flow_tile = self.E_adj[0]

                                        #E equal or more than self
                                        else:
                                            flow_tile = self.W_adj[0]


                            #E doesn't exist
                            else:
                                flow_tile = self.W_adj[0]
                        
                        #W full
                        else:

                            #E exists
                            if self.E_adj != None:

                                #E not tall
                                if self.E_adj[1] != True:

                                    #E not full
                                    if self.E_adj[0].water < (100-FLOW):
                                        flow_tile = self.E_adj
                                    #E full
                                    else:
                                        #N exists
                                        if self.N_adj != None:

                                            #N not tall
                                            if self.N_adj[1] != True:
                                                
                                                #N not full
                                                if self.N_adj[0].water < (100 - FLOW):
                                                    flow_tile = self.N_adj
                                            
                                #E tall
                                else:

                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj      

                            #E doesn't exist
                            else:

                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj                   

                    #W tall
                    else:

                        #E exists
                        if self.E_adj != None:

                            #E not tall
                            if self.E_adj[1] != True:

                                #E not full
                                if self.E_adj[0].water < (100-FLOW):
                                    flow_tile = self.E_adj
                                #E full
                                else:
                                    #N exists
                                    if self.N_adj != None:

                                        #N not tall
                                        if self.N_adj[1] != True:
                                            
                                            #N not full
                                            if self.N_adj[0].water < (100 - FLOW):
                                                flow_tile = self.N_adj
                                        
                            #E tall
                            else:

                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj      

                        #E doesn't exist
                        else:

                            #N exists
                            if self.N_adj != None:

                                #N not tall
                                if self.N_adj[1] != True:
                                    
                                    #N not full
                                    if self.N_adj[0].water < (100 - FLOW):
                                        flow_tile = self.N_adj

                #W doesn't exist 
                else:       
                    #E exists
                    if self.E_adj != None:

                        #E not tall
                        if self.E_adj[1] != True:

                            #E not full
                            if self.E_adj[0].water < (100-FLOW):
                                flow_tile = self.E_adj
                            #E full
                            else:
                                #N exists
                                if self.N_adj != None:

                                    #N not tall
                                    if self.N_adj[1] != True:
                                        
                                        #N not full
                                        if self.N_adj[0].water < (100 - FLOW):
                                            flow_tile = self.N_adj
                                    
                        #E tall
                        else:

                            #N exists
                            if self.N_adj != None:

                                #N not tall
                                if self.N_adj[1] != True:
                                    
                                    #N not full
                                    if self.N_adj[0].water < (100 - FLOW):
                                        flow_tile = self.N_adj      

                    #E doesn't exist
                    else:

                        #N exists
                        if self.N_adj != None:

                            #N not tall
                            if self.N_adj[1] != True:
                                
                                #N not full
                                if self.N_adj[0].water < (100 - FLOW):
                                    flow_tile = self.N_adj

Solution

  • I suggest that you split the logic into helper function for each direction then you'll collect all eligible direction then perform random.choice() on it

    direction = []
    ## Check condition for each direction
    ## you can update values based on it
    if condition_for_W: 
        direction.append(W_adj[0])
    if condition_for_E:
        direction.append(E_adj[0])
    if condition_for_N:
        direction.append(N_adj[0])
    if condition_for_S:
        direction.append(S_adj[0])
    
    if direction:
        flow_tile = random.choice(direction)
    

    Try this:

    import random
    
    FLOW = 10 # Min
    
    class Tile:
        def __init__(self, water=0, tall=False):
            self.water = water
            self.tall = tall
            self.N_adj = None
            self.S_adj = None
            self.E_adj = None
            self.W_adj = None
    
        def can_flow_to(self, adj):
            # True and False and True
            return adj and not adj[1] and adj[0].water < (100 - FLOW)
    
        def water_flow(self):
            flow_tile = self
    # S -> W -> E(E tall = W)
            if self.water <= FLOW:
                return flow_tile 
    
            # flow South immediately
            if self.can_flow_to(self.S_adj):
                return self.S_adj[0]
    
            # east and west options
            options = []
            if self.can_flow_to(self.W_adj):
                options.append(self.W_adj[0])
            if self.can_flow_to(self.E_adj):
                options.append(self.E_adj[0])
    
            # Random
            if self.W_adj and self.E_adj:
                W_ok = self.W_adj[0] in options
                E_ok = self.E_adj[0] in options
                if W_ok and E_ok:
                    W_water = self.W_adj[0].water
                    E_water = self.E_adj[0].water
    
                    if W_water < self.water or E_water < self.water:
                        if W_water < E_water:
                            return self.W_adj[0]
                        elif E_water < W_water:
                            return self.E_adj[0]
                        else:
                            return random.choice([self.E_adj[0], self.W_adj[0]])
                    else:
                        return self.W_adj[0]  # default
    
            if options:
                return random.choice(options)
    
            # Check North
            if self.can_flow_to(self.N_adj):
                return self.N_adj[0]
    
            return flow_tile
            
    def test_flow():
        for i, case in enumerate([case1 ,case2, case3, case4, case5]):
            result_tile = case()
            print(f"Case {i+1}: flows to ->",result_tile)
    
    
    def case1():
        center = Tile(water=80)
        south  = Tile(water=30, tall=False) 
        center.S_adj = [south, False]
        res = center.water_flow()
        res = center.water_flow()
        return result(center=center, res=res)
    
    def case2():
        center = Tile(water=80)
        south  = Tile(water=95, tall=False)  
        east   = Tile(water=25, tall=False)
        west   = Tile(water=10, tall=False)
        center.S_adj = [south, False]
        center.E_adj = [east, False]
        center.W_adj = [west, False]
        res = center.water_flow()
        return result(center=center, res=res)
    
    def result(center, res):
        if center.N_adj != None:
            if res == center.N_adj[0]:
                return "North"
        if center.S_adj != None:
            if res == center.S_adj[0]:
                return "South"
        if center.E_adj != None:
            if res == center.E_adj[0]:
                return "east"
        if center.W_adj != None:
            if res == center.W_adj[0]:
                return "West"
        else:
            return "Center"
        
    def case3():
        center = Tile(water=80)
        west   = Tile(water=20, tall=False)
        east   = Tile(water=20, tall=False)
        center.W_adj = [west, False]
        center.E_adj = [east, False]
        res = center.water_flow()
        return result(center=center, res=res)
    
    def case4():
        center = Tile(water=80)
        south  = Tile(water=20, tall=True)    
        west   = Tile(water=20, tall=True)    
        east   = Tile(water=15, tall=False)   
        center.S_adj = [south, True]
        center.W_adj = [west, True]
        center.E_adj = [east, False]
        res = center.water_flow()
        return result(center=center, res=res)
    def case5():
        center = Tile(water=80)
        south  = Tile(water=90, tall=True)
        east   = Tile(water=100, tall=False)
        west   = Tile(water=100, tall=False)
        north  = Tile(water=10, tall=False)
        center.S_adj = [south, True]
        center.E_adj = [east, False]
        center.W_adj = [west, False]
        center.N_adj = [north, False]
        res = center.water_flow()
        return result(center=center, res=res)
    
    test_flow()
    

    It should work