Supposed I want to create a game. At the start of the game, the player will pick a monster.
It's easy to picks the monster 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];
}
And picks the monster 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 that, when I add a new monster to the game, I have to edit the if-else. Or I change the chance of winning GoldMonster to 0.2, I have to change all 0.1 into 0.2 .It's ugly, and not easily maintained.
// 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 probability algorithm can be refactored so that the codes can be maintained easily when new monster is added and the chances of winning monsters are adjusted?
Basically what @Egor Skriptunoff said. This should scale easily. You could use a collection of Class<Monster>
if you didn't want to use an 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);
}
You could perhaps make the enum Monsters
plural, and have it point to a Class<? extends Monster>
if you still want to instantiate monster classes. I just tried to make the example clearer.
I would uses a total weight which increases with each monster added.
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With