Conditions based on attributes of other objects in BGE

Hello Community
I’d like to ask if somebody might help me with my problem. I try to create in BGE 4 objects. Their color is a random color (Green, Pink, Brown, Blue, Red, Yellow). My problem now: How can I let python know, that two objects shouldn’t have the same color?

Script connected with an always sensor:

import bge
import random


def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner
    
   <b> blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]
    
    colorset = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(colorset)</b>
    
   <i> if solution1_object.color == red:</i>
        own.color = green
    
    
    
main()

The bold part works fine, but the italic part… I don’t know hot to take the color attribute of the cube called “sulution1” into the script.

this can be done in logic brick easily…though tedious if completed, this can be copied and modded for any object! i may tinker with this idea once i go home from work. i know that it can be done. just difficult to say right off hand!


# ./your_module.py
import bge

COLOR_ATTR = "color"
COLORS = [
    (0, 0, 1, 1),
    (0, 1, 0, 1),
    # ...
]

def setRandomColor(controller):
    owner = controller.owner

    available = COLORS.copy()

    for object in owner.scene.objects:
        color = object.get(COLOR_ATTR, None)
        if color and color in available:
            available.remove(color)

    if available:
        owner.color = owner[COLOR_ATTR] = random.choice(available)
    else:
        print("every color was already used")

Maybe

If I understand correctly, all you want is get the color attribute of the solution1 object?

Maybe something like this, in a module controller:


#colors.py

import bge
import random

def main(cont):
    own = cont.owner
    scene= own.scene

    solution1 = scene.objects["solution1"] #object name here
    
    blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]
    
    colorset = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(colorset)
    
    if solution1.color == red:        
        own.color = green

Put that into a module controller

Maybe that’s not what you want. If so, please tell me :wink:

Why not have something that hands them out:


import random

COLOR_MAP = [
    (0, 0, 1, 1),
    (1, 0, 1, 1),
    (1, 1, 0, 1),
   ....
]
random.shuffle(COLOR_MAP)  # Randomize order of the items in the color map

def assign_color(cont):
    cont.owner.color = COLOR_MAP.pop()  # Grab the last item from the color map (and remove it so future objects can't get it)


Need to use in module mode as we are storing the COLOR_MAP at module level. If you are going to be trying to do this multiple times you’ll need something more advanced, as a “restart scene” actuator may start off with the COLOR_MAP being empty.

Or if objects are going to be created/destroyed, you may need to check at runtime:


COLOR_MAP = [
    (0, 0, 1, 1),
    (1, 0, 1, 1),
    (1, 1, 0, 1),
   ....
]
random.shuffle(COLOR_MAP)  # Randomize order of the items in the color map

def assign_color(cont):
    '''Assigns a color from COLOR_MAP to the object calling this script'''
    available_color = get_color()
    if available_color is not None:
        cont.owner.color = available_color
        # Store a direct reference at the color in use so we don't have to compare floats
        cont.owner["__USES_COLOR__"] = col  


def get_available_color():
    '''Iterate through colors until it finds one that is available. If all are in use, it returns None'''
    index = 0
    while index &lt; len(COLOR_MAP) - 1:
        if check_color_available(COLOR_MAP[index]): 
            available_color = COLOR_MAP[index]
            break
        index += 1
    else:
        # No available color was found (the break statement was never hit
        available_color = None
    return available_color


def check_color_available(col):
    '''Checks to see if any objects that have called "assign_color" are using the specified color'''
    for obj in bge.logic.getCurrentScene().objects():
        if obj.get("__USES_COLOR__") is col:
            return False
    return True
    


So I’ll be bit more specific, because I couldn’t figer out how it works and I need the names later to compare colours. (The goeal is to create a master mind game)

Name of cubes: solution1, 2, 3 and 4
They should al have different colors.
I upladed now the blend file.

https://i.stack.imgur.com/xNlDi.png
My scripts for each cube
cube 1

import bge
import random


#I've made different scrips for each cube

def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner

    blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

    color = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(color)


main()

cube2

import bge
import random


#I've made different scrips for each cube

def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner

    solution1 = bge.logic.getCurrentScene().objects['solution1']
    solution2 = bge.logic.getCurrentScene().objects['solution2']
    solution3 = bge.logic.getCurrentScene().objects['solution3']
    solution4 = bge.logic.getCurrentScene().objects['solution4']

    blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

    color = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(color)

    #Part for solution1

    if solution1 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution1 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution1 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)



main()

cube3

import bge
import random


#I've made different scrips for each cube

def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner

    solution1 = bge.logic.getCurrentScene().objects['solution1']
    solution2 = bge.logic.getCurrentScene().objects['solution2']
    solution3 = bge.logic.getCurrentScene().objects['solution3']
    solution4 = bge.logic.getCurrentScene().objects['solution4']

    blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

    color = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(color)

    #Part for solution1

    if solution1 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution1 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution1 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)

    #Part for solution2

    if solution2 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution2 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution2 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)



main()

cube4

import bge
import random


#I've made different scrips for each cube

def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner

    solution1 = bge.logic.getCurrentScene().objects['solution1']
    solution2 = bge.logic.getCurrentScene().objects['solution2']
    solution3 = bge.logic.getCurrentScene().objects['solution3']
    solution4 = bge.logic.getCurrentScene().objects['solution4']

    blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

    color = [blue, green, red, yellow, brown, pink]

    own.color = random.choice(color)

    #Part for solution1

    if solution1 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution1 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution1 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution1 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)

    #Part for solution2

    if solution2 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution2 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution2 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution2 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)

    #Part for solution3

    if solution3 == red:
        color = [blue, green, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution3 == green:
        color = [blue, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution3 == blue:
        color = [green, red, yellow, brown, pink]
        own.color = random.choice(color)

    elif solution3 == yellow:
        color = [blue, red, green, brown, pink]
        own.color = random.choice(color)

    elif solution3 == pink:
        color = [blue, red, yellow, brown, green]
        own.color = random.choice(color)

    elif solution3 == brown:
        own.color = green
        color = [blue, red, yellow, green, pink]
        own.color = random.choice(color)



main()

Attachments

Fix.blend (9.84 MB)

First of all: using “main” makes no sense. (just in case you know any benefit - please let me know ;)).

You problem is that you try to create uniqueness without being unique. You objects do not know what other colors are used already. Therefore it is impossible to determine a scene unique color.

You can either extract the used colors from the scene (by checking all existing objects) or you cache the already used colors at a central storage that all objects know of.

While the first method is saver, the last one is faster (not that you will notify that).

A central storage can be an object of the scene:


storage = scene.objects["Used color cache object"]
usedColors = storage.get("used colors", [] )

usedColor = ... # whatever method you use

usedColor.append( newColor )
storage["used colors"] = usedColors


or you use a Python module:
colorCache.py



usedColors = []


import colorCache

usedColors = colorCache.usedColors
usedColor = ... # whatever method you use

usedColor.append( newColor )

I hope this helps a bit

Uh, hard to understand your codes…


storage = scene.objects["#Can I use here just an object, which is conected with the scipts that determine the colors?"]
usedColors = storage.get("used colors", [#am I supposed to fill in something?] )

usedColor = ... # would be the list?
                    [blue, green, red, yellow, brown, pink] 
                      #I'd define the colors obove with:
                      
blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

usedColor.append( newColor )
storage["used colors"] = usedColors

monster and his ‘main’ debate :slight_smile: I have to poke fun at you over it monster…I know irritates you when people use it :)…although it makes no difference one way or the other.

HMMM, to the script part - I like the Idea

  cont = bge.logic.getCurrentController()
    own = cont.owner

#doesnotwork....!!!    ???

    import random
    import colorCache

    usedColors = colorCache.usedColors
    
    own.color = random.choice(usedColors)
    usedColors.remove(own.color)
    #now I'd like to save the new list in the script


Store script:

# Python script for saving colors

blue = [0,0,1,True]
green = [0,1,0,True]
red = [1,0,0,True]
yellow = [255,255,0,True]
brown = [0.207917,0.105,0.030,True]
pink = [2.55,0.20,1.47,True]

usedColors = [blue, green, red, yellow, brown, pink]

Monster is right, doing:


def main():
    # ...

main()

… makes no sense.

Also:


storage = scene.objects["Used color cache object"]
usedColors = storage.get("used colors", [] )

Can become:


storage = scene.objects["Used color cache object"]
usedColors = storage.attrDict.setdefault("used colors", [])

And then you are directly modifying the list in the storage object. Although I would prefer using a module variable for this, like Monster showed.

Yes and no

You got the storage idea right.

no you do not need to fill the default/fallback list with values. You always start with an empty list.

Unfortunately you mix it with color creation.

Be aware, you need the used colors every time you want to create a new color. But you create a lot of new colors each time this code runs:


blue = [0,0,1,True]
    green = [0,1,0,True]
    red = [1,0,0,True]
    yellow = [255,255,0,True]
    brown = [0.207917,0.105,0.030,True]
    pink = [2.55,0.20,1.47,True]

This is not necessary at that stage. You need exactly one color - a unique one.

When you determined the new color (newColor) you can add it to the storage as you already do.

It seams you want to get a new color out of a set of predefined colors. This can be a list:


blue = [0,0,1,True]
green = [0,1,0,True]
red = [1,0,0,True]
yellow = [255,255,0,True]
brown = [0.207917,0.105,0.030,True]
pink = [2.55,0.20,1.47,True]

availableColors = [blue, green, red, yellow, brown, pink]

or just:
availableColors = [
  [0,0,1,True]
, [0,1,0,True]
, [1,0,0,True]
, [255,255,0,True]
, [0.207917,0.105,0.030,True]
, [2.55,0.20,1.47,True] ]

The result is a list. You need this list before you chose the first color. But you do not need to create it again, as it is already there. Just store it the same way as the used colors (either in an object property or in a module).

example: create and store colors (on any object you like to create all colors):


storage = scene.objects["however you want to name the storage object"]
storage["available colors"] = availableColors # see above

another call (and consecutive calls) can grab the colors (on the object that want to use a new color):


storage = scene.objects["however you want to name the storage object"]
availableColors = storage["available colors"] 

newColor = randomPick(availableColors) # take a method from the previous posts 

usedColors = storage.get("used colors", [] ) # start with empty list
usedColor.append( newColor ) # add first color
storage["used colors"] = usedColors # store the list

# use newColor

The option to choose out of a list provides an additional option to ensure uniqueness. You remove a used color from the list of available colors (which is more or less the inverse). Be aware: Do not create the list of available colors again (unless you want to reset the colors).

Alternative uniqueness:


storage = scene.objects["however you want to name the storage object"]
availableColors = storage["available colors"] 

newColor = randomPick(availableColors) # take a method from the previous posts 
availableColors.remove(newColor) 

# used colors is not necessary anymore

# use newColor

This method is good when you have a known number of colors, while the other option is better when you calculate the colors when you need a new one.

(There are more options but I do not want to make this topic more complex than it already is.)

Btw. the same thing with module as storage:

colorCache.py


availableColors = [
  [0,0,1,True]
, [0,1,0,True]
, [1,0,0,True]
, [255,255,0,True]
, [0.207917,0.105,0.030,True]
, [2.55,0.20,1.47,True] ]


This creates the list of colors the first time this module gets imported by any python code.

setUniqueColor


import bge
import colorCache

controller = bge.logic.getCurrentController()
owner = controller.owner

owner.color = random.choice(colorCache.availableColors)
availableColors.remove(own.color)


How does this work?

When your logic executes the script “setUniqueColor” the first time, it imports colorCache. As it is imported the first time the module will execute it’s module level code (indentation zero). Which creates the variable “availableColors” assigned with the list of colors (just values - no names). Due to this import behavior it is guarantied the variable is present.

After that the script code gets executed (after import). It reads the module variable availableColors. The code picks one color and assigns it to the object color. Then the picked element gets removed from availableColors. No need to write the list back as you are working on the list directly (call by reference).

Any further execution of the script “setUniqueColor” will not execute the initialization of the imported modules again and just continue after the imports (picking elements and setting colors).

Now guess what happens when the list of available colors is empty ;).

Sorry to take this off topic, but ‘back in the day’ I used main to control all my functions when I ran code from just one file/script…and I I mean it has no real impact on performance if people choose to use a main function in this sense…it’s just another name. I’m not the python master that most of you are…but even I use main in scripts that do not use a class but have multiple functions controlled by ‘main’…because it is the main function in those scripts…ofc I tend to do things my own way regardless of what people tell me is standard :)…this might be my downfall one day, but it works for my uses currently :slight_smile:

now on simpler pieces of code there is not much need at all defining functions.

I’ve made two observations:eek::

setUniqueColor - I used the script to find out where it fails

import bge
import random



def main():

    cont = bge.logic.getCurrentController()
    own = cont.owner
    
    availableColors = [[0,1,1,True], [1,0,0,True]]
    

    own.color = random.choice(availableColors)
    
main()

The code written above works

  1. without main, it does not work - seems strange to me…
  2. as soon as I write import colorCache nothing works

colorCache.py - just a script, not connected with sensors or actuators, not even used as controller

 availableColors = [
    [0,0,1,True]
, [0,1,0,True]
, [1,0,0,True]
, [255,255,0,True]
, [0.207917,0.105,0.030,True]
, [2.55,0.20,1.47,True] ]

I don’t get it, nothing works. The goal was just to create 4 cubes. The cubes should all have different random colors.
The main part is in my script, because I used a gamelogic simple reference.

I tryed as well the thing with the storage - There are two opinions:

  1. I’m too stupid to do it correctly
  2. There’re some other reasons why it does not work

I don’t know if you know mastermind. It is a game where you have to guess the right combintaion of colors. The 4 cubes would be the solution of the game. This is the reason why they all should have random colors, because otherways the game gets boring after the first round. The reason why they are not allowed to have the same color is, that it is a rule in the game - Never use 2times the same color.

If I somehow manage to color the four cubes with unique colors, I’ll have to compare the guessed colors (color palette is there and works) with the ‘solution colors’. But this part later… - I have already an Idea to solve it.

Mabe you have some more Ideas how to get to my goal. I’ll upload the .blend file later on if you want me so. I hope it isn’t too annoying for you to help me. If so just let me know.

#Oh yeah, I hope my English isn’t too terrible

here is almost a complete mastermind game in pure python, only player interaction is missing.


import random
pieces = {}

pieces["blue"] = [0,0,1,True]
pieces["green"] = [0,1,0,True]
pieces["red"] = [1,0,0,True]
pieces["yellow"] = [1.000,0.712,0.013,True]
pieces["brown"] = [0.207,0.105,0.030,True]
pieces["pink"] = [2.55,0.20,1.47,True]

class Board:
    def __init__(self):
        self.rows = []
        self.lineup = self.Lineup()
        
              
    def Lineup(self):
        piecelist = list(pieces)
        random.shuffle(piecelist)
        return piecelist[:4]
 
 
    def add_row(self):
        self.rows.append([None,None,None,None])
 
 
    def check(self,row):
        tmp = {}
        out = []
        
        if row == self.lineup:
            return "Correct"
        
        for i,c in enumerate(row):
            if c in self.lineup:
                if row[i] == self.lineup[i]:
                    tmp[c] = "W"
                else:
                    if c not in tmp:
                        tmp[c] = "B"
        
        out = list(tmp.values())
        
        out.sort(reverse=True)
        
        return out
        
                
    def add_piece(self,place=0,color=None):
        self.rows[-1][place] = color


    ## TO DO
    def drawboard(self):
        for row in self.rows:
            out = self.check(row)
            print(row,out)
            
            if out == "Correct":
                break
        
        
board = Board()


## for testing
for i in range(150):
    board.add_row()
    
    lineup = board.Lineup()
    
    for i,color in enumerate(lineup):
        board.add_piece(i,color)
   

        
board.drawboard()


I applied above code to your demo file from post#6.

create a text block called colorGenerator.py


# this is a module 
# it acts as color cache too
import random


#--- module level code 
''' 
This code will be called a single time 
when this module gets accessed the first time
'''


# A pseudo constant that defines the initial colors
POSSIBLE_COLORS = [
      [0,0,1,True]
    , [0,1,0,True]
    , [1,0,0,True]
    , [255,255,0,True]
    , [0.207917,0.105,0.030,True]
    , [2.55,0.20,1.47,True]
]


# A new list of colors 
availableColors = list(POSSIBLE_COLORS)


#--- BGE callable function (has controller as argument)
def setRandomColor(controller):


    owner = controller.owner
    
    pickedColor = random.choice(availableColors)
    availableColors.remove(pickedColor)
    
    owner.color = pickedColor
 

Then change the python controllers. Switch from Script mode to Module mode as this is a module not a script.
In the module field enter colorGenerator.setRandomColor.

That should do what you want.

Its not the name that we criticized, its the fact that when using a Python controller in Script mode, your whole script is the main function already, its fully executed by the controller. This means that instead of doing:


def functionA():
    doA()

def functionB():
    doB()

def main():
    functionB()
    functionA()

main() # meeeeh

… it would make more sense to just do:


def functionA():
    doA()

def functionB():
    doB()

# main part
functionB()
functionA()

… and if you want to be super Pythonic, in case you just want to run the script but could also import it:


def functionA():
    doA()

def functionB():
    doB()

if __name__ == "__main__":
    # only triggers if ran as a script
    # won't trigger if imported as a module
    functionB()
    functionA()

… but now if you switch the Python controller to module mode, having a “main” function makes sense, because the BGE will not look for a script to run, but for a function from your script/module:


def functionA():
    doA()

def functionB():
    doB()

def main(controller):
    functionB()
    functionA()

# "main()" call not needed, as the controller can directly look into the
#  module and trigger the main function, if you set it up so.

edit: Even better, when running scripts each function defined in the script is redefined each time, like doing:


def main():
    
    def functionA():
        print("A")

    functionA()

To better illustrate I made a .blend to show the fact that the function, despite looking the same and doing the same thing each time, is in fact an other object: BGE_Scripts.blend (463 KB) In this exemple, we can see that the function test is redefined each time, because we are handed a different reference: the BGE executes the full script each time, unlike with a module approach where you would load a module once in memory, and then use the defined functions without re-parsing them.

#################################
@Samiro33:

If something related to Python doesn’t work, try to look for the error in the console. If you take the time to read it will display the name of the error, maybe a little message with it, the line where the error occured and the file. 95% of the time the error is rather explicit.

No, I get it :slight_smile: and I agree especially the way things are handled inside the BGE…my point is I have seen no impact on performance for scripts using a ‘main()’ function…if there is it is extremely minimal…I no longer have the need to run a main script inside BGE since I’ve learned a lot more about it…aside from my inventory…it gets pretty complex with quests, quest rewards, items and trading all tied into it…if I were to run all the functions in that piece of code that were not needed every single tic…that would be a major issue…for obvious reasons…I’m sure a lot of you could do a better job at it than I but…it works for me…and I’ve derailed this conversation enough :slight_smile:

Main()
Indeed I mainly argument against the name “main”.

This name serves no purpose. In my opinion a function name should provide at least a little bit of information of what it is supposed to do (I know this is opinion based).

“main()” does not express anything it is a property like little() or nice(). More worse in other context (like Java or C) it has a complete different meaning.

Function within Script mode

Within a script using functions of the script itself results in a complex processing order that does not support a reader to understand what goes on.

A script executes from top to bottom. This includes function definitions. Be aware a function definition creates a function it does not execute the function. Before you can execute a function you need to define it. Therefore any call to a function needs to be after the definition. The result is, that the top level code it behind your function code.

While this is 100% Python processing flow it forces a reader to jump over the functions to read the top level code, which tells him to jump back to the functions again. The top level code is simply not at the begin of the script.

When you do not use function definitions you can simply read the code from top to button without jumping.

This does not mean you should not use functions. They are good and very useful. You can place them inside a module and import the module at the beginning of your top level code. The top level code is still after the imports, but imports are much smaller and less distracting than function definitions.

And here the naming comes back to game. With a descriptive naming a reader can guess what the top level code is doing, without checking the modules. The reader can still look at them to see the specific implementations.

Just some little notes :wink: