2010-08-20 3 views
1

C'est presque certainement une question très novice, mais étant comme je suis un novice complet, je vais bien avec ça. Pour le dire simplement, j'aimerais savoir comment faire un système de butin dans un jeu simple, où lorsque vous atteignez un certain objectif, vous avez une chance d'obtenir certains objets plus que d'autres. S'il y a des jeux python open-source qui ont ceci, veuillez me les référer.Comment faire un RNG sélectif pour un jeu en Python?

Voici ce que je sais comment faire: étant donné un échantillon [A, B, C, D, E, F], sélectionnez 3 articles.

Ceci est vraiment simple et facile, cependant, que dois-je faire lorsque je souhaite que certains éléments de l'échantillon soient sélectionnés plus souvent que d'autres, par exemple: échantillon donné [A, B, C, D, E, F ] 3 doivent être sélectionnés, sans répétition, mais A doit être sélectionné 30% du temps, B 25%, C 20%, D 15%, E 5%, F 5%. Ou peut-être même mieux, sans limite (ou une limite à distance, par exemple 3-5 articles) sur la quantité sélectionnée, mais chaque élément de l'échantillon peut être sélectionné à un taux différent et sans répétitions, de sorte que Je pourrais faire A 20%, B 20%, C 15%, D 10%, E 2%, F 1%.

Espérons que cela a du sens.

+0

Avec plusieurs éléments et aucune répétition, les chances d'obtenir une valeur seront plus élevées si elle n'a pas été sélectionnée la première fois. Si, par exemple, A est choisi comme premier article, comment aimeriez-vous que cela affecte les pourcentages de B, C, D, E ou F, étant donné qu'ils doivent à nouveau être additionnés à 100? – Omnifarious

+0

Je vais probablement vouloir obtenir une implémentation qui ne les obligeait pas à ajouter jusqu'à 100. Nous verrons - je commence juste mon voyage ici et je n'ai encore rien conceptualisé/mappé - c'est un exercer dans la curiosité pour un futur jeu de texte que je prévois de travailler. Je commence juste à apprendre la programmation. – BrotherGA2

Répondre

1

est ici un moyen facile, paresseux pour le faire.

donné une liste de (item,weight) paires.

loot = [ (A,20), (B,20), (C,15), (D,10), (E,2), (F,1) ] 

Remarque, les poids n'ont rien à ajouter à quoi que ce soit, ils doivent simplement être des nombres entiers.

étape de préparation unique.

choices = [] 
for item, weight in loot: 
    choices.extend([item]*weight) 

Maintenant c'est juste random.choice(choices).

+0

C'est vraiment très simple. Dans le bon sens. Au début, j'ai eu du mal à comprendre comment mettre en œuvre cela, mais une fois que j'ai compris tout ce qui était nécessaire pour que cela fonctionne, j'ai compris. Merci mec! Pas exactement propre, faisant les longues listes, mais cela fonctionne pour ce que j'ai besoin de faire. – BrotherGA2

+0

@ BrotherGA2: "Pas exactement propre, faisant les longues listes"? Cela ressemble à 3 lignes de code pour moi. Qu'est-ce qui "n'est pas vraiment propre"? –

0

pseudocode:

if(random()<0.2)addloot(a); 
if(random()<0.15)addloot(b); 
if(random()<0.1)addloot(c); 
if(random()<0.02)addloot(d); 
if(random()<0.01)addloot(e); 

où au hasard est un nombre aléatoire de 0 à 1. Voici comment cela fonctionne dans tous les MMORPG.

+3

Il serait plus juste (et plus rapide) d'évaluer 'random()' qu'une seule fois. – wallyk

+1

NO. c'est faux. Si l'utilisateur obtient 0.001, il aura toujours tout le butin dans ce cas. Il serait impossible d'obtenir seulement chose rare. – BarsMonster

+0

Mais dans votre cas, chaque fois que l'utilisateur obtient une baisse, il obtient 5 'random()' s. Pourquoi ne pas faire un if/else? Ou essayez-vous de vous assurer qu'ils reçoivent plus d'un article à chaque fois? Comment allez-vous vous assurer qu'ils n'obtiennent que 3 articles? – Stephen

0

Voici une recette bien si vous voulez une gradation lisse de vraisemblances sans faire une liste énorme à l'échantillon de:

class WeightedRandom(random.Random): 
    """All numbers are random, but some are more random than others. 

    Initialise with a weighting curve gamma. gamma=1 is unweighted, >1 returns 
    the lower numbers more often, <1 prefers the higher numbers. 
    """ 
    def __init__(self, gamma): 
     self.gamma= gamma # 1 is unweighted, >1 pushes values downwards 
     random.Random.__init__(self) 
    def random(self): 
     return random.Random.random(self)**self.gamma 

    # Override the standard sample method, whose pool-based 'optimisation' cocks 
    # up weighted sampling. We know result set is small, so no need for dict 
    # lookup either. 
    # 
    def sample(self, population, k): 
     if k>=len(population): 
      return population 
     indexes= [] 
     for _ in range(k): 
      while True: 
       index= int(self.random()*len(population)) 
       if index not in indexes: 
        break 
      indexes.append(index) 
     return [population[index] for index in indexes] 

>>> r= WeightedRandom(0.5) 
>>> r.sample(range(100), 3) 
[86, 98, 81] 
+0

L'utilisation d'une fonction gamma est une idée intéressante. Est-ce que gamma est une loi de puissance? Si ce n'est pas le cas, je proposerais plutôt une distribution par loi de puissance. – Omnifarious

+0

Pour être honnête, cette méthode est un peu partout dans ma tête en ce moment (surtout la terminologie - je suis un enfant en bas âge à la programmation/python). Au fur et à mesure que je progresse un peu dans ma programmation, je vais essayer de relire cela et voir si je comprends ce qui se passe. Votre description semble néanmoins souhaitable. Merci pour le code! – BrotherGA2

1

Vous me secouèrent un peu quand vous Caractérisé cette question comme un « très novice » . Ce n'est pas aussi simple que ça en a l'air, selon le type de comportement que vous voulez. La réponse de BarsMonster est une bonne chose si cela ne vous dérange pas qu'un joueur chanceux puisse gagner tous les objets et qu'un joueur malchanceux puisse repartir sans rien.

Si vous voulez toujours sélectionner un certain nombre d'éléments, alors j'aller avec la méthode de S. Lott de choisir un élément, mais l'utiliser à plusieurs reprises. Si vous ne souhaitez pas autoriser le même élément à être sélectionné plusieurs fois, vous devez supprimer l'élément sélectionné de loot, puis recréer choices entre les sélections. Par exemple (pseudocode très rugueux):

items_won = random.randint(3, 5) 
for i in range(items_won): 
    item_won = s_lott_weighted_selection() 
    inventory.add(item_won) 
    loot.remove(item_won) 
+0

Ouais ... Je ne me suis pas rendu compte que ce n'était pas juste un simple "import aléatoire" et que vous aviez à peu près terminé. La méthode de S.Lott semble assez simple, mais comme vous l'avez dit, elle a le potentiel d'exiger du travail supplémentaire ou de causer des maux de tête. BarsMonster, avec quelques modifications, pourrait être ce que je vais finir par utiliser, si je peux comprendre comment le faire fonctionner. Merci pour l'ajout de inventory/loot.remove. J'ai beaucoup aidé à le comprendre. – BrotherGA2

0

Une alternative à la sélection pondérée de S.Lott.

Attention - code non testé.

import random 

def weighted_selection(weights):  
    """returns an index corresponding to the weight of the item chosen""" 
    total_sum = sum(weights) 
    rnd = random.uniform(0, total_sum) 
    cumulative_sum = 0 
    for (idx, weight) in enumerate(weights): 
     if rnd <= cumulative_sum + weight: 
      return idx 
     cumulative_sum += weight 
    assert(0) # should never get here 

weights = [30, 25, 20, 15, 5, 5] 
# example of choosing 1 - will return value from 0 to 5 
choice = weighted_selection(weights) 

# example of choosing 3 such values without repeats 
choices = [] 
for n in range(3): 
    new_choice = weighted_selection(weights) 
    del weights[new_choice] 
    choices.append(new_choice) 

Vous pouvez envelopper la sélection, sans remplacement code à la fin dans une sorte d'emballage qui assure le nombre de choix uniques que vous faites ne dépasse jamais le nombre d'options disponibles.

Questions connexes