Item aus Liste ausgeben mit bestimmter Wahrscheinlichkeit

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Hey,
ich wollte eine Art "Lootbox" machen. Es gibt drei Kategorien: Normal, Selten, Sehr selten. Jede Kategorie soll eine bestimmte Wahrscheinlichkeit haben. Ich glaube numpy.random.choice, allerdings bekomme ich einen Fehler und weiß nicht wieso.

Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "lootbox.py", line 15, in <module>
    gegenstand = choice(gegenstände[choice(list(gegenstände.keys()))], p=wahrscheinlichkeiten.values())
  File "mtrand.pyx", line 1135, in mtrand.RandomState.choice
ValueError: object of too small depth for desired array

Code:

Code: Alles auswählen

from numpy.random import choice

gegenstände = {"Normal":["Lederhelm", "Lederhose", "Lederschuhe"],
               "Selten":["Eisenhelm", "Eisenhose", "Eisenschuhe"],
               "Sehr selten":["Stahlhelm", "Stahlhose", "Stahlschuhe"]}

wahrscheinlichkeiten = {"Normal":0.5,
                        "Selten":0.35,
                        "Sehr selten":0.15}

gegenstand = choice(gegenstände[choice(list(gegenstände.keys()))], p=wahrscheinlichkeiten.values())


print(gegenstand)
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Du hast übersehen, dass du auch aus wahrscheinlichkeiten.values() eine Liste machen musst.

Abgesehen davon hast du dich in deinen Ebenen verlaufen. Du wendest die Wahrscheinlichkeit nicht auf die Schlüssel der Gegenstände ("Normal", "Selten", "Sehr selten") an, sondern auf den ermittelten Wert. Das merkst du spätestens dann, wenn du jeder Liste noch einen Wert hinzufügst. Mach das ganze häppchenweise, dann liest es sich leichter.

Code: Alles auswählen

    from numpy.random import choice

    things = {
        'Normal': ['Lederhelm', 'Lederhose', 'Lederschuhe'],
        'Selten': ['Eisenhelm', 'Eisenhose', 'Eisenschuhe'],
        'Sehr selten': ['Stahlhelm', 'Stahlhose', 'Stahlschuhe']
    }

    probabilities = {
        'Normal': 0.5,
        'Selten': 0.35,
        'Sehr selten': 0.15
    }

    group = choice(list(things.keys()), p=list(probabilities.values()))
    thing = choice(things[group])

    print(thing)
Im Endeffekt würde man wohl auch nur ein Dictionary verwenden um dort sowohl die Wertelisten wie auch die Wahrscheinlichkeiten unterzubringen.
Zuletzt geändert von /me am Donnerstag 27. Juni 2019, 10:03, insgesamt 1-mal geändert.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Okay, jetzt funktioniert es. Scheint aber so als würden die Wahrscheinlichkeit keinen Einfluss haben.


#edit:

habe es gelöst, hatte die Klammern falsch gesetzt

Code: Alles auswählen

from numpy.random import choice

gegenstände = {"Normal":["Lederhelm", "Lederhose", "Lederschuhe"],
               "Selten":["Eisenhelm", "Eisenhose", "Eisenschuhe"],
               "Sehr selten":["Stahlhelm", "Stahlhose", "Stahlschuhe"]}

wahrscheinlichkeiten = {"Normal":0.6,
                        "Selten":0.3,
                        "Sehr selten":0.1}





gegenstand = choice(gegenstände[choice(list(gegenstände.keys()), p=list(wahrscheinlichkeiten.values()))])


print(gegenstand)
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich finde, zumindest für diesen Ausschnitt, `numpy` eine ziemlich heftige Abhängigkeit, wenn man doch `random.choices()` aus der Standardbibliothek nehmen könnte.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Aber random.choices kann keine relativen Wahrscheinlichkeiten. Das kann man sich natuerlich selbst bauen, aber eine Zeile "pip install numpy" ... ;)
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@__deets__: Doch `random.choices()` hat gewichtete Wahrscheinlichkeiten:

Code: Alles auswählen

In [122]: random.choices?
Signature: random.choices(population, weights=None, *, cum_weights=None, k=1)
Docstring:
Return a k sized list of population elements chosen with replacement.

If the relative weights or cumulative weights are not specified,
the selections are made with equal probability.
File:      /usr/lib/python3.6/random.py
Type:      method
Nicht zu verwechseln mit `random.choice()` ohne `s` am Ende.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Habe jetzt die Items mit der Anzahl wie oft man Sie hat in ein dict geladen. Möchte nun aber die Ausgabe nach der Rarität sortieren, also Normal -> Selten -> Sehr selten. Ich weiß, dass ich dafür sorted und einen key brauche, allerdings weiß ich nicht wie ich den key für das sorted machen soll.

Code: Alles auswählen

        for key, value in sorted(inventar_mit_anzahl.items()):
            print(f"{key} {value}")
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Oh, wieder was gelernt. Nett.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Jankie: die key-Funktion bekommt das Item. Das ist bei dir (key, value). Und sie muss den Wert nach dem sortiert werden soll liefern. Das ist die relative Wahrscheinlichkeit. Die kannst du also nachschlagen mit dem key.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

@__deets__:
Sorry aber das verstehe ich nicht so ganz. Kannst du mir bitte ein Beispiel zeigen?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Code: Alles auswählen

sorted(dinge, key=lambda item: probabilities[item[0]])
Benutzeravatar
__blackjack__
User
Beiträge: 14044
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei man sich eventuell vielleicht noch Gedanken machen möchte nach was als nächstes sortiert werden soll innerhalb der Raritätengruppen, oder ob das egal ist.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

ich bekomme es nicht hin mit

Code: Alles auswählen

        for key, value in sorted(inventar_mit_anzahl.items(), key=lambda item: wahrscheinlichkeiten[item[0]])
            print(f"{key} {value}")


@__blackjack__:

Ist es möglich die Reihenfolge genau so zu machen wie sie in

Code: Alles auswählen

gegenstände = {"Normal":["Lederhelm", "Lederhose", "Lederschuhe"],
               "Selten":["Eisenhelm", "Eisenhose", "Eisenschuhe"],
               "Sehr selten":["Stahlhelm", "Stahlhose", "Stahlschuhe"],
               "Legendär":["Diamanthelm", "Diamanthose", "Diamantschuhe"]}
steht, also dass zu erst der Helm kommt, dann Hose, Schuhe....
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es ging BlackJack da denke ich um etwas anderes. Wenn du mehrere items mit der gleichen Rarität hast, wie werden die untereinander sortiert?

So oder so halte ich deinen Ansatz für wenig gelungen. Das ruft nach einer objektorientierten Modellierung, und da ist Rarität eines von vielen Kriterien. Die Wahrscheinlichkeit ist ja eher beim Kauf/Fund relevant.

Und wenn es nicht geht, zeig bitte den gesamten Code und ggf Fehlermeldungen.
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

@__deets__:

Ja das meinte ich auch, also wenn man zum Beispiel nur Ledersachen hat, dass diese dann in der Reihenfolge dargestellt werden, wie sie in der value des dictionarys vorkommen. Das wäre mein Wunsch.

Hier mal der komplette Code:

Code: Alles auswählen

from numpy.random import choice
from collections import defaultdict

gegenstände = {"Normal":["Lederhelm", "Lederhose", "Lederschuhe"],
               "Selten":["Eisenhelm", "Eisenhose", "Eisenschuhe"],
               "Sehr selten":["Stahlhelm", "Stahlhose", "Stahlschuhe"],
               "Legendär":["Diamanthelm", "Diamanthose", "Diamantschuhe"]}

wahrscheinlichkeiten = {"Normal":0.55,
                        "Selten":0.3,
                        "Sehr selten":0.1,
                        "Legendär":0.05}




class Spieler:
    def __init__(self):
        self.inventar = []
        self.guthaben = 10

    def gebe_x_zufällige_items(self,anzahl):
        if self.guthaben > 0 and (self.guthaben - anzahl) >= 0:
            self.guthaben -= anzahl
            for _ in range(0, anzahl):
                gegenstand = choice(gegenstände[choice(list(gegenstände.keys()), p=list(wahrscheinlichkeiten.values()))])
                self.inventar.append(gegenstand)
                print(f"Du öffnest eine Kiste und bekommst {gegenstand}!")
        else:
            print(f"Nicht genug Guthaben, du hast nur {self.guthaben} Guthaben.")

    def zeige_inventar(self):
        inventar_mit_anzahl = defaultdict(int)
        for item in self.inventar:
            inventar_mit_anzahl[item] += 1
        for key, value in sorted(inventar_mit_anzahl.items(), key=item: wahrscheinlichkeiten[item[0]]):
            print(f"{key:<{max(map(len, gegenstände))}}     {value}")


def main():
    spieler = Spieler()
    while True:
        auswahl = int(input("""
Was möchtest du tun?

1 - Kisten öffnen
2 - Inventar anzeigen

>"""))
        if auswahl == 1:
            anzahl_kisten = int(input("Wie viele Kisten willst du öffnen?: "))
            spieler.gebe_x_zufällige_items(anzahl_kisten)
        elif auswahl == 2:
            spieler.zeige_inventar()

main()

und die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Users\xxx\Desktop\lootbox.py", line 56, in <module>
    main()
  File "C:\Users\xxx\Desktop\lootbox.py", line 54, in main
    spieler.zeige_inventar()
  File "C:\Users\xxx\Desktop\lootbox.py", line 36, in zeige_inventar
    for key, value in sorted(inventar_mit_anzahl.items(), key=lambda item: wahrs
cheinlichkeiten[item[0]]):
  File "C:\Users\xxx\Desktop\lootbox.py", line 36, in <lambda>
    for key, value in sorted(inventar_mit_anzahl.items(), key=lambda item: wahrs
cheinlichkeiten[item[0]]):
KeyError: 'Lederschuhe'

Hier mal ein Beispiel wie das ganze ca. dargestellt werden soll (nach 100 Kisten geöffnet habe ich folgende Anzahl:

Code: Alles auswählen

Diamanthelm     03
Diamanthose     02
Eisenhelm       09
Eisenhose       11
Eisenschuhe     14
Lederhelm       18
Lederhose       18
Lederschuhe     20
Stahlhelm       01
Stahlhose       01
Stahlschuhe     03

und würde es gerne hinbekommen, dass es am Ende so aussieht:

Code: Alles auswählen

Lederhelm       18
Lederhose       18
Lederschuhe     20
Eisenhelm       09
Eisenhose       11
Eisenschuhe     14
Stahlhelm       01
Stahlhose       01
Stahlschuhe     03
Diamanthelm     03
Diamanthose     02
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist genau das was ich ansprach: du sortierst eben nicht loot Boxen, sondern beliebig viel Krempel, der in deinem inventory rumliegt. Und dazu musst du anders vorgehen: du musst im inventory Objekte haben. Die haben eben Rarität als Eigenschaft, und danach kannst du sortieren. Doch sie können und müssen noch viele andere Eigenschaften haben: wie stark schützen denn die Lederschuhe? Welchen attackwert hat ein Schwert? Etc.
Antworten