Java – how to write a probabilistic algorithm that can be easily maintained?
Suppose I want to create a game At the beginning of the game, the player will choose a monster
It's easy to choose monsters fairly
// 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]; }
Choose monsters unfairly
// 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(); } }
The problem is, when I add a new monster to the game, I have to edit if else Or I'll change my chance to win goldmonster to 0.2. I have to change all 0.1 to 0.2. It's ugly and not easy to maintain
// 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(); } }
How can this probabilistic algorithm be refactored so that code can be easily maintained when adding new monsters and adjusting the chances of winning monsters?
Solution
Basically what @ Egor skriptunoff said This should be easy to expand You can use the collection of class < monster > If you don't want to use enumeration
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); }
You may be able to enumerate the monster plural and point it to a class if you also want to instantiate the monster class I just want to make this example clearer