コード例 #1
0
        private void CreateRelicModel(EquipSlot s, Equipment e)
        {
            var gv = gear[s, e];

            RelicConfig config = null;

            SolverConfig.RelicConfigs.TryGetValue(e.ItemLevel.Key, out config);

            if (config == null || !config.Items.Contains(e.Key))
            {
                return;
            }

            var isNonSurjective = config.ConversionFunction != null;

            var statCap = config.StatCapOverrides.ContainsKey(Job) && config.StatCapOverrides[Job].ContainsKey(s)
                ? config.StatCapOverrides[Job][s]
                : config.StatCap;

            Model.AddConstraint(
                Expression.Sum(RelevantStats.Select(bp => isNonSurjective ? relicBase[s, e, bp] : cap[s, e, bp])) <=
                statCap * gv,
                $"total relic cap for {e} in slot {s}");

            foreach (var bp in RelevantStats)
            {
                var remCap = e.GetMateriaMeldCap(bp, true);
                if (remCap == 0)
                {
                    continue;
                }

                var cv = cap[s, e, bp];

                var func = config.ConversionFunction;

                if (config.ConversionOverride != null && config.ConversionOverride.ContainsKey(bp))
                {
                    func = config.ConversionOverride[bp];
                }

                if (isNonSurjective)
                {
                    func.AddToModel(Model, relicBase[s, e, bp], cv, SolverConfig.SolverSupportsSOS);
                }

                Model.AddConstraint(cv <= remCap * gv, $"upper stat cap for {bp} of relic {e} in slot {s}");
                StatExprs.AddExprToDict(bp, cv);
            }
        }
コード例 #2
0
        private void CreateObjective()
        {
            var objExpr = Expression.EmptyExpression;

            foreach (var bp in RelevantStats)
            {
                if (MainStats.Contains(bp.Name))
                {
                    StatExprs.AddExprToDict(bp, allocstat[bp]);
                }

                Model.AddConstraint(stat[bp] == StatExprs[bp], "set collected stat " + bp);
                Model.AddConstraint(modstat[bp] <= FoodExprs[bp], "relative food bonuses for " + bp);

                if (JobConfig.Weights.ContainsKey(bp))
                {
                    if (SolverConfig.MaximizeUnweightedValues && TieredStats.Contains(bp.Name))
                    {
                        // add a small dummy weight so shown stats are amxed out
                        DummyObjective += modstat[bp] * 1e-5;
                    }
                    objExpr += JobConfig.Weights[bp] * tieredstat[bp];
                }

                if (JobConfig.StatRequirements.ContainsKey(bp))
                {
                    if (SolverConfig.MaximizeUnweightedValues && !JobConfig.Weights.ContainsKey(bp))
                    {
                        DummyObjective += modstat[bp] * 1e-5;
                    }
                    Model.AddConstraint(modstat[bp] >= JobConfig.StatRequirements[bp], "satisfy stat requirement for " + bp);
                }
            }
            Model.AddObjective(new Objective(objExpr + DummyObjective, "stat weight", ObjectiveSense.Maximize),
                               "stat weight");
        }
コード例 #3
0
        private void CreateMateriaModel(EquipSlot s, Equipment e)
        {
            if (!MateriaChoices.Any())
            {
                return;
            }
            var gv = gear[s, e];

            //TODO: you can probably optimize tons here
            Model.AddConstraint(
                Expression.Sum(MateriaChoices.Select(m => materia[s, e, m.Key])) <= e.TotalMateriaSlots() * gv,
                $"restrict total materia amount to amount permitted for {e} in {s}");

            if (e.IsAdvancedMeldingPermitted)
            {
                if (MateriaChoices.Any(m => MainStats.Contains(m.Key.BaseParam.Name)))
                {
                    Model.AddConstraint(
                        Expression.Sum(
                            MateriaChoices.Where(m => MainStats.Contains(m.Key.BaseParam.Name))
                            .Select(m => materia[s, e, m.Key])) <= e.FreeMateriaSlots * gv,
                        $"restrict advanced melding for mainstat materia to amount of slots in {e} in {s}");
                }
                if (MateriaChoices.Any(m => !m.Value))
                {
                    Model.AddConstraint(
                        Expression.Sum(
                            MateriaChoices.Where(m => !m.Value).Select(m => materia[s, e, m.Key])) <=
                        (e.FreeMateriaSlots + SolverConfig.OvermeldThreshold) * gv,
                        $"restrict regular materia amount to amount permitted for {e} in {s}");
                }
            }


            // SIMPLIFICATION: ignoring whether materia fits; doesn't matter anyway
            foreach (var matGrp in MateriaChoices.GroupBy(m => m.Key.BaseParam))
            {
                var bp = matGrp.Key;

                var remCap = e.GetMateriaMeldCap(bp, true);
                if (remCap == 0)
                {
                    continue;
                }

                // we need to constrain against min(remaining cap, melded materia) to account for stat caps
                var cv = cap[s, e, bp];
                Model.AddConstraint(cv <= remCap * gv,
                                    $"cap stats using {e}'s meld cap for {bp} in slot {s}");

                var maxRegularMat  = matGrp.MaxBy(f => f.Key.Value).Key;
                var maxAdvancedMat = maxRegularMat;
                if (e.IsAdvancedMeldingPermitted)
                {
                    maxAdvancedMat = matGrp.Where(m => m.Value).MaxBy(f => f.Key.Value).Key;
                }

                // need hash-set here for uniqueness
                Model.AddConstraint(
                    cv <=
                    Expression.Sum(
                        new HashSet <MateriaItem> {
                    maxRegularMat, maxAdvancedMat
                }
                        .Select(m => m.Value * materia[s, e, m])),
                    $"cap stats using used {bp} for {e} in slot {s}");

                StatExprs.AddExprToDict(bp, cv);
            }
        }
コード例 #4
0
        private void CreateGearModel()
        {
            foreach (var grp in GearChoices.GroupBy(g => g.EquipSlotCategory))
            {
                // if gear is unique, equip it once only.
                if (grp.Key.PossibleSlots.Count() > 1)
                {
                    grp.Where(e => e.IsUnique)
                    .ForEach(
                        e =>
                        Model.AddConstraint(Expression.Sum(grp.Key.PossibleSlots.Select(s => gear[s, e])) <= 1,
                                            $"ensure gear uniqueness for {e}"));
                }

                // SIMPLIFICATION: we ignore blocked slots
                foreach (var s in grp.Key.PossibleSlots)
                {
                    // choose at most one gear per slot
                    Model.AddConstraint(Expression.Sum(grp.Select(e => gear[s, e])) <= 1,
                                        $"choose at most one item for slot {s}");
                    foreach (var e in grp)
                    {
                        var gv = gear[s, e];

                        // ASSUMPTION: all gear choices have fixed parameters
                        var stats =
                            e.AllParameters.Where(p => RelevantStats.Contains(p.BaseParam))
                            .ToDictionary(p => p.BaseParam,
                                          p => p.Values.OfType <ParameterValueFixed>().Select(val => val.Amount).Sum());

                        if (SolverConfig.EquipmentOverrides?.ContainsKey(e) ?? false)
                        {
                            var statsOverride = SolverConfig.EquipmentOverrides[e];

                            if (statsOverride == null)
                            {
                                continue;
                            }

                            foreach (var kv in statsOverride)
                            {
                                stats[kv.Key] = kv.Value;
                            }
                        }

                        // sanity check stats
                        foreach (var kv in stats)
                        {
                            if (e.GetMaximumParamValue(kv.Key) < kv.Value)
                            {
                                Console.Error.WriteLine($"{kv.Key} => {kv.Value} for {e} is out of range");
                            }
                        }

                        stats.ForEach(p => StatExprs.AddExprToDict(p.Key, p.Value * gv));

                        // ASSUMPTION: all meldable items have at least one materia slot
                        // ASSUMPTION: customisable relics are unmeldable
                        if (e.FreeMateriaSlots > 0)
                        {
                            CreateMateriaModel(s, e);
                        }
                        else
                        {
                            CreateRelicModel(s, e);
                        }
                    }
                }
            }
        }