简体   繁体   English

OptaPlanner:如何从 JSON 读取游戏装甲数据并根据重量 + 统计数据找到最佳装甲集?

[英]OptaPlanner: How to read game armor data from JSON and find optimal set of armor based on weight + stats?

Elden Ring is a hit game that has some interesting theorycrafting behind it. Elden Ring是一款热门游戏,其背后有一些有趣的理论。

There are hundreds of armor pieces, weapons, and spells.有数百件盔甲、武器和法术。 Finding optimal combinations of them based on player & item stats is an interesting practical problem.根据玩家和物品统计数据找到它们的最佳组合是一个有趣的实际问题。

I've always wanted to learn how to use Constraint Solvers and it seems a good usecase exists!我一直想学习如何使用约束求解器,而且似乎存在一个很好的用例!


Goal:目标:

  • Given a list of all armor in the game in JSON format给出游戏中所有盔甲的列表,格式为 JSON
  • Find the set of armor (head, chest, legs, arms) that has:找到具有以下特征的盔甲(头部、胸部、腿部、手臂):
    • The highest POISE and PHYSICAL_DEFENSE最高的 POISEPHYSICAL_DEFENSE
    • For the lowest WEIGHT对于最低的重量

Here is the repo:这是回购协议:

My attempt so far:到目前为止我的尝试:

Update更新

I managed to solve it after advice below我在下面的建议后设法解决了它

The trick was to change to use PlanningEntityProperty :诀窍是更改为使用PlanningEntityProperty

@PlanningSolution
public class ArmorSetComboPlanningSolution {

    public List<ArmorPiece> armorPieces;
    public Map<Integer, List<ArmorPiece>> armorByType;

    @ValueRangeProvider(id = "headRange")
    @ProblemFactCollectionProperty
    public List<ArmorPiece> headList;

    @ValueRangeProvider(id = "chestRange")
    @ProblemFactCollectionProperty
    public List<ArmorPiece> chestList;

    @ValueRangeProvider(id = "armsRange")
    @ProblemFactCollectionProperty
    public List<ArmorPiece> armsList;

    @ValueRangeProvider(id = "legsRange")
    @ProblemFactCollectionProperty
    public List<ArmorPiece> legsList;

    @PlanningEntityProperty
    public ArmorSet armorSet;

    @PlanningScore(bendableHardLevelsSize = 1, bendableSoftLevelsSize = 5)
    BendableLongScore score;

    ArmorSetComboPlanningSolution() {
    }

    ArmorSetComboPlanningSolution(List<ArmorPiece> armorPieces) {
        this.armorPieces = armorPieces;
        this.armorByType = armorPieces.stream().collect(groupingBy(ArmorPiece::armorCategoryID));
        this.headList = armorByType.get(0);
        this.chestList = armorByType.get(1);
        this.armsList = armorByType.get(2);
        this.legsList = armorByType.get(3);
        // Need to initialize a starting value
        this.armorSet = new ArmorSet(0L, this.headList.get(0), this.chestList.get(0), this.armsList.get(0), this.legsList.get(0));
    }
}

Then the scorer:然后是得分手:

public class ArmorSetEasyOptimizer implements EasyScoreCalculator<ArmorSetComboPlanningSolution, BendableLongScore> {

    private final int TARGE_POISE = 61;
    private final double MAX_WEIGHT = 60.64;

    public ArmorSetEasyOptimizer() {
    }

    @Override
    public BendableLongScore calculateScore(ArmorSetComboPlanningSolution solution) {
        long hardScore = 0L;
        ArmorSet armorSet = solution.armorSet;

        if (armorSet.getTotalPoise() < TARGE_POISE) {
            hardScore--;
        }

        if (armorSet.getTotalWeight() > MAX_WEIGHT) {
            hardScore--;
        }

        long poiseRatio = (long) (armorSet.getTotalPoise() / (double) armorSet.getTotalWeight() * 100);

        long physicalDefenseScaled = (long) (armorSet.getTotalPhysicalDefense() * 100);
        long physicalDefenseToWeightRatio = (long) (physicalDefenseScaled / armorSet.getTotalWeight());

        long magicDefenseScaled = (long) (armorSet.getTotalMagicDefense() * 100);
        long magicDefenseToWeightRatio = (long) (magicDefenseScaled / armorSet.getTotalWeight());

        return BendableLongScore.of(
                new long[]{
                        hardScore
                },
                new long[]{
                        poiseRatio,
                        physicalDefenseScaled,
                        physicalDefenseToWeightRatio,
                        magicDefenseScaled,
                        magicDefenseToWeightRatio
                }
        );
    }
}

Results结果


19:02:12.707 [main] INFO org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase - Local Search phase (1) ended: time spent (10000), best score ([0]hard/[179/3540/97/2750/75]soft), score calculation speed (987500/sec), step total (4046).

19:02:12.709 [main] INFO org.optaplanner.core.impl.solver.DefaultSolver - Solving ended: time spent (10000), best score ([0]hard/[179/3540/97/2750/75]soft), score calculation speed (985624/sec), phase total (2), environment mode (REPRODUCIBLE), move thread count (NONE).

[0]hard/[179/3540/97/2750/75]soft

ArmorSet (Weight: 36.3, Poise: 65, Physical: 35.4, Phys/Weight: 0.97, Magic: 27.5, Magic/Weight: 0.75 ) [
    head: Radahn Soldier Helm (Weight: 4.0, Poise: 5),
    chest: Veteran's Armor (Weight: 18.9, Poise: 37),
    arms: Godskin Noble Bracelets (Weight: 1.7, Poise: 1),
    legs: Veteran's Greaves (Weight: 11.7, Poise: 22)
]

This is a bit of a strange problem, because you only need one planning entity instance.这是一个有点奇怪的问题,因为您只需要一个规划实体实例。 There only ever needs to be one ArmorSet object - and the solver will be assigning different armor pieces as it comes closer and closer to an optimal combination.只需要一个ArmorSet object - 解算器将分配不同的装甲件,因为它越来越接近最佳组合。

Therefore your easy score calculator doesn't ever need to do any looping.因此,您的简易分数计算器永远不需要进行任何循环。 It simply takes the single ArmorSet 's weight and poise and creates a score out of it.它只是采用单个ArmorSet的重量和平衡,并从中创建一个分数。

However, even though I think that this use case may be useful as a learning path towards constraint solvers, some sort of a brute force algorithm could work as well - your data set isn't too large.然而,尽管我认为这个用例可能作为约束求解器的学习路径很有用,但某种蛮力算法也可以工作——你的数据集不是太大。 More importantly, with an exhaustive algorithm such as brute force, you're eventually guaranteed to reach the optimal solution.更重要的是,借助穷举算法(例如蛮力),您最终一定会找到最优解。

(That said, if you want to enhance the problem with matching these armor sets to particular character traits, then it perhaps may be complex enough for brute force to become inadequate.) (也就是说,如果你想通过将这些盔甲套装与特定的性格特征相匹配来加强这个问题,那么它可能足够复杂,以至于蛮力变得不够用。)

On a personal note, I attempted Elden Ring and found it too hardcore for me.就个人而言,我尝试过《Elden Ring》,但发现它对我来说太硬核了。 :-) I prefer games that guide you a bit more. :-) 我更喜欢能为您提供更多指导的游戏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM