Remove threadlocal dependence

This commit is contained in:
Asriel Camora
2023-06-21 13:20:05 -07:00
parent 0f2267dabf
commit 5faaa02f1a
43 changed files with 279 additions and 244 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ internal static class ActionUtils
{ {
private static (CraftAction? CraftAction, Action? Action) GetActionRow(this ActionType me, ClassJob classJob) private static (CraftAction? CraftAction, Action? Action) GetActionRow(this ActionType me, ClassJob classJob)
{ {
var actionId = me.WithUnsafe().ActionId; var actionId = me.Base().ActionId;
if (LuminaSheets.CraftActionSheet.GetRow(actionId) is CraftAction baseCraftAction) if (LuminaSheets.CraftActionSheet.GetRow(actionId) is CraftAction baseCraftAction)
{ {
return (classJob switch return (classJob switch
+4 -4
View File
@@ -71,15 +71,15 @@ public class SimulatorWindow : Window
ImGuiUtils.BeginGroupPanel(category.Key.GetDisplayName()); ImGuiUtils.BeginGroupPanel(category.Key.GetDisplayName());
foreach (var action in category.OrderBy(a => a.Level())) foreach (var action in category.OrderBy(a => a.Level()))
{ {
var baseAction = action.With(Simulation); var baseAction = action.Base();
if (showOnlyGuaranteedActions && !baseAction.IsGuaranteedAction) if (showOnlyGuaranteedActions && baseAction.SuccessRate(Simulation) != 1)
continue; continue;
ImGui.BeginDisabled(!baseAction.CanUse || Simulation.IsComplete); ImGui.BeginDisabled(!baseAction.CanUse(Simulation) || Simulation.IsComplete);
if (ImGui.ImageButton(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2))) if (ImGui.ImageButton(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2)))
(_, State) = Simulation.Execute(State, action); (_, State) = Simulation.Execute(State, action);
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip($"{action.GetName(ClassJob.Carpenter)}\n{baseAction.GetTooltip(true)}"); ImGui.SetTooltip($"{action.GetName(ClassJob.Carpenter)}\n{baseAction.GetTooltip(Simulation, true)}");
ImGui.EndDisabled(); ImGui.EndDisabled();
if (++i % 5 != 0) if (++i % 5 != 0)
ImGui.SameLine(); ImGui.SameLine();
+8 -21
View File
@@ -51,32 +51,19 @@ public static class ActionUtils
.ToArray(); .ToArray();
} }
public static void SetSimulation(Simulator simulation) => public static BaseAction Base(this ActionType me) => Actions[(int)me];
BaseAction.TLSSimulation = simulation;
public static BaseAction WithUnsafe(this ActionType me) => Actions[(int)me]; public static IEnumerable<ActionType> AvailableActions(Simulator simulation) =>
simulation.IsComplete
public static BaseAction With(this ActionType me, Simulator simulation) ? Enumerable.Empty<ActionType>()
{ : Enum.GetValues<ActionType>()
SetSimulation(simulation); .Where(a => a.Base().CanUse(simulation));
return WithUnsafe(me);
}
public static IEnumerable<ActionType> AvailableActions(Simulator simulation)
{
if (simulation.IsComplete)
return Enumerable.Empty<ActionType>();
SetSimulation(simulation);
return Enum.GetValues<ActionType>()
.Where(a => WithUnsafe(a).CanUse);
}
public static int Level(this ActionType me) => public static int Level(this ActionType me) =>
WithUnsafe(me).Level; me.Base().Level;
public static ActionCategory Category(this ActionType me) => public static ActionCategory Category(this ActionType me) =>
WithUnsafe(me).Category; me.Base().Category;
public static string IntName(this ActionType me) => public static string IntName(this ActionType me) =>
me switch me switch
+3 -2
View File
@@ -6,7 +6,8 @@ internal sealed class AdvancedTouch : BaseAction
public override int Level => 84; public override int Level => 84;
public override uint ActionId => 100411; public override uint ActionId => 100411;
public override int CPCost => Simulation.ActionStates.TouchComboIdx == 2 ? 18 : 46;
public override float Efficiency => 1.50f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int CPCost(Simulator s) => s.ActionStates.TouchComboIdx == 2 ? 18 : 46;
public override float Efficiency(Simulator s) => 1.50f;
} }
+33 -34
View File
@@ -4,79 +4,78 @@ namespace Craftimizer.Simulator.Actions;
public abstract class BaseAction public abstract class BaseAction
{ {
[ThreadStatic]
internal static Simulator? TLSSimulation;
protected static Simulator Simulation => TLSSimulation!;
// Non-instanced properties // Non-instanced properties
// Metadata
public abstract ActionCategory Category { get; } public abstract ActionCategory Category { get; }
public abstract int Level { get; } public abstract int Level { get; }
// Doesn't matter from which class, we'll use the sheet to extrapolate the rest // Doesn't matter from which class, we'll use the sheet to extrapolate the rest
public abstract uint ActionId { get; } public abstract uint ActionId { get; }
// Instanced properties // Action properties
public abstract int CPCost { get; }
public virtual float Efficiency => 0f;
public virtual bool IncreasesProgress => false; public virtual bool IncreasesProgress => false;
public virtual bool IncreasesQuality => false; public virtual bool IncreasesQuality => false;
public virtual float SuccessRate => 1f;
public virtual int DurabilityCost => 10; public virtual int DurabilityCost => 10;
public virtual bool IncreasesStepCount => true; public virtual bool IncreasesStepCount => true;
public virtual bool IsGuaranteedAction => SuccessRate == 1f;
public virtual bool CanUse => // Instanced properties
Simulation.Input.Stats.Level >= Level && Simulation.CP >= CPCost; public abstract int CPCost(Simulator s);
public virtual float Efficiency(Simulator s) => 0f;
public virtual float SuccessRate(Simulator s) => 1f;
public virtual void Use() public virtual bool CanUse(Simulator s) =>
s.Input.Stats.Level >= Level && s.CP >= CPCost(s);
public virtual void Use(Simulator s)
{ {
if (Simulation.RollSuccess(SuccessRate)) if (s.RollSuccess(SuccessRate(s)))
UseSuccess(); UseSuccess(s);
Simulation.ReduceCP(CPCost); s.ReduceCP(CPCost(s));
Simulation.ReduceDurability(DurabilityCost); s.ReduceDurability(DurabilityCost);
if (Simulation.Durability > 0) if (s.Durability > 0)
{ {
if (Simulation.HasEffect(EffectType.Manipulation)) if (s.HasEffect(EffectType.Manipulation))
Simulation.RestoreDurability(5); s.RestoreDurability(5);
} }
if (IncreasesStepCount) if (IncreasesStepCount)
Simulation.IncreaseStepCount(); s.IncreaseStepCount();
} }
public virtual void UseSuccess() public virtual void UseSuccess(Simulator s)
{ {
if (Efficiency != 0f) if (Efficiency(s) != 0f)
{ {
if (IncreasesProgress) if (IncreasesProgress)
Simulation.IncreaseProgress(Efficiency); s.IncreaseProgress(Efficiency(s));
if (IncreasesQuality) if (IncreasesQuality)
Simulation.IncreaseQuality(Efficiency); s.IncreaseQuality(Efficiency(s));
} }
} }
public virtual string GetTooltip(bool addUsability) public virtual string GetTooltip(Simulator s, bool addUsability)
{ {
var builder = new StringBuilder(); var builder = new StringBuilder();
if (addUsability && !CanUse) if (addUsability && !CanUse(s))
builder.AppendLine($"Cannot Use"); builder.AppendLine($"Cannot Use");
builder.AppendLine($"Level {Level}"); builder.AppendLine($"Level {Level}");
if (CPCost != 0) if (CPCost(s) != 0)
builder.AppendLine($"-{Simulation.CalculateCPCost(CPCost)} CP"); builder.AppendLine($"-{s.CalculateCPCost(CPCost(s))} CP");
if (DurabilityCost != 0) if (DurabilityCost != 0)
builder.AppendLine($"-{Simulation.CalculateDurabilityCost(DurabilityCost)} Durability"); builder.AppendLine($"-{s.CalculateDurabilityCost(DurabilityCost)} Durability");
if (Efficiency != 0) if (Efficiency(s) != 0)
{ {
if (IncreasesProgress) if (IncreasesProgress)
builder.AppendLine($"+{Simulation.CalculateProgressGain(Efficiency)} Progress"); builder.AppendLine($"+{s.CalculateProgressGain(Efficiency(s))} Progress");
if (IncreasesQuality) if (IncreasesQuality)
builder.AppendLine($"+{Simulation.CalculateQualityGain(Efficiency)} Quality"); builder.AppendLine($"+{s.CalculateQualityGain(Efficiency(s))} Quality");
} }
if (!IncreasesStepCount) if (!IncreasesStepCount)
builder.AppendLine($"Does Not Increase Step Count"); builder.AppendLine($"Does Not Increase Step Count");
if (SuccessRate != 1f) if (SuccessRate(s) != 1f)
builder.AppendLine($"{Simulation.CalculateSuccessRate(SuccessRate) * 100}%% Success Rate"); builder.AppendLine($"{s.CalculateSuccessRate(SuccessRate(s)) * 100}%% Success Rate");
return builder.ToString(); return builder.ToString();
} }
} }
+6 -5
View File
@@ -4,17 +4,18 @@ namespace Craftimizer.Simulator.Actions;
internal abstract class BaseBuffAction : BaseAction internal abstract class BaseBuffAction : BaseAction
{ {
// Non-instanced properties
public abstract EffectType Effect { get; } public abstract EffectType Effect { get; }
public virtual byte Duration => 1; public virtual byte Duration => 1;
public override int DurabilityCost => 0; public sealed override int DurabilityCost => 0;
public override void UseSuccess() => public override void UseSuccess(Simulator s) =>
Simulation.AddEffect(Effect, Duration); s.AddEffect(Effect, Duration);
public override string GetTooltip(bool addUsability) public sealed override string GetTooltip(Simulator s, bool addUsability)
{ {
var builder = new StringBuilder(base.GetTooltip(addUsability)); var builder = new StringBuilder(base.GetTooltip(s, addUsability));
builder.AppendLine($"{Duration} Steps"); builder.AppendLine($"{Duration} Steps");
return builder.ToString(); return builder.ToString();
} }
+4 -3
View File
@@ -6,8 +6,9 @@ internal sealed class BasicSynthesis : BaseAction
public override int Level => 1; public override int Level => 1;
public override uint ActionId => 100001; public override uint ActionId => 100001;
public override int CPCost => 0;
// Basic Synthesis Mastery Trait
public override float Efficiency => Simulation.Input.Stats.Level >= 31 ? 1.20f : 1.00f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override int CPCost(Simulator s) => 0;
// Basic Synthesis Mastery Trait
public override float Efficiency(Simulator s) => s.Input.Stats.Level >= 31 ? 1.20f : 1.00f;
} }
+3 -2
View File
@@ -6,7 +6,8 @@ internal sealed class BasicTouch : BaseAction
public override int Level => 5; public override int Level => 5;
public override uint ActionId => 100002; public override uint ActionId => 100002;
public override int CPCost => 18;
public override float Efficiency => 1.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int CPCost(Simulator s) => 18;
public override float Efficiency(Simulator s) => 1.00f;
} }
+7 -6
View File
@@ -6,15 +6,16 @@ internal sealed class ByregotsBlessing : BaseAction
public override int Level => 50; public override int Level => 50;
public override uint ActionId => 100339; public override uint ActionId => 100339;
public override int CPCost => 24;
public override float Efficiency => 1.00f + (0.20f * Simulation.GetEffectStrength(EffectType.InnerQuiet));
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override bool CanUse => Simulation.HasEffect(EffectType.InnerQuiet) && base.CanUse; public override int CPCost(Simulator s) => 24;
public override float Efficiency(Simulator s) => 1.00f + (0.20f * s.GetEffectStrength(EffectType.InnerQuiet));
public override void UseSuccess() public override bool CanUse(Simulator s) => s.HasEffect(EffectType.InnerQuiet) && base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.RemoveEffect(EffectType.InnerQuiet); s.RemoveEffect(EffectType.InnerQuiet);
} }
} }
+4 -4
View File
@@ -6,12 +6,12 @@ internal sealed class CarefulObservation : BaseAction
public override int Level => 55; public override int Level => 55;
public override uint ActionId => 100395; public override uint ActionId => 100395;
public override int CPCost => 0;
public override int DurabilityCost => 0; public override int DurabilityCost => 0;
public override bool IncreasesStepCount => false; public override bool IncreasesStepCount => false;
public override bool CanUse => Simulation.Input.Stats.IsSpecialist && Simulation.ActionStates.CarefulObservationCount < 3; public override int CPCost(Simulator s) => 0;
public override void UseSuccess() => public override bool CanUse(Simulator s) => s.Input.Stats.IsSpecialist && s.ActionStates.CarefulObservationCount < 3;
Simulation.StepCondition();
public override void UseSuccess(Simulator s) => s.StepCondition();
} }
+4 -3
View File
@@ -6,8 +6,9 @@ internal sealed class CarefulSynthesis : BaseAction
public override int Level => 62; public override int Level => 62;
public override uint ActionId => 100203; public override uint ActionId => 100203;
public override int CPCost => 7;
// Careful Synthesis Mastery Trait
public override float Efficiency => Simulation.Input.Stats.Level >= 82 ? 1.80f : 1.50f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override int CPCost(Simulator s) => 7;
// Careful Synthesis Mastery Trait
public override float Efficiency(Simulator s) => s.Input.Stats.Level >= 82 ? 1.80f : 1.50f;
} }
+3 -2
View File
@@ -6,8 +6,9 @@ internal sealed class DelicateSynthesis : BaseAction
public override int Level => 76; public override int Level => 76;
public override uint ActionId => 100323; public override uint ActionId => 100323;
public override int CPCost => 32;
public override float Efficiency => 1.00f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int CPCost(Simulator s) => 32;
public override float Efficiency(Simulator s) => 1.00f;
} }
+2 -1
View File
@@ -6,9 +6,10 @@ internal sealed class FinalAppraisal : BaseBuffAction
public override int Level => 42; public override int Level => 42;
public override uint ActionId => 19012; public override uint ActionId => 19012;
public override int CPCost => 1;
public override bool IncreasesStepCount => false; public override bool IncreasesStepCount => false;
public override EffectType Effect => EffectType.FinalAppraisal; public override EffectType Effect => EffectType.FinalAppraisal;
public override byte Duration => 5; public override byte Duration => 5;
public override int CPCost(Simulator s) => 1;
} }
+4 -3
View File
@@ -6,8 +6,9 @@ internal sealed class FocusedSynthesis : BaseAction
public override int Level => 67; public override int Level => 67;
public override uint ActionId => 100235; public override uint ActionId => 100235;
public override int CPCost => 5;
public override float Efficiency => 2.00f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override float SuccessRate => Simulation.ActionStates.Observed ? 1.00f : 0.50f;
public override int CPCost(Simulator s) => 5;
public override float Efficiency(Simulator s) => 2.00f;
public override float SuccessRate(Simulator s) => s.ActionStates.Observed ? 1.00f : 0.50f;
} }
+4 -3
View File
@@ -6,8 +6,9 @@ internal sealed class FocusedTouch : BaseAction
public override int Level => 68; public override int Level => 68;
public override uint ActionId => 100243; public override uint ActionId => 100243;
public override int CPCost => 18;
public override float Efficiency => 1.50f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override float SuccessRate => Simulation.ActionStates.Observed ? 1.00f : 0.50f;
public override int CPCost(Simulator s) => 18;
public override float Efficiency(Simulator s) => 1.50f;
public override float SuccessRate(Simulator s) => s.ActionStates.Observed ? 1.00f : 0.50f;
} }
+2 -2
View File
@@ -6,8 +6,8 @@ internal sealed class GreatStrides : BaseBuffAction
public override int Level => 21; public override int Level => 21;
public override uint ActionId => 260; public override uint ActionId => 260;
public override int CPCost => 32;
public override EffectType Effect => EffectType.GreatStrides; public override EffectType Effect => EffectType.GreatStrides;
public override byte Duration => 3; public override byte Duration => 3;
public override int CPCost(Simulator s) => 32;
} }
+8 -10
View File
@@ -6,16 +6,14 @@ internal sealed class Groundwork : BaseAction
public override int Level => 72; public override int Level => 72;
public override uint ActionId => 100403; public override uint ActionId => 100403;
public override int CPCost => 18;
// Groundwork Mastery Trait
public override float Efficiency
{
get
{
var ret = Simulation.Input.Stats.Level >= 86 ? 3.60f : 3.00f;
return Simulation.Durability < Simulation.CalculateDurabilityCost(DurabilityCost) ? ret / 2 : ret;
}
}
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override int DurabilityCost => 20; public override int DurabilityCost => 20;
public override int CPCost(Simulator s) => 18;
public override float Efficiency(Simulator s)
{
// Groundwork Mastery Trait
var ret = s.Input.Stats.Level >= 86 ? 3.60f : 3.00f;
return s.Durability < s.CalculateDurabilityCost(DurabilityCost) ? ret / 2 : ret;
}
} }
+4 -3
View File
@@ -6,8 +6,9 @@ internal sealed class HastyTouch : BaseAction
public override int Level => 9; public override int Level => 9;
public override uint ActionId => 100355; public override uint ActionId => 100355;
public override int CPCost => 0;
public override float Efficiency => 1.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override float SuccessRate => 0.60f;
public override int CPCost(Simulator s) => 0;
public override float Efficiency(Simulator s) => 1.00f;
public override float SuccessRate(Simulator s) => 0.60f;
} }
+3 -2
View File
@@ -6,10 +6,11 @@ internal sealed class HeartAndSoul : BaseBuffAction
public override int Level => 86; public override int Level => 86;
public override uint ActionId => 100419; public override uint ActionId => 100419;
public override int CPCost => 0;
public override bool IncreasesStepCount => false; public override bool IncreasesStepCount => false;
public override EffectType Effect => EffectType.HeartAndSoul; public override EffectType Effect => EffectType.HeartAndSoul;
public override bool CanUse => Simulation.Input.Stats.IsSpecialist && !Simulation.ActionStates.UsedHeartAndSoul; public override int CPCost(Simulator s) => 0;
public override bool CanUse(Simulator s) => s.Input.Stats.IsSpecialist && !s.ActionStates.UsedHeartAndSoul;
} }
+2 -2
View File
@@ -6,8 +6,8 @@ internal sealed class Innovation : BaseBuffAction
public override int Level => 26; public override int Level => 26;
public override uint ActionId => 19004; public override uint ActionId => 19004;
public override int CPCost => 18;
public override EffectType Effect => EffectType.Innovation; public override EffectType Effect => EffectType.Innovation;
public override byte Duration => 4; public override byte Duration => 4;
public override int CPCost(Simulator s) => 18;
} }
+10 -10
View File
@@ -6,19 +6,19 @@ internal sealed class IntensiveSynthesis : BaseAction
public override int Level => 78; public override int Level => 78;
public override uint ActionId => 100315; public override uint ActionId => 100315;
public override int CPCost => 6;
public override float Efficiency => 4.00f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override bool IsGuaranteedAction => false;
public override bool CanUse => public override int CPCost(Simulator s) => 6;
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul)) public override float Efficiency(Simulator s) => 4.00f;
&& base.CanUse;
public override void UseSuccess() public override bool CanUse(Simulator s) =>
(s.Condition == Condition.Good || s.Condition == Condition.Excellent || s.HasEffect(EffectType.HeartAndSoul))
&& base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) if (s.Condition != Condition.Good && s.Condition != Condition.Excellent)
Simulation.RemoveEffect(EffectType.HeartAndSoul); s.RemoveEffect(EffectType.HeartAndSoul);
} }
} }
+9 -9
View File
@@ -6,21 +6,21 @@ internal sealed class Manipulation : BaseBuffAction
public override int Level => 65; public override int Level => 65;
public override uint ActionId => 4574; public override uint ActionId => 4574;
public override int CPCost => 96;
public override EffectType Effect => EffectType.Manipulation; public override EffectType Effect => EffectType.Manipulation;
public override byte Duration => 8; public override byte Duration => 8;
public override void Use() public override int CPCost(Simulator s) => 96;
public override void Use(Simulator s)
{ {
if (Simulation.HasEffect(EffectType.Manipulation)) if (s.HasEffect(EffectType.Manipulation))
Simulation.RestoreDurability(5); s.RestoreDurability(5);
Simulation.ReduceCP(CPCost); s.ReduceCP(CPCost(s));
Simulation.ReduceDurability(DurabilityCost); s.ReduceDurability(DurabilityCost);
UseSuccess(); UseSuccess(s);
Simulation.IncreaseStepCount(); s.IncreaseStepCount();
} }
} }
+4 -3
View File
@@ -6,9 +6,10 @@ internal sealed class MastersMend : BaseAction
public override int Level => 7; public override int Level => 7;
public override uint ActionId => 100003; public override uint ActionId => 100003;
public override int CPCost => 88;
public override int DurabilityCost => 0; public override int DurabilityCost => 0;
public override void UseSuccess() => public override int CPCost(Simulator s) => 88;
Simulation.RestoreDurability(30);
public override void UseSuccess(Simulator s) =>
s.RestoreDurability(30);
} }
+7 -6
View File
@@ -6,15 +6,16 @@ internal sealed class MuscleMemory : BaseAction
public override int Level => 54; public override int Level => 54;
public override uint ActionId => 100379; public override uint ActionId => 100379;
public override int CPCost => 6;
public override float Efficiency => 3.00f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override bool CanUse => Simulation.IsFirstStep && base.CanUse; public override int CPCost(Simulator s) => 6;
public override float Efficiency(Simulator s) => 3.00f;
public override void UseSuccess() public override bool CanUse(Simulator s) => s.IsFirstStep && base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.AddEffect(EffectType.MuscleMemory, 5); s.AddEffect(EffectType.MuscleMemory, 5);
} }
} }
+2 -1
View File
@@ -6,6 +6,7 @@ internal sealed class Observe : BaseAction
public override int Level => 13; public override int Level => 13;
public override uint ActionId => 100010; public override uint ActionId => 100010;
public override int CPCost => 7;
public override int DurabilityCost => 0; public override int DurabilityCost => 0;
public override int CPCost(Simulator s) => 7;
} }
+11 -11
View File
@@ -6,20 +6,20 @@ internal sealed class PreciseTouch : BaseAction
public override int Level => 53; public override int Level => 53;
public override uint ActionId => 100128; public override uint ActionId => 100128;
public override int CPCost => 18;
public override float Efficiency => 1.50f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override bool IsGuaranteedAction => false;
public override bool CanUse => public override int CPCost(Simulator s) => 18;
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul)) public override float Efficiency(Simulator s) => 1.50f;
&& base.CanUse;
public override void UseSuccess() public override bool CanUse(Simulator s) =>
(s.Condition == Condition.Good || s.Condition == Condition.Excellent || s.HasEffect(EffectType.HeartAndSoul))
&& base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.StrengthenEffect(EffectType.InnerQuiet); s.StrengthenEffect(EffectType.InnerQuiet);
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) if (s.Condition != Condition.Good && s.Condition != Condition.Excellent)
Simulation.RemoveEffect(EffectType.HeartAndSoul); s.RemoveEffect(EffectType.HeartAndSoul);
} }
} }
+6 -5
View File
@@ -6,14 +6,15 @@ internal sealed class PreparatoryTouch : BaseAction
public override int Level => 71; public override int Level => 71;
public override uint ActionId => 100299; public override uint ActionId => 100299;
public override int CPCost => 40;
public override float Efficiency => 2.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int DurabilityCost => 20; public override int DurabilityCost => 20;
public override void UseSuccess() public override int CPCost(Simulator s) => 40;
public override float Efficiency(Simulator s) => 2.00f;
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.StrengthenEffect(EffectType.InnerQuiet); s.StrengthenEffect(EffectType.InnerQuiet);
} }
} }
+6 -5
View File
@@ -6,12 +6,13 @@ internal sealed class PrudentSynthesis : BaseAction
public override int Level => 88; public override int Level => 88;
public override uint ActionId => 100427; public override uint ActionId => 100427;
public override int CPCost => 18;
public override float Efficiency => 1.80f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override int DurabilityCost => base.DurabilityCost / 2; public override int DurabilityCost => base.DurabilityCost / 2;
public override bool CanUse => public override int CPCost(Simulator s) => 18;
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2)) public override float Efficiency(Simulator s) => 1.80f;
&& base.CanUse;
public override bool CanUse(Simulator s) =>
!(s.HasEffect(EffectType.WasteNot) || s.HasEffect(EffectType.WasteNot2))
&& base.CanUse(s);
} }
+6 -5
View File
@@ -6,12 +6,13 @@ internal sealed class PrudentTouch : BaseAction
public override int Level => 66; public override int Level => 66;
public override uint ActionId => 100227; public override uint ActionId => 100227;
public override int CPCost => 25;
public override float Efficiency => 1.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int DurabilityCost => base.DurabilityCost / 2; public override int DurabilityCost => base.DurabilityCost / 2;
public override bool CanUse => public override int CPCost(Simulator s) => 25;
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2)) public override float Efficiency(Simulator s) => 1.00f;
&& base.CanUse;
public override bool CanUse(Simulator s) =>
!(s.HasEffect(EffectType.WasteNot) || s.HasEffect(EffectType.WasteNot2))
&& base.CanUse(s);
} }
+5 -4
View File
@@ -6,9 +6,10 @@ internal sealed class RapidSynthesis : BaseAction
public override int Level => 9; public override int Level => 9;
public override uint ActionId => 100363; public override uint ActionId => 100363;
public override int CPCost => 0;
// Rapid Synthesis Mastery Trait
public override float Efficiency => Simulation.Input.Stats.Level >= 63 ? 5.00f : 2.50f;
public override bool IncreasesProgress => true; public override bool IncreasesProgress => true;
public override float SuccessRate => 0.50f;
public override int CPCost(Simulator s) => 0;
// Rapid Synthesis Mastery Trait
public override float Efficiency(Simulator s) => s.Input.Stats.Level >= 63 ? 5.00f : 2.50f;
public override float SuccessRate(Simulator s) => 0.50f;
} }
+7 -6
View File
@@ -6,15 +6,16 @@ internal sealed class Reflect : BaseAction
public override int Level => 69; public override int Level => 69;
public override uint ActionId => 100387; public override uint ActionId => 100387;
public override int CPCost => 6;
public override float Efficiency => 1.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override bool CanUse => Simulation.IsFirstStep && base.CanUse; public override int CPCost(Simulator s) => 6;
public override float Efficiency(Simulator s) => 1.00f;
public override void UseSuccess() public override bool CanUse(Simulator s) => s.IsFirstStep && base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.StrengthenEffect(EffectType.InnerQuiet); s.StrengthenEffect(EffectType.InnerQuiet);
} }
} }
+3 -2
View File
@@ -6,7 +6,8 @@ internal sealed class StandardTouch : BaseAction
public override int Level => 18; public override int Level => 18;
public override uint ActionId => 100004; public override uint ActionId => 100004;
public override int CPCost => Simulation.ActionStates.TouchComboIdx == 1 ? 18 : 32;
public override float Efficiency => 1.25f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int CPCost(Simulator s) => s.ActionStates.TouchComboIdx == 1 ? 18 : 32;
public override float Efficiency(Simulator s) => 1.25f;
} }
+9 -8
View File
@@ -6,15 +6,16 @@ internal sealed class TrainedEye : BaseAction
public override int Level => 80; public override int Level => 80;
public override uint ActionId => 100283; public override uint ActionId => 100283;
public override int CPCost => 250;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override bool CanUse => public override int CPCost(Simulator s) => 250;
Simulation.IsFirstStep &&
!Simulation.Input.Recipe.IsExpert &&
Simulation.Input.Stats.Level >= (Simulation.Input.Recipe.ClassJobLevel + 10) &&
base.CanUse;
public override void UseSuccess() => public override bool CanUse(Simulator s) =>
Simulation.IncreaseQualityRaw(Simulation.Input.Recipe.MaxQuality - Simulation.Quality); s.IsFirstStep &&
!s.Input.Recipe.IsExpert &&
s.Input.Stats.Level >= (s.Input.Recipe.ClassJobLevel + 10) &&
base.CanUse(s);
public override void UseSuccess(Simulator s) =>
s.IncreaseQualityRaw(s.Input.Recipe.MaxQuality - s.Quality);
} }
+6 -5
View File
@@ -6,12 +6,13 @@ internal sealed class TrainedFinesse : BaseAction
public override int Level => 90; public override int Level => 90;
public override uint ActionId => 100435; public override uint ActionId => 100435;
public override int CPCost => 32;
public override float Efficiency => 1.00f;
public override bool IncreasesQuality => true; public override bool IncreasesQuality => true;
public override int DurabilityCost => 0; public override int DurabilityCost => 0;
public override bool CanUse => public override int CPCost(Simulator s) => 32;
Simulation.GetEffectStrength(EffectType.InnerQuiet) == 10 public override float Efficiency(Simulator s) => 1.00f;
&& base.CanUse;
public override bool CanUse(Simulator s) =>
s.GetEffectStrength(EffectType.InnerQuiet) == 10
&& base.CanUse(s);
} }
+9 -9
View File
@@ -6,18 +6,18 @@ internal sealed class TricksOfTheTrade : BaseAction
public override int Level => 13; public override int Level => 13;
public override uint ActionId => 100371; public override uint ActionId => 100371;
public override int CPCost => 0;
public override int DurabilityCost => 0; public override int DurabilityCost => 0;
public override bool IsGuaranteedAction => false;
public override bool CanUse => public override int CPCost(Simulator s) => 0;
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul))
&& base.CanUse;
public override void UseSuccess() public override bool CanUse(Simulator s) =>
(s.Condition == Condition.Good || s.Condition == Condition.Excellent || s.HasEffect(EffectType.HeartAndSoul))
&& base.CanUse(s);
public override void UseSuccess(Simulator s)
{ {
Simulation.RestoreCP(20); s.RestoreCP(20);
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) if (s.Condition != Condition.Good && s.Condition != Condition.Excellent)
Simulation.RemoveEffect(EffectType.HeartAndSoul); s.RemoveEffect(EffectType.HeartAndSoul);
} }
} }
+2 -3
View File
@@ -6,9 +6,8 @@ internal sealed class Veneration : BaseBuffAction
public override int Level => 15; public override int Level => 15;
public override uint ActionId => 19297; public override uint ActionId => 19297;
public override int CPCost => 18;
public override int DurabilityCost => 0;
public override EffectType Effect => EffectType.Veneration; public override EffectType Effect => EffectType.Veneration;
public override byte Duration => 4; public override byte Duration => 4;
public override int CPCost(Simulator s) => 18;
} }
+5 -5
View File
@@ -6,14 +6,14 @@ internal sealed class WasteNot : BaseBuffAction
public override int Level => 15; public override int Level => 15;
public override uint ActionId => 4631; public override uint ActionId => 4631;
public override int CPCost => 56;
public override EffectType Effect => EffectType.WasteNot; public override EffectType Effect => EffectType.WasteNot;
public override byte Duration => 4; public override byte Duration => 4;
public override void UseSuccess() public override int CPCost(Simulator s) => 56;
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.RemoveEffect(EffectType.WasteNot2); s.RemoveEffect(EffectType.WasteNot2);
} }
} }
+5 -5
View File
@@ -6,14 +6,14 @@ internal sealed class WasteNot2 : BaseBuffAction
public override int Level => 47; public override int Level => 47;
public override uint ActionId => 4639; public override uint ActionId => 4639;
public override int CPCost => 98;
public override EffectType Effect => EffectType.WasteNot2; public override EffectType Effect => EffectType.WasteNot2;
public override byte Duration => 8; public override byte Duration => 8;
public override void UseSuccess() public override int CPCost(Simulator s) => 98;
public override void UseSuccess(Simulator s)
{ {
base.UseSuccess(); base.UseSuccess(s);
Simulation.RemoveEffect(EffectType.WasteNot); s.RemoveEffect(EffectType.WasteNot);
} }
} }
+11
View File
@@ -1,3 +1,5 @@
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Craftimizer.Simulator; namespace Craftimizer.Simulator;
@@ -16,6 +18,7 @@ public struct Effects
public byte Manipulation; public byte Manipulation;
public bool HeartAndSoul; public bool HeartAndSoul;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetDuration(EffectType effect, byte duration) public void SetDuration(EffectType effect, byte duration)
{ {
switch (effect) switch (effect)
@@ -54,12 +57,15 @@ public struct Effects
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Strengthen(EffectType effect) public void Strengthen(EffectType effect)
{ {
if (effect == EffectType.InnerQuiet && InnerQuiet < 10) if (effect == EffectType.InnerQuiet && InnerQuiet < 10)
InnerQuiet++; InnerQuiet++;
} }
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly byte GetDuration(EffectType effect) => public readonly byte GetDuration(EffectType effect) =>
effect switch effect switch
{ {
@@ -76,13 +82,18 @@ public struct Effects
_ => 0 _ => 0
}; };
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly byte GetStrength(EffectType effect) => public readonly byte GetStrength(EffectType effect) =>
effect == EffectType.InnerQuiet ? InnerQuiet : effect == EffectType.InnerQuiet ? InnerQuiet :
(byte)(GetDuration(effect) != 0 ? 1 : 0); (byte)(GetDuration(effect) != 0 ? 1 : 0);
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool HasEffect(EffectType effect) => public readonly bool HasEffect(EffectType effect) =>
GetDuration(effect) != 0; GetDuration(effect) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void DecrementDuration() public void DecrementDuration()
{ {
if (WasteNot > 0) if (WasteNot > 0)
+4 -4
View File
@@ -50,17 +50,17 @@ public class Simulator
if (IsComplete) if (IsComplete)
return ActionResponse.SimulationComplete; return ActionResponse.SimulationComplete;
var baseAction = action.With(this); var baseAction = action.Base();
if (!baseAction.CanUse) if (!baseAction.CanUse(this))
{ {
if (baseAction.Level > Input.Stats.Level) if (baseAction.Level > Input.Stats.Level)
return ActionResponse.ActionNotUnlocked; return ActionResponse.ActionNotUnlocked;
if (baseAction.CPCost > CP) if (baseAction.CPCost(this) > CP)
return ActionResponse.NotEnoughCP; return ActionResponse.NotEnoughCP;
return ActionResponse.CannotUseAction; return ActionResponse.CannotUseAction;
} }
baseAction.Use(); baseAction.Use(this);
ActionStates.MutateState(action); ActionStates.MutateState(action);
ActionCount++; ActionCount++;
+10 -3
View File
@@ -1,4 +1,5 @@
using Craftimizer.Simulator.Actions; using Craftimizer.Simulator.Actions;
using System.Diagnostics.Contracts;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@@ -42,23 +43,29 @@ public struct ActionSet
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int FromAction(ActionType action) => Array.IndexOf(Simulator.AcceptedActions, action); private static int FromAction(ActionType action) => Simulator.AcceptedActionsLUT[(byte)action];
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ActionType ToAction(int index) => Simulator.AcceptedActions[index]; private static ActionType ToAction(int index) => Simulator.AcceptedActions[index];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool HasAction(ActionType action) => (bits & (1u << (FromAction(action) + 1))) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddAction(ActionType action) => bits |= 1u << (FromAction(action) + 1); public void AddAction(ActionType action) => bits |= 1u << (FromAction(action) + 1);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAction(ActionType action) => bits &= ~(1u << (FromAction(action) + 1)); public void RemoveAction(ActionType action) => bits &= ~(1u << (FromAction(action) + 1));
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool HasAction(ActionType action) => (bits & (1u << (FromAction(action) + 1))) != 0;
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ActionType ElementAt(int index) => ToAction(NthBitSet(bits, index) - 1); public readonly ActionType ElementAt(int index) => ToAction(NthBitSet(bits, index) - 1);
[Pure]
public readonly int Count => BitOperations.PopCount(bits); public readonly int Count => BitOperations.PopCount(bits);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count)); public readonly ActionType SelectRandom(Random random) => ElementAt(random.Next(Count));
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ActionType First() => ElementAt(0); public readonly ActionType First() => ElementAt(0);
} }
+25 -10
View File
@@ -1,10 +1,12 @@
using Craftimizer.Simulator; using Craftimizer.Simulator;
using Craftimizer.Simulator.Actions; using Craftimizer.Simulator.Actions;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using Sim = Craftimizer.Simulator.Simulator; using Sim = Craftimizer.Simulator.Simulator;
namespace Craftimizer.Solver.Crafty; namespace Craftimizer.Solver.Crafty;
public class Simulator : Sim public sealed class Simulator : Sim
{ {
private readonly int maxStepCount; private readonly int maxStepCount;
@@ -52,12 +54,26 @@ public class Simulator : Sim
ActionType.BasicTouch, ActionType.BasicTouch,
}; };
// https://github.com/alostsock/crafty/blob/cffbd0cad8bab3cef9f52a3e3d5da4f5e3781842/crafty/src/craft_state.rs#L146 public static readonly int[] AcceptedActionsLUT;
private bool CanUseAction(ActionType action, bool strict)
{
var baseAction = action.WithUnsafe();
if (CalculateSuccessRate(baseAction.SuccessRate) != 1) static Simulator()
{
AcceptedActionsLUT = new int[Enum.GetValues<ActionType>().Length];
for (var i = 0; i < AcceptedActions.Length; i++)
AcceptedActionsLUT[(byte)AcceptedActions[i]] = i;
}
// 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 CanUseAction(ActionType action, bool strict)
#pragma warning restore MA0051 // Method is too long
{
var baseAction = action.Base();
if (CalculateSuccessRate(baseAction.SuccessRate(this)) != 1)
return false; return false;
// don't allow quality moves at max quality // don't allow quality moves at max quality
@@ -72,7 +88,7 @@ public class Simulator : Sim
{ {
// always used Trained Eye if it's available // always used Trained Eye if it's available
if (action == ActionType.TrainedEye) if (action == ActionType.TrainedEye)
return baseAction.CanUse; return baseAction.CanUse(this);
// only allow Focused moves after Observe // only allow Focused moves after Observe
if (ActionStates.Observed && if (ActionStates.Observed &&
@@ -94,7 +110,7 @@ public class Simulator : Sim
if (baseAction.IncreasesProgress) if (baseAction.IncreasesProgress)
{ {
var progressIncrease = CalculateProgressGain(baseAction.Efficiency); var progressIncrease = CalculateProgressGain(baseAction.Efficiency(this));
var wouldFinish = Progress + progressIncrease >= Input.Recipe.MaxProgress; var wouldFinish = Progress + progressIncrease >= Input.Recipe.MaxProgress;
if (wouldFinish) if (wouldFinish)
@@ -142,7 +158,7 @@ public class Simulator : Sim
return false; return false;
} }
return baseAction.CanUse; return baseAction.CanUse(this);
} }
// https://github.com/alostsock/crafty/blob/cffbd0cad8bab3cef9f52a3e3d5da4f5e3781842/crafty/src/craft_state.rs#L137 // https://github.com/alostsock/crafty/blob/cffbd0cad8bab3cef9f52a3e3d5da4f5e3781842/crafty/src/craft_state.rs#L137
@@ -151,7 +167,6 @@ public class Simulator : Sim
if (IsComplete) if (IsComplete)
return new(); return new();
ActionUtils.SetSimulation(this);
var ret = new ActionSet(); var ret = new ActionSet();
foreach (var action in AcceptedActions) foreach (var action in AcceptedActions)
if (CanUseAction(action, strict)) if (CanUseAction(action, strict))
+7 -11
View File
@@ -1,5 +1,6 @@
using Craftimizer.Simulator; using Craftimizer.Simulator;
using Craftimizer.Simulator.Actions; using Craftimizer.Simulator.Actions;
using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -32,20 +33,14 @@ public class Solver
{ {
} }
private (SimulationState NewState, CompletionState SimulatorCompletionState, ActionSet AvailableActions) ExecuteSimple(SimulationState state, ActionType action, bool strict)
{
(_, var newState) = Simulator.Execute(state, action);
return (newState, Simulator.CompletionState, Simulator.AvailableActionsHeuristic(strict));
}
private SimulationNode Execute(SimulationState state, ActionType action, bool strict) private SimulationNode Execute(SimulationState state, ActionType action, bool strict)
{ {
(var newState, var completionState, var newActions) = ExecuteSimple(state, action, strict); (_, var newState) = Simulator.Execute(state, action);
return new( return new(
newState, newState,
action, action,
completionState, Simulator.CompletionState,
newActions Simulator.AvailableActionsHeuristic(strict)
); );
} }
@@ -177,11 +172,12 @@ public class Solver
break; break;
randomAction = currentActions.SelectRandom(Random); randomAction = currentActions.SelectRandom(Random);
actions[actionCount++] = randomAction; actions[actionCount++] = randomAction;
(currentState, currentCompletionState, currentActions) = ExecuteSimple(currentState, randomAction, true); (_, currentState) = Simulator.Execute(currentState, randomAction);
currentCompletionState = Simulator.CompletionState;
currentActions = Simulator.AvailableActionsHeuristic(true);
} }
// store the result if a max score was reached // store the result if a max score was reached
currentCompletionState = SimulationNode.GetCompletionState(currentCompletionState, currentActions);
var score = SimulationNode.CalculateScoreForState(currentState, currentCompletionState, Config.MaxStepCount) ?? 0; var score = SimulationNode.CalculateScoreForState(currentState, currentCompletionState, Config.MaxStepCount) ?? 0;
if (currentCompletionState == CompletionState.ProgressComplete) if (currentCompletionState == CompletionState.ProgressComplete)
{ {