11
votes

Comment puis-je placer au hasard plusieurs rectors non-collisions?

Je travaille sur des jeux 2D avec Pygame. J'ai besoin de placer plusieurs objets en même temps de manière aléatoire sans eux intersectant . J'ai essayé quelques méthodes évidentes, mais ils n'ont pas fonctionné.

Méthodes évidentes suivez (en pseudo): xxx

Cette méthode a pris pour toujours.

Autre méthode J'ai essayé: xxx

Cette méthode est renvoyée près des listes vides.

Je traite avec une liste qui est n'importe où de 2 - 20 objets grands. Toute suggestion?

EDIT: Les rectangles sont toutes des tailles différentes différentes.


2 commentaires

Les rectangles sont-elles tailles aléatoires? Tailles prédéfinies? Toute la même taille?


Quelle est la résolution de la toile et de la taille des objets?


7 Réponses :


0
votes
list_of_objects = []
for i in range(20):
    while True:
        new_object = create_object()
        if not any(collides(new_object, x) for x in list_of_objects):
            break
    list_of_objects.append(new_object)
I assume you already have the create_object() and collides() functionsYou may also need to decrease the size of the rects if this loops too many times

4 commentaires

Je ne sais pas pourquoi, mais votre méthode, comme la mienne, des rect à chevauchement générés inexplicablement ... il regarde comme ça devrait fonctionner ... Je ne sais pas ce qui s'est mal passé.


@John, peut-être que leur est un bogue dans votre fonction de collision. Pouvez-vous poster le code pour cela?


J'utilise les objets de pygames records intégrés dans la fonction CollidAderect (autre_rect). Malheureusement, je ne trouve pas le code pour cela, mais je vais essayer d'utiliser une fonction différente et de vous contacter.


J'ai trouvé l'autre fonction de collision et j'ai essayé aussi. Cela n'a pas fonctionné non plus. Le code est ici: Pastebin.com/a8qvc89g



1
votes

Trois idées:

Diminuez la taille de vos objets h3>

La première méthode échoue car frapper une matrice aléatoire de 20 objets non chevauchants est très improbable (en réalité (1-p) ^ 20 code>, où 0

est la probabilité de deux objets en collision). Si vous pouviez radicalement (les commandes de magnitude du drame) diminuer leur taille, cela pourrait aider. P>

Choisissez-les un par un h3>

L'amélioration la plus évidente serait: P>

while len(rectangles)<N:
    new_rectangle=get_random_rectangle()
    for rectangle in rectangles:
        if not any(intersects (rectangle, new_rectangle) for rectangle in rectangles)
            rectangles.add(new_rectangle)


3 commentaires

Vous pouvez utiliser une instruction continuer ou - si vous vous sentez assez aventureux - une application minutieuse du n'importe quel intégré, pour simplifier la logique ici.


Je ne m'attendais pas à ce que vous utilisiez ! ;) Nous pouvons simplement faire si non (...): # Faites l'ajout . De plus, vous n'avez pas besoin des crochets ici: Lisez sur les compréhensions des générateurs. tout accepte les ittérables, pas seulement des séquences. En outre, cela est cassé maintenant car le pour la boucle répète la logique implémentée avec tout . :(


Je ne devrais jamais écrire de code avant 9h00 et deux tasses de café.



0
votes

Avez-vous essayé:

Until there are enough objects:
    create new object
    if it doesn't collide with anything in the list:
        add it to the list


7 commentaires

Vient d'essayer ça. Il a itéré plus de 50 000 fois sans trouver 20 rects. Je suis toujours novice, donc je ne sais pas comment trouver la région d'intersection beaucoup moins le centre de celui-ci! Je suis simplement en utilisant la méthode de Pygames Colliderect () pour dire si ils entrent en collision. Malheureusement, je ne peux pas dire .


Comment choisissez-vous des tailles de rectangle?


@Karl: Désolé, je n'ai pas vu ton commentaire. Sans "@john" je n'ai jamais été informé de cela. Les tailles de rectangle sont aléatoires. J'utilise hasard.Randint (début, fin) pour les obtenir.


@John Oui, si vous choisissez des tailles de rectangle comme celle-ci, alors bien sûr, il sera très difficile d'obtenir 20 rectangles non chevauchants ... Pensez à ce que sera la zone moyenne d'un rectangle donné!


@Karl: hmm tu as raison. Pouvez-vous suggérer un meilleur moyen de choisir des tailles de rectangle?


@John je pourrais faire plusieurs suggestions sans y penser trop, mais je pense vraiment que vous seriez mieux servi en essayant de trouver quelque chose vous-même. :) Je pense que certaines des autres réponses / commentaires mentionnés à cela aussi.


@Karl: D'accord, je vais voir ce que je peux faire. Merci pour l'aide.



0
votes

Un pseudocode alternatif, à ceux déjà mentionnés: xxx

ou quelque chose comme ça.

Mais cela a l'avantage de créer éventuellement de créer des objets plus petits que vous ne vouliez et créer des objets qui se croisent presque (c.-à-d. Touch).

Si c'est une carte pour le joueur de traverser, ils ne pourront toujours pas le traverser parce que leur chemin pourrait être bloqué.


0 commentaires

15
votes

J'ai changé ma réponse un peu pour répondre à votre question de suivi sur le point de savoir s'il pourrait être modifié pour générer au lieu de générer des carrés de non-collision aléatoires des carrés em> plutôt que des rectangles arbitrairement. Je l'ai fait de la manière la plus simple que je puisse travailler, qui devait poster la production rectangulaire de ma réponse originale et transformer son contenu en sous-régions carrées. Je également mis à jour le code de visualisation en option pour montrer les deux types de sortie. Évidemment, ce type de filtrage pourrait être étendu pour faire d'autres choses comme l'instar de chaque rectangle ou de la place légèrement pour les empêcher de se toucher.

Ma réponse évite de faire ce que bon nombre des réponses déjà publiées - qui génèrent des rectangles tout en générant au hasard. rejeter toutes celles qui entrent en collision avec tout déjà créé - parce que cela semble intrinsèquement lent et informatiquement gaspillage. Mon approche se concentre à la place pour générer uniquement ceux qui ne se chevauchent pas en premier lieu. P>

qui doit être fait relativement simple en le transformant en un simple problème de subdivision de surface qui peut être effectué très rapidement. Vous trouverez ci-dessous une implémentation de la manière dont cela peut être fait. Elle commence par un rectangle définissant la limite extérieure qui la divise en quatre petits rectangles ne se chevauchent pas. Cela est accompli en choisissant un point intérieur semi-aléatoire et l'utiliser en même temps que les quatre points d'angle existants du rectangle extérieur pour former les quatre sous-sections. P>

La plupart du lieu de prendre d'action dans le quadsect () code> fonction. Le choix du point intérieur est crucial pour déterminer ce que ressemble à la sortie. Vous pouvez contraindre comme vous le souhaitez, par exemple que la sélection qui se traduirait par des sous-rectangles d'au moins une certaine largeur minimale ou à la hauteur ou pas plus grand que une certaine quantité. Dans le code échantillon dans ma réponse, il est défini comme étant le point central  ± 1 sup> / 3 sub> de la largeur et la hauteur du rectangle extérieur, mais au fond tout point intérieur fonctionnerait Dans une certaine mesure. P>

Étant donné que cet algorithme génère des sous-rectangles très rapidement, il est correct de passer du temps de calcul déterminant le point de division intérieure. P>

Pour visualiser les résultats de cette approche, il y a un code non essentiel à la fin que les utilisations du module PIL code> (Python Imaging Library) pour créer un fichier image affichant les rectangles générés au cours de certaines courses d'essai j'ai fait. p>

Quoi qu'il en soit, voici la dernière version du code et des échantillons de sortie: p>

import random
from random import randint
random.seed()

NUM_RECTS = 20
REGION = Rect(0, 0, 640, 480)

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y

    @staticmethod
    def from_point(other):
        return Point(other.x, other.y)

class Rect(object):
    def __init__(self, x1, y1, x2, y2):
        minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
        miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
        self.min, self.max = Point(minx, miny), Point(maxx, maxy)

    @staticmethod
    def from_points(p1, p2):
        return Rect(p1.x, p1.y, p2.x, p2.y)

    width  = property(lambda self: self.max.x - self.min.x)
    height = property(lambda self: self.max.y - self.min.y)

plus_or_minus = lambda v: v * [-1, 1][(randint(0, 100) % 2)]  # equal chance +/-1

def quadsect(rect, factor):
    """ Subdivide given rectangle into four non-overlapping rectangles.
        'factor' is an integer representing the proportion of the width or
        height the deviatation from the center of the rectangle allowed.
    """
    # pick a point in the interior of given rectangle
    w, h = rect.width, rect.height  # cache properties
    center = Point(rect.min.x + (w // 2), rect.min.y + (h // 2))
    delta_x = plus_or_minus(randint(0, w // factor))
    delta_y = plus_or_minus(randint(0, h // factor))
    interior = Point(center.x + delta_x, center.y + delta_y)

    # create rectangles from the interior point and the corners of the outer one
    return [Rect(interior.x, interior.y, rect.min.x, rect.min.y),
            Rect(interior.x, interior.y, rect.max.x, rect.min.y),
            Rect(interior.x, interior.y, rect.max.x, rect.max.y),
            Rect(interior.x, interior.y, rect.min.x, rect.max.y)]

def square_subregion(rect):
    """ Return a square rectangle centered within the given rectangle """
    w, h = rect.width, rect.height  # cache properties
    if w < h:
        offset = (h - w) // 2
        return Rect(rect.min.x, rect.min.y+offset,
                    rect.max.x, rect.min.y+offset+w)
    else:
        offset = (w - h) // 2
        return Rect(rect.min.x+offset, rect.min.y,
                    rect.min.x+offset+h, rect.max.y)

# call quadsect() until at least the number of rects wanted has been generated
rects = [REGION]   # seed output list
while len(rects) <= NUM_RECTS:
    rects = [subrect for rect in rects
                        for subrect in quadsect(rect, 3)]

random.shuffle(rects)  # mix them up
sample = random.sample(rects, NUM_RECTS)  # select the desired number
print '%d out of the %d rectangles selected' % (NUM_RECTS, len(rects))

#################################################
# extra credit - create an image file showing results

from PIL import Image, ImageDraw

def gray(v): return tuple(int(v*255) for _ in range(3))

BLACK, DARK_GRAY, GRAY = gray(0), gray(.25), gray(.5)
LIGHT_GRAY, WHITE = gray(.75), gray(1)
RED, GREEN, BLUE = (255, 0, 0), (0, 255, 0), (0, 0, 255)
CYAN, MAGENTA, YELLOW = (0, 255, 255), (255, 0, 255), (255, 255, 0)
BACKGR, SQUARE_COLOR, RECT_COLOR = (245, 245, 87), (255, 73, 73), (37, 182, 249)

imgx, imgy = REGION.max.x + 1, REGION.max.y + 1
image = Image.new("RGB", (imgx, imgy), BACKGR)  # create color image
draw = ImageDraw.Draw(image)

def draw_rect(rect, fill=None, outline=WHITE):
    draw.rectangle([(rect.min.x, rect.min.y), (rect.max.x, rect.max.y)],
                   fill=fill, outline=outline)

# first draw outlines of all the non-overlapping rectanges generated
for rect in rects:
    draw_rect(rect, outline=LIGHT_GRAY)

# then draw the random sample of them selected
for rect in sample:
    draw_rect(rect, fill=RECT_COLOR, outline=WHITE)

# and lastly convert those into squares and re-draw them in another color
for rect in sample:
    draw_rect(square_subregion(rect), fill=SQUARE_COLOR, outline=WHITE)

filename = 'square_quadsections.png'
image.save(filename, "PNG")
print repr(filename), 'output image saved'


5 commentaires

+1, grande approche! La visualisation de PIL est une belle touche aussi :)


+1 a l'air bien! Je l'aime bien! Je vais certainement utiliser cela dans l'un de mes projets. Toute chance que vous puissiez trouver une variante qui produit carrés parfaites de tailles aléatoires à des endroits aléatoires?


@John: Eh bien, oui, je suis sûr que je pourrais, bien que cela soit techniquement une nouvelle question.


@Martineau: C'est ce que je me demandais, pensez-vous que je devrais poser une nouvelle question ou deviendrait-il fermé comme une dupe?


@John: HMMM, n'a jamais pensé à la possibilité de dupe. Dites-vous quoi, si vous acceptez ma réponse actuelle tel qu'il est, je vais le mettre à jour pour répondre à votre question suivante.



1
votes

Il y a une approximation très simple à votre problème qui a bien fonctionné pour moi:

  • Définissez une grille. Par exemple, une grille de 100 pixels écrit (x, y) -> (int (x / 100), int (y / 100)). Les éléments de la grille ne se chevauchent pas.
  • soit mettre chaque objet dans une grille différente (au hasard à l'intérieur de la grille aura l'air plus jolie), soit placez de manière aléatoire quelques objets dans chaque grille, si vous pouvez permettre à quelques objets de chevauchement.

    J'ai utilisé ceci pour générer au hasard une carte 2D (Zelda comme). Les images d'objets sont plus petites que <100 * 100>, donc j'ai utilisé la grille de taille <500 * 500> et autorisé pour 1 à 6 objets dans chaque grille.


0 commentaires

0
votes

Dans mon cas, j'ai eu un problème similaire, sauf que j'avais quelques rectangles précontrants à l'intérieur du rectangle global. Donc, de nouveaux rectangles ont dû être placés autour de ceux existants.

J'ai utilisé une approche gourmande:

  • rectangulaire le rectangle global (global): Créez une grille à partir des coordonnées triées X & triées de tous les rectangles jusqu'à présent. Donc, cela vous donnera une grille irrégulière (mais rectangulaire).
  • Pour chaque cellule de grille Calculez la zone, cela vous donne une matrice de zones.
  • utilisez Algorithme Kadanes 2D pour trouver la sous-matrice qui vous donne la zone maximale (= le plus grand rectangle libre)
  • Placez un rectangle aléatoire dans cet espace libre
  • Répéter

    Cela nécessite une conversion de votre espace de coordonnées d'origine vers / depuis l'espace de la grille, mais directement à faire.

    (Notez que courir Kadene directement sur l'original, le rectangle global est à long terme. Aller via une approximation de la grille est assez rapide pour mon application)


0 commentaires