Quadrillage de jeu: liste en python

Fermé
Seyyko_ - 26 oct. 2023 à 22:39
BoBot Messages postés 4137 Date d'inscription mardi 4 juillet 2023 Statut Modérateur Dernière intervention 26 novembre 2024 - 4 nov. 2023 à 12:09

Bonjour, en ce moment je travaille sur un jeu python (interface terminal), et je souhaite crée des niveaux, comme l'image ci-dessous :

dans un premier temps j'ai pensé a faire des listes, mais j'ai vite bloqué sur les chemins (représenté en gris sur l'image), la question est la suivante, comment je peux relier un point A à un point B aléatoirement en python (en utilisant une liste si possible).

Voici un exemple :

L'idée ici est de relier les points D (début) et F (Fin) aléatoirement.

Merci encore de votre temps.
Windows / Opera 102.0.0.0

2 réponses

mamiemando Messages postés 33380 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 26 novembre 2024 7 802
Modifié le 30 oct. 2023 à 02:31

Bonjour,

Malheureusement il n'y a pas de manière intelligente de faire un tel tirage : dans le cas général, le nombre de chemin dans un graphe (ici il s'agit d'une grille 2D, c'est un cas particulier) entre deux sommets croit exponentiellement avec le nombre d'arcs (ici, les transitions entre deux cases). Ici il n'y a aucun critère pour privilégier un chemin plus qu'un autre et guider la recherche.

Du coup, sans plus de précision, la seule approche que je vois pour le moment consiste à  faire un tirage aléatoire par rapport à la position courante C récursif, (où C est initialisée D) permettant de choisir vers quelle case S se déplacer.

  • on interdit toute case S voisine d'une case déjà à l'état de chemin autre que C (cela évite des culs de sacs, des carrefours et des boucles)
  • si F fait partie des case S atteignable, on se jette dessus, car on a alors trouvé un chemin qui connecte D à S : on stoppe la récursion et on retourne le chemin trouvé (critère d'arrêt)
  • si aucune direction n'est possible depuis la case C, alors la trajectoire en cours de construction s'est enfermée et on ne peut plus atteindre F (famine) : on stoppe la récursion et on recommence le tirage aléatoire.

Pour faire cette recherche aléatoire, on peut faire un algorithme de branchement :

  • À chaque étape, on branche sur l'une des directions admissibles et on choisit aléatoirement une branche dans laquelle plonger.
  • On arrête l'exploration d'une branche en cas de rejet, ou si l'on a trouvé une configuration qui atteint le critère d'arrêt.

Voici à quoi ça peut ressembler (j'utilise numpy pour avoir un code plus concis et plus lisible mais on peut s'en sortir avec des listes de listes).

#!/usr/bin/env python3

import numpy as np
import random


GROUND = 0
PATH = 1


def neighbors(a: np.array, c: tuple) -> list:
    (i, j) = c
    (m, n) = a.shape
    return [
        (i + d, j)
        for d in (-1, 1)
        if 0 <= i + d < m
    ] + [
        (i, j + d)
        for d in (-1, 1)
        if 0 <= j + d < n
    ]


def is_valid_neighbor(a: np.array, c: tuple, s: tuple) -> bool:
    if a[s] == PATH:
        return False
    for s_ in neighbors(a, s):
        if a[s_] == PATH and s_ != c:
            return False
    return True


def valid_neighbors(a: np.array, c: tuple) -> list:
    return [
        s
        for s in neighbors(a, c)
        if is_valid_neighbor(a, c, s)
    ]


def branch(a: np.array, c: tuple, f: tuple) -> (np.array, bool):
    neighborhood = valid_neighbors(a, c)
    if neighborhood:
        if f in neighborhood:
            a[f] = PATH  # Critère d'arrêt
            return (a, True)
        else:
            random.shuffle(neighborhood)
            for s in neighborhood:
                a_ = a.copy()  # Important !
                a_[s] = PATH
                (a_, found) = branch(a_, s, f)
                if found:
                    return (a_, found)
    return (None, False)  # Famine


def on_border(shape: tuple, c: tuple) -> bool:
    return (
        c[0] in {0, shape[0] - 1} or
        c[1] in {0, shape[1] - 1}
    )


def find_path(shape: tuple, d: tuple, f: tuple) -> np.array:
    assert len(shape) == 2      # Grille 2D
    assert (0, 0) <= d < shape  # La case D est dans la grille
    assert (0, 0) <= f < shape  # La case F est dans la grille
    assert d != f               # Les cases D et F sont distinctes
    assert on_border(shape, d)  # La case D est sur un bord
    assert on_border(shape, f)  # La case F est sur un bord
    a = np.zeros(shape, dtype=np.uint8)
    a[d] = PATH
    (a_, found) = branch(a, d, f)
    assert found  # Il y a toujours une solution
    return a_


def main():
    shape = (3, 4)  # Matrice 3x4 
    d = (0, 0)      # Case de départ: en haut à gauche
    f = (2, 3)      # Case d'arrivée: 2e ligne (en bas), 3e colonne
    for _ in range(5):
        print(find_path(shape, d, f))
        print()

main()

Ce qui donne (sortie aléatoire) :

[[1 0 0 0]
 [1 1 1 1]
 [0 0 0 1]]

[[1 0 0 0]
 [1 1 0 0]
 [0 1 1 1]]

[[1 1 1 0]
 [0 0 1 1]
 [0 0 0 1]]

[[1 0 0 0]
 [1 1 1 0]
 [0 0 1 1]]

[[1 1 0 0]
 [0 1 1 0]
 [0 0 1 1]]

Bonne chance

2
BoBot Messages postés 4137 Date d'inscription mardi 4 juillet 2023 Statut Modérateur Dernière intervention 26 novembre 2024 272
4 nov. 2023 à 12:09
Bonjour !
L'IA qui aime les défis, est là !

Il est possible de réaliser cela grâce à un algorithme de recherche de chemin, comme l'algorithme de Dijkstra ou l'algorithme A*. Ces algorithmes sont capables de trouver le chemin le plus court entre deux points dans un graphe. Dans votre cas, vous pouvez considérer chaque case de votre plateforme de jeu comme un nœud de votre graphe.

Voici une très courte explication de comment vous pouvez implémenter cela en Python:

1. Créez une liste 2D d'objets "case", où chaque case a une position (x, y), un booléen "estObstacle" et peut-être d'autres informations si nécessaires.

2. Choisissez un point de départ et un point d'arrivée.

3. Utilisez un algorithme de recherche de chemin pour trouver le chemin le plus court entre ces deux points : chaque itération de votre algorithme doit choisir la case non visitée avec la plus petite distance depuis le départ, puis mettre à jour les distances de ses voisins.

Cette liste 2D peut alors être utilisée pour afficher vos niveaux, et le chemin peut être mis en évidence.

Il existe de nombreuses ressources en ligne qui peuvent vous aider à comprendre et à implémenter ces algorithmes.

0