我已经创建了一个工具,可以让我计算在我玩的MMORPG游戏中执行某些操作的盈利能力,它更接近于一个快速和肮脏的解决方案,而不是一个整洁的解决方案,因为它只是一个工具,不需要过度的健壮性,但是我认为还有改进的余地。
我想回顾一下所有的方面,首先我将解释它的真正意义,因为某些领域的知识是必需的。
这个工具的重点是29防御等级( DR )的项目作为输入,它是所有的升级项目,最终的目标是生产一个32 DR项目,可以出售相当数量的钱。
该项目附带了0级的升级,对于这些实践,它需要升级到7级,尽管可以在游戏中进行升级,但这里有一个具有特征的表:
当一个项目成功7,它可以转换成一个“32 an晶体”,可以出售。
当一个项目失败时,它将产生1~15个令人眼花缭乱的矿石和100级芯片。如果升级失败是从6到7,它将生产2级100芯片。一旦失败,项目就会丢失。
要增加升级成功,您可以添加500个闪亮水晶从升级4到5,这将增加一倍的成功机会。
我的目标是计算出最赚钱的方法(可能是负利润,也称为亏损),考虑到29 DR项目的价格。
就实现而言,我现在决定让它在控制台上运行,以便于使用,并使用一些硬编码的变量参数。它们类似于游戏中某一物品的价格/价值,因为它依赖于玩家,所以每天都会有所不同。它只是供个人使用,所以我不认为每天重新编译它是坏的。
但在未来,我希望支持多种类型的项目和目标(升级级别),其中以下细节是可变的:
这是我的密码:
public class GE32Tool {
/* Variable data */
private final static int DAZZLING_ORE_PRICE = 120_000;
private final static int CRYSTAL_32DR_PRICE = 45_000_000;
private final static int LVL_100_CHIP_PRICE = 190_000;
private final static int SHINY_CRYSTAL_PRICE = 6_000;
/* Constant data */
private final static int UPGRADE_MAXIMUM = 7;
private final static int SHINY_CRYSTAL_MAXIMUM = 500;
private final static int[] BASE_UPGRADE_COSTS = {
141_000, //to 1
141_000, //to 2
141_000, //to 3
141_000, //to 4
141_000, //to 5
211_500, //to 6
282_000, //to 7
};
private final static float[] BASE_UPGRADE_SUCCESS = {
1, //to 1
1, //to 2
1, //to 3
1, //to 4
0.5f, //to 5
0.25f, //to 6
0.125f, //to 7
};
private final static int[] UPGRADE_FAILURE_CHIPS = {
1, //to 1
1, //to 2
1, //to 3
1, //to 4
1, //to 5
1, //to 6
2, //to 7
};
private final static int UPGRADE_DAZZLING_ORE_MIN = 1;
private final static int UPGRADE_DAZZLING_ORE_MAX = 15;
private final static float UPGRADE_DAZZLING_ORE_AVG = (UPGRADE_DAZZLING_ORE_MIN + UPGRADE_DAZZLING_ORE_MAX) / 2f;
private void calculate() {
Scanner scanner = new Scanner(System.in);
System.out.print("Le Noir price: ");
int leNoirPrice = scanner.nextInt();
System.out.println();
int initialCost = leNoirPrice + Arrays.stream(BASE_UPGRADE_COSTS, 0, 4).sum();
Map<String, Float> costs = new HashMap<>();
addToMap(costs, false, false, false);
addToMap(costs, false, false, true);
addToMap(costs, false, true, false);
addToMap(costs, false, true, true);
addToMap(costs, true, false, false);
addToMap(costs, true, false, true);
addToMap(costs, true, true, false);
addToMap(costs, true, true, true);
Map.Entry<String, Float> bestResult = costs.entrySet().stream()
.sorted(Comparator.comparingDouble(entry -> entry.getValue()))
.findFirst()
.get();
float finalProfit = -(initialCost + bestResult.getValue());
System.out.println(String.format("Expected profit: %.2f", finalProfit));
System.out.println("Using " + bestResult.getKey());
}
private void addToMap(final Map<String, Float> costs, final boolean upgrade5ShinyCrystals, final boolean upgrade6ShinyCrystals, final boolean upgrade7ShinyCrystals) {
costs.put("5=" + upgrade5ShinyCrystals + " / 6=" + upgrade6ShinyCrystals + " / 7=" + upgrade7ShinyCrystals,
calculateUpgradeCosts(
upgradeCostsFor(upgrade5ShinyCrystals, upgrade6ShinyCrystals, upgrade7ShinyCrystals),
upgradeSuccessFor(upgrade5ShinyCrystals, upgrade6ShinyCrystals, upgrade7ShinyCrystals),
5
)
);
}
private float[] upgradeSuccessFor(final boolean upgrade5ShinyCrystals, final boolean upgrade6ShinyCrystals, final boolean upgrade7ShinyCrystals) {
return new float[]{
BASE_UPGRADE_SUCCESS[0],
BASE_UPGRADE_SUCCESS[1],
BASE_UPGRADE_SUCCESS[2],
BASE_UPGRADE_SUCCESS[3],
BASE_UPGRADE_SUCCESS[4] + (upgrade5ShinyCrystals ? BASE_UPGRADE_SUCCESS[4] : 0),
BASE_UPGRADE_SUCCESS[5] + (upgrade6ShinyCrystals ? BASE_UPGRADE_SUCCESS[5] : 0),
BASE_UPGRADE_SUCCESS[6] + (upgrade7ShinyCrystals ? BASE_UPGRADE_SUCCESS[6] : 0),
};
}
private int[] upgradeCostsFor(final boolean upgrade5ShinyCrystals, final boolean upgrade6ShinyCrystals, final boolean upgrade7ShinyCrystals) {
return new int[]{
BASE_UPGRADE_COSTS[0],
BASE_UPGRADE_COSTS[1],
BASE_UPGRADE_COSTS[2],
BASE_UPGRADE_COSTS[3],
BASE_UPGRADE_COSTS[4] + (upgrade5ShinyCrystals ? SHINY_CRYSTAL_MAXIMUM * SHINY_CRYSTAL_PRICE : 0),
BASE_UPGRADE_COSTS[5] + (upgrade6ShinyCrystals ? SHINY_CRYSTAL_MAXIMUM * SHINY_CRYSTAL_PRICE : 0),
BASE_UPGRADE_COSTS[6] + (upgrade7ShinyCrystals ? SHINY_CRYSTAL_MAXIMUM * SHINY_CRYSTAL_PRICE : 0),
};
}
private float calculateUpgradeCosts(final int[] upgradeCosts, final float[] upgradeSuccess, final int upgradeLevel) {
if (upgradeLevel == UPGRADE_MAXIMUM + 1) {
return -CRYSTAL_32DR_PRICE;
}
float successChance = upgradeSuccess[upgradeLevel - 1];
float successCost = successChance *
(upgradeCosts[upgradeLevel - 1] + calculateUpgradeCosts(upgradeCosts, upgradeSuccess, upgradeLevel + 1));
float failureCost = (1f - successChance) *
(upgradeCosts[upgradeLevel - 1] - (UPGRADE_DAZZLING_ORE_AVG * DAZZLING_ORE_PRICE) - (UPGRADE_FAILURE_CHIPS[upgradeLevel - 1] * LVL_100_CHIP_PRICE));
return successCost + failureCost;
}
public static void main(String[] args) {
new GE32Tool().calculate();
}
}(关于可变名称的参考:29个DR项目被称为"Le Noir“装甲)
在测试此应用程序时,您可以使用300,000至1,000,000之间的价格,这是游戏中的常规市场价值。
发布于 2014-07-10 16:54:48
目前,您将来想要抽象的许多细节(因为您对多个项目类型和目标的想法)都被喷在您的代码中。例如,看看有多少地方有像upgrade7ShinyCrystals这样名字的变量。虽然可以使用数组之类的数据结构来避免这种情况,但是您很快就会陷入一种进一步复杂化已经相当复杂的代码的情况。这很可能导致易碎的代码,如果你发现自己在问这样一个问题:“如果我也想升级到比盈利计算中的销售更低的水平,那该怎么办?”你很难得到答案。
因此,我建议采用一种更面向对象的方法。首先考虑如何构造数据和功能,然后再从那里开始。
我建议您在这一点上的基础课程是ItemAtLevel。我称之为它,是为了将它与更一般的概念区分开来,在这个概念中,您可能认为当前的级别只是一个特定的字段。关于某一项的大多数重要信息是每个级别的特定信息,因此它将有用于以下内容的字段:
我还建议有一个方法来获得下一个级别的项目,这将作为一个单一链接的列表。
我不知道您是否会将其用于足够大的项量,还是使用可能的升级路径足够长/复杂的项,从而影响性能。但是,迭代每一个可能的晶体使用组合是不必要的低效,也强加了不必要的限制,你如何构造你的主循环,这可能会使程序更难修改以后。
这是很容易计算是否使用发光晶体在一个项目的水平。条件是:
failureValue*(1-P) + successValue*P < failureValue*(1-2P) + successValue*2P - crystalCost它重新安排如下:
crystalCost < (successValue-failureValue)*P 在这里,成功和失败值是您为每个结果得到的产品的值,而P是失败的概率。
请注意,这仅仅取决于单个级别的特性。因此,您可以单独计算每个级别,是否使用晶体。
因此,综合起来,ItemAtLevel可以有一个boolean shouldUseCrystals()方法,它使用上面的公式来确定它是否应该使用晶体,然后是一个int averageUpgradeProfit(bool useCrystals)方法,它是一个简单的计算预期利润的方法,包括成功和失败的可能性,就像你在代码中所做的那样。
这样,您所需要做的就是为每个级别的项目(或感兴趣的项目)构造ItemAtLevel实例,其余的都非常简单。如果您想知道总预期利润升级到您的最高水平,您可以使用一个循环(或流函数)来汇总每个级别的平均利润。如果您想知道停止升级的最佳级别,那就不难了。
(顺便提一句,我做过的一个假设是,你可以在任何升级级别上买卖商品,并且这个水平会反映在价格中。)我认为如果不是这样的话,我所说的不会有太大的不同,但是会有一些改变)
发布于 2014-07-10 17:18:14
很高兴看到您正在使用Java 8。
calculate(),以便将该价格作为参数。(如果您将所有价格都放在一个文件中,则此建议不适用。)bestResult。无论如何,您可能应该从float切换到double :在这里使用float没有好处,而且由于您必须向数字中添加一个尾随f,这是很烦人的。注意,正如我前面提到的,您应该计算平均值,而不是最大值,但是也有一个方法DoubleStream.average()。addToMap方法。将与此相关的所有内容都封装在内部类中。您应该小心命名:从名称上看,addToMap实际上是做成本计算的,这一点并不明显。Collections而不是原始数组,因为它更干净(更多OO)。但是,如果您遵循我的建议,那么在这一点上就不会有太多原始数组了。https://codereview.stackexchange.com/questions/56661
复制相似问题