2013-05-31 13 views
7

Przypuśćmy, że chcę stworzyć grę. Na początku gry gracz wybierze potwora.Jak napisać algorytm prawdopodobieństwa, który można łatwo utrzymać?

Łatwo wybrać potwora w odpowiedni sposób.

// get all monsters with equal chance 
public Monster getMonsterFair(){ 
    Monster[] monsters = {new GoldMonster(), new SilverMonster(), new BronzeMonster()}; 
    int winIndex = random.nextInt(monsters.length); 
    return monsters[winIndex]; 
} 

i podnosi potwora niesprawiedliwie.

// get monsters with unequal chance 
public Monster getMonsterUnFair(){ 
    double r = Math.random(); 
    // about 10% to win the gold one 
    if (r < 0.1){ 
     return new GoldMonster(); 
    } 
    // about 30% to winthe silver one 
    else if (r < 0.1 + 0.2){ 
     return new SilverMonster(); 
    } 
    // about 70% to win the bronze one 
    else { 
     return new BronzeMonster(); 
    } 
} 

Problem polega na tym, że po dodaniu nowego potwora do gry, muszę edytować if-else. Lub zmieniam szansę na wygraną GoldMonster na 0,2, muszę zmienić wszystkie 0.1 na 0.2 . Jest brzydki i niełatwo go utrzymać.

// get monsters with unequal change & special monster 
public Monster getMonsterSpecial(){ 
    double r = Math.random(); 
    // about 10% to win the gold one 
    if (r < 0.1){ 
     return new GoldMonster(); 
    } 
    // about 30% to win the silver one 
    else if (r < 0.1 + 0.2){ 
     return new SilverMonster(); 
    } 
    // about 50% to win the special one 
    else if (r < 0.1 + 0.2 + 0.2){ 
     return new SpecialMonster(); 
    } 
    // about 50% to win the bronze one 
    else { 
     return new BronzeMonster(); 
    } 
} 

Jak to algorytm prawdopodobieństwo może być refactored tak że kody mogą być utrzymywane łatwo, gdy nowy potwór dodaje a szanse na wygraną są dostosowane potworów?

+5

Wybierz char z losowej pozycji ciągu 'GSSBBBBBBB'. Taki ciąg jest łatwy do zmiany. –

Odpowiedz

3

Zasadniczo to, co powiedział @Egor Skriptunoff. To powinno łatwo skalować. Możesz użyć kolekcji Class<Monster>, jeśli nie chcesz korzystać z enum.

enum Monster { 
    GOLD(1), 
    SILVER(3), 
    BRONZE(6) // pseudo probabilities 

    private int weight; 
    // constructor etc.. 
} 

public Monster getMonsterSpecial() { 
    List<Monster> monsters = new ArrayList<>(); 

    for(Monster monsterType : Monster.values()) { 
     monsters.addAll(Collections.nCopies(monsterType.getWeight(), monsterType)); 
    } 

    int winIndex = random.nextInt(monsters.length); 
    return monsters.get(winIndex); 
} 

Można być może uczynić enum Monsters mnogiej, i to wskazywać na Class<? extends Monster> jeśli nadal chcesz instancję klasy potworów. Po prostu starałem się, aby przykład był jaśniejszy.

+0

+1 za korzystanie z enum – monika

+1

Nie ma oznak, że tak się faktycznie stanie, ale gdybyś miał tysiące potworów, które mogły mieć ciężary w 1000, może to być wąskim gardłem. – Dukeling

+1

Myślę, że chodziło o 'monster.size()' –

1

Używałbym całkowitej wagi, która rośnie wraz z każdym dodanym potworem.

private final Random rand = new Random(); 

public Monster getMonsterSpecial() { 
    int weight = rand.nextInt(1+2+2+5); 
    if ((weight -= 1) < 0) return new GoldMonster(); 
    if ((weight -= 2) < 0) return new SilverMonster(); 
    if ((weight -= 2) < 0) return new SpecialMonster(); 
    // 50% chance of bronze 
    return new BronzeMonster(); 
} 
+0

Nadal wymaga dodania instrukcji if podczas dodawania potwora. – Dukeling

+0

@Dukeling Tak czy inaczej musisz coś dodać, a jedna linia to nie jest tak dużo do utrzymania, jak sądzę. Czasami mając bardziej wszechstronne rozwiązanie, możesz ukryć to, co naprawdę robi. ;) –

+1

Myślę, że idealnym rozwiązaniem byłoby rozwiązanie, w którym można dodawać potwory bez konieczności zmiany kodu (tak można to zrobić podczas wykonywania), co [moja odpowiedź] (http://stackoverflow.com/a/16858940/1711796) może dostarczyć. Nie chodzi o to, że uważam, że ktoś robi aktualizacje gry podczas uruchamiania, nawet w przypadku gier MMO. – Dukeling

1

Jest to oparte na Peter's answer, po prostu więcej konserwacji. Wszystko, co musisz zrobić, to dodać nowego potwora do tablicy i dodać wagę do całkowitej wagi - może to łatwo zostać przedłużone, aby zdarzyło się w czasie działania, jeśli chcesz (dlatego, nieważne, zmiany kodu, , których nawet nie potrzebujesz aby ponownie uruchomić program, aby dodać potwora (zakładając, że pozwala na to reszta programu)).

klasa potwora:

Masz zmienną int wagi dla każdego potwora.

Jeśli waga wynosi 1,2 i 7, odpowiednie prawdopodobieństwa będą wynosić 10%, 20% i 70% (obliczone jako 100*x/(1+2+7)).

Globals:

Random rand = new Random(); 
int totalMonsterWeight; 
Monster[] monsters; // set this up somewhere 

Globalny inicjalizacji waga:

totalMonsterWeight = 0; 
for (Monster monster: monsters) 
    totalMonsterWeight += monster.getWeight(); 

funkcja Get-potwór:

public Monster getMonster() 
{ 
    int weight = rand.nextInt(totalMonsterWeight); 
    for (Monster monster: monsters) 
    if ((weight -= monster.getWeight()) < 0) 
     return monster.getClass().newInstance(); 
} 

Powyższe jest leniwym sposobem (prawdopodobnie nie najlepszym sposobem) na zwrócenie nowej instancji podczas każdego połączenia. Właściwa droga to prawdopodobnie użycie Factory pattern.