假设我想制作一款游戏。在游戏开始时,玩家将挑选一个怪物。
公平地挑选怪物很容易。
// 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();
}
}
如何重构这种概率算法,以便在添加新怪物时能够轻松维护代码并调整获胜怪物的机会?
答案 0 :(得分:3)
基本上是@Egor Skriptunoff所说的。这应该很容易扩展。如果您不想使用Class<Monster>
,则可以使用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);
}
如果您仍想要实例化怪物类,也许可以使枚举Monsters
复数,并指向Class<? extends Monster>
。我只是想让这个例子更清楚。
答案 1 :(得分: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();
}
答案 2 :(得分:1)
这基于Peter's answer,更易于维护。你所要做的就是在阵列中添加一个新的怪物并将重量加到总重量上 - 如果你愿意的话,可以很容易地将其扩展到运行期间(因此,不要介意更改代码,你不要甚至需要重新启动程序来添加怪物(假设你的程序的其余部分允许这样做))。
怪物类:
为每个怪物提供int
权重变量。
如果权重为1,2和7,则相应概率分别为10%,20%和70%(按100*x/(1+2+7)
计算)。
<强>全局:强>
Random rand = new Random();
int totalMonsterWeight;
Monster[] monsters; // set this up somewhere
全局权重初始化:
totalMonsterWeight = 0;
for (Monster monster: monsters)
totalMonsterWeight += monster.getWeight();
获取怪物功能
public Monster getMonster()
{
int weight = rand.nextInt(totalMonsterWeight);
for (Monster monster: monsters)
if ((weight -= monster.getWeight()) < 0)
return monster.getClass().newInstance();
}
以上是在每次调用期间返回新实例的一种懒惰方式(可能不是最佳方式)。正确的方法可能是使用Factory pattern。