Reliability Simulation

This commit is contained in:
Asriel Camora
2023-11-12 21:23:15 +00:00
parent 4bf7397b0f
commit 44700abd4e
2 changed files with 127 additions and 13 deletions
+1
View File
@@ -82,6 +82,7 @@ public class Configuration : IPluginConfiguration
private List<Macro> macros { get; set; } = new(); private List<Macro> macros { get; set; } = new();
[JsonIgnore] [JsonIgnore]
public IReadOnlyList<Macro> Macros => macros; public IReadOnlyList<Macro> Macros => macros;
public int ReliabilitySimulationCount { get; set; } = 500;
public bool ConditionRandomness { get; set; } = true; public bool ConditionRandomness { get; set; } = true;
public SolverConfig SimulatorSolverConfig { get; set; } = SolverConfig.SimulatorDefault; public SolverConfig SimulatorSolverConfig { get; set; } = SolverConfig.SimulatorDefault;
public SolverConfig SynthHelperSolverConfig { get; set; } = SolverConfig.SynthHelperDefault; public SolverConfig SynthHelperSolverConfig { get; set; } = SolverConfig.SynthHelperDefault;
+125 -12
View File
@@ -21,6 +21,7 @@ using System.Numerics;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using RandomSim = Craftimizer.Simulator.Simulator;
using Sim = Craftimizer.Simulator.SimulatorNoRandom; using Sim = Craftimizer.Simulator.SimulatorNoRandom;
namespace Craftimizer.Windows; namespace Craftimizer.Windows;
@@ -76,16 +77,77 @@ public sealed class MacroEditor : Window, IDisposable
private List<int> HQIngredientCounts { get; set; } private List<int> HQIngredientCounts { get; set; }
private int StartingQuality => RecipeData.CalculateStartingQuality(HQIngredientCounts); private int StartingQuality => RecipeData.CalculateStartingQuality(HQIngredientCounts);
private readonly record struct SimulationReliablity
{
public record struct ParamReliability
{
public int Max { get; private set; }
public int Min { get; private set; }
public float Average { get; private set; }
public ParamReliability()
{
Min = int.MaxValue;
}
public void Add(int value)
{
Max = Math.Max(Max, value);
Min = Math.Min(Min, value);
Average += value;
}
public void Finalize(int count)
{
if (count != 0)
Average /= count;
}
}
public readonly ParamReliability Progress = new();
public readonly ParamReliability Quality = new();
// Param is either collectability, quality, or hq%, depending on the recipe
public readonly ParamReliability Param = new();
public SimulationReliablity(in SimulationState startState, IEnumerable<ActionType> actions, int iterCount, RecipeData recipeData)
{
Func<SimulationState, int> getParam;
if (recipeData.Recipe.ItemResult.Value!.IsCollectable)
getParam = s => s.Collectability;
else if (recipeData.Recipe.RequiredQuality > 0)
getParam = s => s.Quality;
else if (recipeData.RecipeInfo.MaxQuality > 0)
getParam = s => s.HQPercent;
else
getParam = s => 0;
for (var i = 0; i < iterCount; ++i)
{
var sim = new RandomSim(startState);
var (_, state, _) = sim.ExecuteMultiple(startState, actions);
Progress.Add(state.Progress);
Quality.Add(state.Quality);
Param.Add(getParam(state));
}
Progress.Finalize(iterCount);
Quality.Finalize(iterCount);
Param.Finalize(iterCount);
}
}
private sealed record SimulatedActionStep private sealed record SimulatedActionStep
{ {
public ActionType Action { get; init; } public ActionType Action { get; init; }
// State *after* executing the action // State *after* executing the action
public ActionResponse Response { get; set; } public ActionResponse Response { get; set; }
public SimulationState State { get; set; } public SimulationState State { get; set; }
public SimulationReliablity Reliability { get; set; }
}; };
private List<SimulatedActionStep> Macro { get; set; } = new(); private List<SimulatedActionStep> Macro { get; set; } = new();
private SimulationState InitialState { get; set; } private SimulationState InitialState { get; set; }
private SimulationState State => Macro.Count > 0 ? Macro[^1].State : InitialState; private SimulationState State => Macro.Count > 0 ? Macro[^1].State : InitialState;
private SimulationReliablity Reliability => Macro.Count > 0 ? Macro[^1].Reliability : new(InitialState, Array.Empty<ActionType>(), 0, RecipeData);
private ActionType[] DefaultActions { get; } private ActionType[] DefaultActions { get; }
private Action<IEnumerable<ActionType>>? MacroSetter { get; set; } private Action<IEnumerable<ActionType>>? MacroSetter { get; set; }
@@ -971,23 +1033,23 @@ public sealed class MacroEditor : Window, IDisposable
ImGui.TableNextColumn(); ImGui.TableNextColumn();
var datas = new List<BarData>(3) var datas = new List<BarData>(3)
{ {
new("Durability", Colors.Durability, State.Durability, RecipeData.RecipeInfo.MaxDurability, null, null), new("Durability", Colors.Durability, null, State.Durability, RecipeData.RecipeInfo.MaxDurability, null, null),
new("Condition", default, 0, 0, null, State.Condition) new("Condition", default, null, 0, 0, null, State.Condition)
}; };
if (RecipeData.Recipe.ItemResult.Value!.IsCollectable) if (RecipeData.Recipe.ItemResult.Value!.IsCollectable)
datas.Add(new("Collectability", Colors.HQ, State.Collectability, State.MaxCollectability, $"{State.Collectability}", null)); datas.Add(new("Collectability", Colors.HQ, Reliability.Param, State.Collectability, State.MaxCollectability, $"{State.Collectability}", null));
else if (RecipeData.Recipe.RequiredQuality > 0) else if (RecipeData.Recipe.RequiredQuality > 0)
datas.Add(new("Quality %", Colors.HQ, State.Quality, RecipeData.Recipe.RequiredQuality, $"{(float)State.Quality / RecipeData.Recipe.RequiredQuality * 100:0}%", null)); datas.Add(new("Quality %", Colors.HQ, Reliability.Param, State.Quality, RecipeData.Recipe.RequiredQuality, $"{(float)State.Quality / RecipeData.Recipe.RequiredQuality * 100:0}%", null));
else if (RecipeData.RecipeInfo.MaxQuality > 0) else if (RecipeData.RecipeInfo.MaxQuality > 0)
datas.Add(new("HQ %", Colors.HQ, State.HQPercent, 100, $"{State.HQPercent}%", null)); datas.Add(new("HQ %", Colors.HQ, Reliability.Param, State.HQPercent, 100, $"{State.HQPercent}%", null));
DrawBars(datas); DrawBars(datas);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
datas = new List<BarData>(3) datas = new List<BarData>(3)
{ {
new("Progress", Colors.Progress, State.Progress, RecipeData.RecipeInfo.MaxProgress, null, null), new("Progress", Colors.Progress, Reliability.Progress, State.Progress, RecipeData.RecipeInfo.MaxProgress, null, null),
new("Quality", Colors.Quality, State.Quality, RecipeData.RecipeInfo.MaxQuality, null, null), new("Quality", Colors.Quality, Reliability.Quality, State.Quality, RecipeData.RecipeInfo.MaxQuality, null, null),
new("CP", Colors.CP, State.CP, CharacterStats.CP, null, null) new("CP", Colors.CP, null, State.CP, CharacterStats.CP, null, null)
}; };
if (RecipeData.RecipeInfo.MaxQuality <= 0) if (RecipeData.RecipeInfo.MaxQuality <= 0)
datas.RemoveAt(1); datas.RemoveAt(1);
@@ -1034,7 +1096,7 @@ public sealed class MacroEditor : Window, IDisposable
} }
} }
private readonly record struct BarData(string Name, Vector4 Color, float Value, float Max, string? Caption, Condition? Condition); private readonly record struct BarData(string Name, Vector4 Color, SimulationReliablity.ParamReliability? Reliability, float Value, float Max, string? Caption, Condition? Condition);
private void DrawBars(IEnumerable<BarData> bars) private void DrawBars(IEnumerable<BarData> bars)
{ {
var spacing = ImGui.GetStyle().ItemSpacing.X; var spacing = ImGui.GetStyle().ItemSpacing.X;
@@ -1074,8 +1136,46 @@ public sealed class MacroEditor : Window, IDisposable
} }
else else
{ {
using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, bar.Color)) if (bar.Reliability is { } reliability)
{
// blend rgb colors, assume alpha is always 1
static Vector4 BlendColor(Vector4 A, Vector4 B)
{
return new(
A.X * (1 - B.W) + B.X * B.W,
A.Y * (1 - B.W) + B.Y * B.W,
A.Z * (1 - B.W) + B.Z * B.W,
1);
}
var relBars = new (float Value, Vector4 Color)[]
{
(bar.Value, bar.Color),
(reliability.Average, BlendColor(bar.Color, new(.5f,.5f,.5f,.6f))),
(reliability.Min, BlendColor(bar.Color, new(0,0,0,.6f))),
(reliability.Max, BlendColor(bar.Color, new(1,1,1,.6f))),
}.DistinctBy(v => Math.Clamp(v.Value, 0, bar.Max)).OrderByDescending(v => v.Value);
var i = 0;
var pos = ImGui.GetCursorPos();
foreach (var relBar in relBars)
{
ImGui.SetCursorPos(pos);
{
using var frameColor = ImRaii.PushColor(ImGuiCol.FrameBg, Vector4.Zero, i != 0);
using var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, relBar.Color);
ImGui.ProgressBar(Math.Clamp(relBar.Value / bar.Max, 0, 1), new(barSize, ImGui.GetFrameHeight()), string.Empty);
}
if (i++ == 0)
{
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"Min: {reliability.Min}\nAvg: {reliability.Average:0.##}\nMax: {reliability.Max}");
}
}
}
else
{
using var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, bar.Color);
ImGui.ProgressBar(Math.Clamp(bar.Value / bar.Max, 0, 1), new(barSize, ImGui.GetFrameHeight()), string.Empty); ImGui.ProgressBar(Math.Clamp(bar.Value / bar.Max, 0, 1), new(barSize, ImGui.GetFrameHeight()), string.Empty);
}
ImGui.SameLine(0, spacing); ImGui.SameLine(0, spacing);
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
if (bar.Caption is { } caption) if (bar.Caption is { } caption)
@@ -1420,6 +1520,7 @@ public sealed class MacroEditor : Window, IDisposable
popupImportUrlTokenSource = null; popupImportUrlTokenSource = null;
} }
} }
private void CalculateBestMacro() private void CalculateBestMacro()
{ {
SolverTokenSource?.Cancel(); SolverTokenSource?.Cancel();
@@ -1485,8 +1586,11 @@ public sealed class MacroEditor : Window, IDisposable
InitialState = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo, StartingQuality)); InitialState = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo, StartingQuality));
var sim = new Sim(InitialState); var sim = new Sim(InitialState);
var lastState = InitialState; var lastState = InitialState;
foreach (var step in Macro) for (var i = 0; i < Macro.Count; i++)
lastState = ((step.Response, step.State) = sim.Execute(lastState, step.Action)).State; {
lastState = ((Macro[i].Response, Macro[i].State) = sim.Execute(lastState, Macro[i].Action)).State;
Macro[i].Reliability = new(InitialState, Macro.Take(i + 1).Select(s => s.Action), Service.Configuration.ReliabilitySimulationCount, RecipeData);
}
} }
private void AddStep(ActionType action, int index = -1, bool isSolver = false) private void AddStep(ActionType action, int index = -1, bool isSolver = false)
@@ -1503,6 +1607,7 @@ public sealed class MacroEditor : Window, IDisposable
var sim = new Sim(State); var sim = new Sim(State);
var resp = sim.Execute(State, action); var resp = sim.Execute(State, action);
Macro.Add(new() { Action = action, Response = resp.Response, State = resp.NewState }); Macro.Add(new() { Action = action, Response = resp.Response, State = resp.NewState });
Macro[^1].Reliability = new(InitialState, Macro.Select(s => s.Action), Service.Configuration.ReliabilitySimulationCount, RecipeData);
} }
else else
{ {
@@ -1510,9 +1615,14 @@ public sealed class MacroEditor : Window, IDisposable
var sim = new Sim(state); var sim = new Sim(state);
var resp = sim.Execute(state, action); var resp = sim.Execute(state, action);
Macro.Insert(index, new() { Action = action, Response = resp.Response, State = resp.NewState }); Macro.Insert(index, new() { Action = action, Response = resp.Response, State = resp.NewState });
Macro[index].Reliability = new(InitialState, Macro.Take(index + 1).Select(s => s.Action), Service.Configuration.ReliabilitySimulationCount, RecipeData);
state = resp.NewState; state = resp.NewState;
for (var i = index + 1; i < Macro.Count; i++) for (var i = index + 1; i < Macro.Count; i++)
{
state = ((Macro[i].Response, Macro[i].State) = sim.Execute(state, Macro[i].Action)).State; state = ((Macro[i].Response, Macro[i].State) = sim.Execute(state, Macro[i].Action)).State;
Macro[i].Reliability = new(InitialState, Macro.Take(i + 1).Select(s => s.Action), Service.Configuration.ReliabilitySimulationCount, RecipeData);
}
} }
} }
@@ -1529,7 +1639,10 @@ public sealed class MacroEditor : Window, IDisposable
var state = index == 0 ? InitialState : Macro[index - 1].State; var state = index == 0 ? InitialState : Macro[index - 1].State;
var sim = new Sim(state); var sim = new Sim(state);
for (var i = index; i < Macro.Count; i++) for (var i = index; i < Macro.Count; i++)
{
state = ((Macro[i].Response, Macro[i].State) = sim.Execute(state, Macro[i].Action)).State; state = ((Macro[i].Response, Macro[i].State) = sim.Execute(state, Macro[i].Action)).State;
Macro[i].Reliability = new(InitialState, Macro.Take(i + 1).Select(s => s.Action), Service.Configuration.ReliabilitySimulationCount, RecipeData);
}
} }
public void Dispose() public void Dispose()