首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用于计算游戏中某些项目的盈利能力的工具

用于计算游戏中某些项目的盈利能力的工具
EN

Code Review用户
提问于 2014-07-10 15:35:23
回答 2查看 686关注 0票数 15

我已经创建了一个工具,可以让我计算在我玩的MMORPG游戏中执行某些操作的盈利能力,它更接近于一个快速和肮脏的解决方案,而不是一个整洁的解决方案,因为它只是一个工具,不需要过度的健壮性,但是我认为还有改进的余地。

我想回顾一下所有的方面,首先我将解释它的真正意义,因为某些领域的知识是必需的。

领域知识

这个工具的重点是29防御等级( DR )的项目作为输入,它是所有的升级项目,最终的目标是生产一个32 DR项目,可以出售相当数量的钱。

该项目附带了0级的升级,对于这些实践,它需要升级到7级,尽管可以在游戏中进行升级,但这里有一个具有特征的表:

  • 从0升级到1:花费141,000,100%的常规机会
  • 从1升级到2:成本141,000,100%的常规机会
  • 从2升级到3:成本141,000,100%的常规机会
  • 从3升级到4:成本141,000,100%的常规机会
  • 从4升级到5:费用141,000,50%的常规机会
  • 从5升级到6:成本211,500,25%的常规机会
  • 从6升级到7:成本282,000,12.5%的常规机会

当一个项目成功7,它可以转换成一个“32 an晶体”,可以出售。

当一个项目失败时,它将产生1~15个令人眼花缭乱的矿石和100级芯片。如果升级失败是从6到7,它将生产2级100芯片。一旦失败,项目就会丢失。

要增加升级成功,您可以添加500个闪亮水晶从升级4到5,这将增加一倍的成功机会。

我的目标是计算出最赚钱的方法(可能是负利润,也称为亏损),考虑到29 DR项目的价格。

就实现而言,我现在决定让它在控制台上运行,以便于使用,并使用一些硬编码的变量参数。它们类似于游戏中某一物品的价格/价值,因为它依赖于玩家,所以每天都会有所不同。它只是供个人使用,所以我不认为每天重新编译它是坏的。

但在未来,我希望支持多种类型的项目和目标(升级级别),其中以下细节是可变的:

  • 升级成本
  • 升级所需的最大数量的闪亮晶体
  • 该项目在失败时产生的眩目矿石的最小和最大数量。

这是我的密码:

代码语言:javascript
复制
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之间的价格,这是游戏中的常规市场价值。

EN

回答 2

Code Review用户

发布于 2014-07-10 16:54:48

结构

目前,您将来想要抽象的许多细节(因为您对多个项目类型和目标的想法)都被喷在您的代码中。例如,看看有多少地方有像upgrade7ShinyCrystals这样名字的变量。虽然可以使用数组之类的数据结构来避免这种情况,但是您很快就会陷入一种进一步复杂化已经相当复杂的代码的情况。这很可能导致易碎的代码,如果你发现自己在问这样一个问题:“如果我也想升级到比盈利计算中的销售更低的水平,那该怎么办?”你很难得到答案。

因此,我建议采用一种更面向对象的方法。首先考虑如何构造数据和功能,然后再从那里开始。

我建议您在这一点上的基础课程是ItemAtLevel。我称之为它,是为了将它与更一般的概念区分开来,在这个概念中,您可能认为当前的级别只是一个特定的字段。关于某一项的大多数重要信息是每个级别的特定信息,因此它将有用于以下内容的字段:

  • 价值
  • 是否是最高级别
  • 成功提升到下一级的可能性
  • 是否可以使用闪亮的晶体
  • 升级失败时生成的项的值。

我还建议有一个方法来获得下一个级别的项目,这将作为一个单一链接的列表。

算法

我不知道您是否会将其用于足够大的项量,还是使用可能的升级路径足够长/复杂的项,从而影响性能。但是,迭代每一个可能的晶体使用组合是不必要的低效,也强加了不必要的限制,你如何构造你的主循环,这可能会使程序更难修改以后。

这是很容易计算是否使用发光晶体在一个项目的水平。条件是:

代码语言:javascript
复制
failureValue*(1-P) + successValue*P < failureValue*(1-2P) + successValue*2P - crystalCost

它重新安排如下:

代码语言:javascript
复制
crystalCost < (successValue-failureValue)*P 

在这里,成功和失败值是您为每个结果得到的产品的值,而P是失败的概率。

请注意,这仅仅取决于单个级别的特性。因此,您可以单独计算每个级别,是否使用晶体。

把它放在一起

因此,综合起来,ItemAtLevel可以有一个boolean shouldUseCrystals()方法,它使用上面的公式来确定它是否应该使用晶体,然后是一个int averageUpgradeProfit(bool useCrystals)方法,它是一个简单的计算预期利润的方法,包括成功和失败的可能性,就像你在代码中所做的那样。

这样,您所需要做的就是为每个级别的项目(或感兴趣的项目)构造ItemAtLevel实例,其余的都非常简单。如果您想知道总预期利润升级到您的最高水平,您可以使用一个循环(或流函数)来汇总每个级别的平均利润。如果您想知道停止升级的最佳级别,那就不难了。

(顺便提一句,我做过的一个假设是,你可以在任何升级级别上买卖商品,并且这个水平会反映在价格中。)我认为如果不是这样的话,我所说的不会有太大的不同,但是会有一些改变)

票数 7
EN

Code Review用户

发布于 2014-07-10 17:18:14

很高兴看到您正在使用Java 8。

  • 关于四种硬编码价格,理想情况下,您可以通过一些公共api连接到游戏服务器以获得这些值。否则,将它们写入文件就更有意义了。此外,目前还不清楚为什么"Le“价格与其他四种价格不同。
  • 将向用户询问"Le“价格的代码放在一个单独的方法中会更干净。您还可以更改calculate(),以便将该价格作为参数。(如果您将所有价格都放在一个文件中,则此建议不适用。)
  • 你的代码让ENUM尖叫。在大小为7的数组中定义不同数量的地方很多,相反,您应该有一个带有七个转换的枚举,其中每个枚举值包含您在散乱数组中定义的所有值。您可以在一个枚举中放置许多不同的成员和方法。见教程
  • 在您的代码中有一个“逻辑错误”:您不应该测试所有8个布尔三重奏组合。当某一项无法升级时,您将不再尝试以后的升级。
  • 存在一个“统计错误”:您不希望将最大值与初始成本进行比较,而是将平均值进行比较。如果平均结果大于初始成本,则尝试升级,否则就不会。
  • 我想你在计算DoubleStream.max()时是在找bestResult。无论如何,您可能应该从float切换到double :在这里使用float没有好处,而且由于您必须向数字中添加一个尾随f,这是很烦人的。注意,正如我前面提到的,您应该计算平均值,而不是最大值,但是也有一个方法DoubleStream.average()
  • 我真的不喜欢addToMap方法。将与此相关的所有内容都封装在内部类中。您应该小心命名:从名称上看,addToMap实际上是做成本计算的,这一点并不明显。
  • 使用Collections而不是原始数组,因为它更干净(更多OO)。但是,如果您遵循我的建议,那么在这一点上就不会有太多原始数组了。
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/56661

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档