I'm trying to use the p5.js library to create a game inspired be The Witness

from p5 import *


# DÉFINITION DES NIVEAUX

LEVELS = [
    {
        "grid": (4, 4),          # taille de la grille : 4 colonnes x 4 lignes
        "start": (0, 0),         # nœud de départ
        "end": (4, 4),           # nœud d'arrivée
        "blocked": [(1,1),(2,1)],# cases interdites
        "symbols": [(0,2,'circle'), (3,1,'circle')] # symboles à visiter avant l'arrivée
    },
    {
        "grid": (5, 5),
        "start": (0, 4),
        "end": (4, 0),
        "blocked": [(2,2),(1,3)],
        "symbols": [(0,2,'circle'), (2,4,'circle')]
    },
    {
        "grid": (6, 6),
        "start": (0, 0),
        "end": (5, 5),
        "blocked": [(2,2),(3,3),(1,4)],
        "symbols": [(1,1,'circle'), (4,4,'circle')]
    }
]


# CONFIGURATION VISUELLE

CELL = 80       # taille d'une cellule
MARGIN = 40     # marge autour de la grille pour le dessin
SNAP_RADIUS = CELL / 1.5  # rayon pour “attraper” le nœud avec le clic

# VARIABLES DU JEU

current_level = 0   # niveau actuel
path = []           # liste des nœuds visités pour le chemin
lines_drawn = set() # lignes déjà tracées (pour ne pas repasser dessus)
game_finished = False

# FONCTIONS UTILITAIRES


def node_position(node):
    """
    Convertit un nœud (col, row) en coordonnées pixels pour l'affichage
    """
    x, y = node
    return MARGIN + x * CELL, MARGIN + y * CELL

def distance(x1, y1, x2, y2):
    """
    Calcul de la distance euclidienne entre deux points
    """
    return ((x1-x2)**2 + (y1-y2)**2)**0.5

def snap_to_node(mx, my, grid):
    """
    Retourne le nœud le plus proche du clic
    """
    closest = None
    min_dist = SNAP_RADIUS
    cols, rows = grid
    for x in range(cols+1):
        for y in range(rows+1):
            nx, ny = node_position((x, y))
            d = distance(mx, my, nx, ny)
            if d <= min_dist:
                closest = (x, y)
                min_dist = d
    return closest

def is_adjacent(a, b):
    """
    Vérifie si deux nœuds sont adjacents horizontalement ou verticalement
    """
    return abs(a[0]-b[0]) + abs(a[1]-b[1]) == 1

def node_blocked(node):
    """
    Vérifie si un nœud est dans la liste des blocs interdits
    """
    x, y = node
    return (x, y) in LEVELS[current_level].get('blocked', [])

def line_used(a, b):
    """
    Vérifie si une ligne entre deux nœuds a déjà été tracée
    """
    return ((a,b) in lines_drawn) or ((b,a) in lines_drawn)


# CONFIGURATION DE LA FENÊTRE

def setup():
    """
    Initialisation du canvas en fonction de la taille de la grille
    """
    cols, rows = LEVELS[current_level]["grid"]
    createCanvas(2*MARGIN + cols*CELL, 2*MARGIN + rows*CELL)
    noLoop()  # dessin manuel uniquement (rafraîchissement avec redraw())



def draw_grid():
    """
    Dessine la grille principale
    """
    cols, rows = LEVELS[current_level]["grid"]
    stroke(80)
    for x in range(cols+1):
        line(MARGIN+x*CELL, MARGIN, MARGIN+x*CELL, MARGIN+rows*CELL)
    for y in range(rows+1):
        line(MARGIN, MARGIN+y*CELL, MARGIN+cols*CELL, MARGIN+y*CELL)

def draw_blocks():
    """
    Dessine les cases bloquées
    """
    fill(0)
    noStroke()
    for bx, by in LEVELS[current_level].get('blocked', []):
        rect(MARGIN+bx*CELL, MARGIN+by*CELL, CELL, CELL)

def draw_symbols():
    """
    Dessine les symboles (ex: cercles) que le joueur doit visiter
    """
    symbols = LEVELS[current_level].get('symbols', [])
    strokeWeight(8)
    for x, y, kind in symbols:
        nx, ny = node_position((x, y))
        if kind=='circle':
            stroke(0,0,255)
            noFill()
            ellipse(nx, ny, 20, 20)
    strokeWeight(1)

def draw_points():
    """
    Dessine les points de départ (vert) et d'arrivée (rouge)
    """
    sx, sy = LEVELS[current_level]["start"]
    ex, ey = LEVELS[current_level]["end"]
    strokeWeight(10)
    stroke(0,255,0)
    px, py = node_position((sx, sy))
    point(px, py)
    stroke(255,0,0)
    px, py = node_position((ex, ey))
    point(px, py)
    strokeWeight(1)

def draw_path():
    """
    Dessine le chemin jaune tracé par le joueur
    """
    stroke(255,255,0)
    strokeWeight(6)
    for i in range(len(path)-1):
        a = path[i]
        b = path[i+1]
        x1, y1 = node_position(a)
        x2, y2 = node_position(b)
        line(x1, y1, x2, y2)
    strokeWeight(1)


# INTERACTION : CLICS

# DESSIN

def draw():
    """
    Dessine la grille, les blocs, les symboles, les points de départ/arrivée et le chemin
    """
    background(30)
    global path, lines_drawn, current_level, game_finished
    if game_finished:
        fill(255)
        textSize(32)
        textAlign(CENTER, CENTER)
        text("JEU TERMINÉ", width/2, height/2)
        return

    draw_grid()
    draw_blocks()
    draw_symbols()
    draw_path()
    draw_points()
    
    if mouseIsPressed:
         """
    Chaque clic fait progresser le chemin :
    - si clic sur départ et chemin vide : commence le chemin
    - si clic sur un nœud adjacent valide : ajoute au chemin
    - si clic sur l'arrivée : vérifie symboles et termine le niveau
    """

    if game_finished: 
        return

    # détecte le nœud le plus proche du clic
    node = snap_to_node(mouseX, mouseY, LEVELS[current_level]["grid"])
    if node is None: 
        return

    # si début du chemin
    if not path and node == LEVELS[current_level]["start"]:
        path.append(node)
        lines_drawn = set()
        redraw()
        return

    # si on a déjà commencé le chemin
    elif path:
        last = path[-1]

        # clic sur l'arrivée
        if node == LEVELS[current_level]["end"]:
            # vérifier que tous les symboles ont été visités
            symbols = LEVELS[current_level].get('symbols', [])
            visited = set(path)
            all_symbols = all((x,y) in visited for x,y,_ in symbols)
            if not all_symbols:
                print("Vous devez passer sur tous les symboles !")
                return
            # niveau terminé : passer au niveau suivant
            current_level += 1
            path = []
            lines_drawn = set()
            if current_level >= len(LEVELS):
                game_finished = True
            redraw()
            return

        # clic sur nœud adjacent valide
        elif is_adjacent(last, node) and not node_blocked(node) and not line_used(last, node):
            path.append(node)
            lines_drawn.add((last, node))
            redraw()
run()

it doesn’t work, and I don’t know why… can anyone help me ?

The script does not appear to have the function run() defined anywhere. run() is not a built-in keyword that automatically runs your script. You have to define it as part of your script.

Did you mean: draw()

It works when I use it, the game is shown, but the interaction with the mouse doesn’t work