2013-05-31 13 views
7

私はゲームを作りたいと思っています。ゲームの開始時に、プレイヤーはモンスターを選ぶでしょう。簡単に維持できる確率アルゴリズムを書くには?

かなりモンスターを選ぶのは簡単です。

// 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]; 
} 

そして不当モンスターを選びます。

// 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(); 
    } 
} 

問題は、私はゲームに新しいモンスターを追加したとき、私はのif-elseを編集する必要があり、ということです。 または、私はGoldMonsterを0.2に勝ち取るチャンスを変更します。すべて0.1を0.2 に変更する必要があります。これは醜いものであり、容易には維持できません。

// 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(); 
    } 
} 

コードが新しいモンスターが追加されたときに容易に維持することができ、モンスターの勝利のチャンスが調整されるように、どのようにこの確率アルゴリズムをリファクタリングすることができますができますか?

+5

文字列 'GSSBBBBBBB'のランダムな位置から文字を選択します。このような文字列は簡単に変更できます。 –

答えて

3

@Egor Skriptunoffが言ったことは基本的には。これは容易に拡大縮小する必要があります。 enumを使用しない場合は、Class<Monster>のコレクションを使用できます。

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); 
} 

あなたはおそらく列挙Monsters複数を作り、そしてあなたはまだモンスタークラスをインスタンス化したい場合、それはClass<? extends Monster>を指す可能性があります。私はちょうど例をより明確にしようとしました。

+0

enumを使用する場合は+1 – monika

+1

これは実際に起こるという兆候はありませんが、1000sで重量を持つ可能性のある1000sのモンスターがあれば、これはかなりのボトルネックになる可能性があります。 – Dukeling

+1

あなたは 'monster.size()'を意味すると思います。 –

1

私は追加されたモンスターごとに増加する総重量を使用します。

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

まだモンスターを追加するときにif文を追加する必要があります。 – Dukeling

+0

@Dukelingいずれにしても、何かを追加しなければならないし、1つの行を維持するのはそれほど多くないと思う。時には、より包括的なソリューションを持っていると、実際に何をしているのかを隠すことができます。 ;) –

+1

私は理想的なのは、コードを変更せずに(つまり実行時に実行できる)モンスターを加えることができる解決策だと思います。[私の答え](http://stackoverflow.com/a/16858940/1711796)が提供できます。 MMOの場合でも、誰もがランタイム中にゲームの更新を行うとは思えません。 – Dukeling

1

これは、Peter's answerに基づいており、より保守性があります。あなたがしなければならないことは、配列に新しいモンスターを追加して総重量に重量を追加することです - これは実行時にあなたが望むなら容易に拡張することができます(したがって、コード変更を気にせずにプログラムを再起動してモンスターを追加します(あなたのプログラムの残りの部分がこれを許可していると仮定します)。

モンスタークラス:

は各モンスターのためint重み変数を持っています。

重みが1,2および7の場合、それぞれの確率は10%、20%および70%になります(100*x/(1+2+7)として計算されます)。

グローバル:

​​

グローバル重みの初期化:

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

のGet-モンスター機能:

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

上記は、各呼び出しの間に新しいインスタンスを返すための怠惰な方法です(恐らく最良の方法ではありません)。正しい方法は恐らくFactory patternです。

関連する問題