From 2a0ce93eecf5cf7378e5ad17d964f494fac4a295 Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Fri, 15 Mar 2024 03:32:28 -0700 Subject: [PATCH] Optimize heuristic code slightly --- Solver/Simulator.cs | 189 ++++++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 79 deletions(-) diff --git a/Solver/Simulator.cs b/Solver/Simulator.cs index 843e86c..1a935cb 100644 --- a/Solver/Simulator.cs +++ b/Solver/Simulator.cs @@ -33,12 +33,27 @@ internal sealed class Simulator : SimulatorNoRandom this.maxStepCount = maxStepCount; } + // https://github.com/alostsock/crafty/blob/cffbd0cad8bab3cef9f52a3e3d5da4f5e3781842/crafty/src/craft_state.rs#L146 + [Pure] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool CouldUseAction(BaseAction baseAction) + { + if (CalculateSuccessRate(baseAction.SuccessRate(this)) != 1) + return false; + + // don't allow quality moves at max quality + if (Quality >= Input.Recipe.MaxQuality && baseAction.IncreasesQuality) + return false; + + return baseAction.CouldUse(this); + } + // https://github.com/alostsock/crafty/blob/cffbd0cad8bab3cef9f52a3e3d5da4f5e3781842/crafty/src/craft_state.rs#L146 [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] // It's just a bunch of if statements, I would assume this is actually quite simple to follow #pragma warning disable MA0051 // Method is too long - private bool CouldUseAction(ActionType action, BaseAction baseAction, bool strict) + private bool ShouldUseAction(ActionType action, BaseAction baseAction) #pragma warning restore MA0051 // Method is too long { if (CalculateSuccessRate(baseAction.SuccessRate(this)) != 1) @@ -48,87 +63,84 @@ internal sealed class Simulator : SimulatorNoRandom if (Quality >= Input.Recipe.MaxQuality && baseAction.IncreasesQuality) return false; - if (strict) + // always use Trained Eye if it's available + if (action == ActionType.TrainedEye) + return baseAction.CouldUse(this); + + // don't allow quality moves under Muscle Memory for difficult crafts + if (Input.Recipe.ClassJobLevel == 90 && + HasEffect(EffectType.MuscleMemory) && + baseAction.IncreasesQuality) + return false; + + // use First Turn actions if it's available and the craft is difficult + if (IsFirstStep && + Input.Recipe.ClassJobLevel == 90 && + baseAction.Category != ActionCategory.FirstTurn && + CP > 10) + return false; + + // don't allow combo actions if the combo is already in progress + if (ActionStates.TouchComboIdx != 0 && + (action == ActionType.StandardTouchCombo || action == ActionType.AdvancedTouchCombo)) + return false; + + // don't allow pure quality moves under Veneration + if (HasEffect(EffectType.Veneration) && + !baseAction.IncreasesProgress && + baseAction.IncreasesQuality) + return false; + + // don't allow pure quality moves when it won't be able to finish the craft + if (baseAction.IncreasesQuality && + CalculateDurabilityCost(baseAction.DurabilityCost) > Durability) + return false; + + if (baseAction.IncreasesProgress) { - // always use Trained Eye if it's available - if (action == ActionType.TrainedEye) - return baseAction.CouldUse(this); + var progressIncrease = CalculateProgressGain(baseAction.Efficiency(this)); + var wouldFinish = Progress + progressIncrease >= Input.Recipe.MaxProgress; - // don't allow quality moves under Muscle Memory for difficult crafts - if (Input.Recipe.ClassJobLevel == 90 && - HasEffect(EffectType.MuscleMemory) && - baseAction.IncreasesQuality) - return false; - - // use First Turn actions if it's available and the craft is difficult - if (IsFirstStep && - Input.Recipe.ClassJobLevel == 90 && - baseAction.Category != ActionCategory.FirstTurn && - CP > 10) - return false; - - // don't allow combo actions if the combo is already in progress - if (ActionStates.TouchComboIdx != 0 && - (action == ActionType.StandardTouchCombo || action == ActionType.AdvancedTouchCombo)) - return false; - - // don't allow pure quality moves under Veneration - if (HasEffect(EffectType.Veneration) && - !baseAction.IncreasesProgress && - baseAction.IncreasesQuality) - return false; - - // don't allow pure quality moves when it won't be able to finish the craft - if (baseAction.IncreasesQuality && - CalculateDurabilityCost(baseAction.DurabilityCost) > Durability) - return false; - - if (baseAction.IncreasesProgress) + if (wouldFinish) { - var progressIncrease = CalculateProgressGain(baseAction.Efficiency(this)); - var wouldFinish = Progress + progressIncrease >= Input.Recipe.MaxProgress; - - if (wouldFinish) - { - // don't allow finishing the craft if there is significant quality remaining - if (Quality < Input.Recipe.MaxQuality / 5) - return false; - } - else - { - // don't allow pure progress moves under Innovation, if it wouldn't finish the craft - if (HasEffect(EffectType.Innovation) && - !baseAction.IncreasesQuality && - baseAction.IncreasesProgress) - return false; - } + // don't allow finishing the craft if there is significant quality remaining + if (Quality < Input.Recipe.MaxQuality / 5) + return false; + } + else + { + // don't allow pure progress moves under Innovation, if it wouldn't finish the craft + if (HasEffect(EffectType.Innovation) && + !baseAction.IncreasesQuality && + baseAction.IncreasesProgress) + return false; } - - if (action == ActionType.ByregotsBlessing && - GetEffectStrength(EffectType.InnerQuiet) <= 1) - return false; - - if ((action == ActionType.WasteNot || action == ActionType.WasteNot2) && - (HasEffect(EffectType.WasteNot) || HasEffect(EffectType.WasteNot2))) - return false; - - if (action == ActionType.MastersMend && - Input.Recipe.MaxDurability - Durability < 25) - return false; - - if (action == ActionType.Manipulation && - HasEffect(EffectType.Manipulation)) - return false; - - if (action == ActionType.GreatStrides && - HasEffect(EffectType.GreatStrides)) - return false; - - if ((action == ActionType.Veneration || action == ActionType.Innovation) && - (GetEffectDuration(EffectType.Veneration) > 1 || GetEffectDuration(EffectType.Innovation) > 1)) - return false; } + if (action == ActionType.ByregotsBlessing && + GetEffectStrength(EffectType.InnerQuiet) <= 1) + return false; + + if ((action == ActionType.WasteNot || action == ActionType.WasteNot2) && + (HasEffect(EffectType.WasteNot) || HasEffect(EffectType.WasteNot2))) + return false; + + if (action == ActionType.MastersMend && + Input.Recipe.MaxDurability - Durability < 25) + return false; + + if (action == ActionType.Manipulation && + HasEffect(EffectType.Manipulation)) + return false; + + if (action == ActionType.GreatStrides && + HasEffect(EffectType.GreatStrides)) + return false; + + if ((action == ActionType.Veneration || action == ActionType.Innovation) && + (GetEffectDuration(EffectType.Veneration) > 1 || GetEffectDuration(EffectType.Innovation) > 1)) + return false; + return baseAction.CouldUse(this); } @@ -139,9 +151,28 @@ internal sealed class Simulator : SimulatorNoRandom return new(); var ret = new ActionSet(); - foreach (var (data, action) in actionPoolObjects) - if (CouldUseAction(action, data, strict)) - ret.AddAction(action); + if (strict) + { + foreach (var (data, action) in actionPoolObjects) + { + if (ShouldUseAction(action, data)) + ret.AddAction(action); + } + + // If Trained Eye is possible, *always* use Trained Eye + if (ret.HasAction(ActionType.TrainedEye)) + { + ret = new(); + ret.AddAction(ActionType.TrainedEye); + } + } + else + { + foreach (var (data, action) in actionPoolObjects) + if (CouldUseAction(data)) + ret.AddAction(action); + } + return ret; } }