Merge branch 'condition-furcation' into main
This commit is contained in:
@@ -106,7 +106,7 @@ internal static class Program
|
|||||||
MaxStepCount = 30,
|
MaxStepCount = 30,
|
||||||
};
|
};
|
||||||
|
|
||||||
var sim = new SimulatorNoRandom(new(input));
|
var sim = new SimulatorNoRandom();
|
||||||
(_, var state) = sim.Execute(new(input), ActionType.MuscleMemory);
|
(_, var state) = sim.Execute(new(input), ActionType.MuscleMemory);
|
||||||
(_, state) = sim.Execute(state, ActionType.PrudentTouch);
|
(_, state) = sim.Execute(state, ActionType.PrudentTouch);
|
||||||
//(_, state) = sim.Execute(state, ActionType.Manipulation);
|
//(_, state) = sim.Execute(state, ActionType.Manipulation);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DalamudPackager" Version="2.1.12" />
|
<PackageReference Include="DalamudPackager" Version="2.1.12" />
|
||||||
|
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
|
||||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.106">
|
<PackageReference Include="Meziantou.Analyzer" Version="2.0.106">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Craftimizer;
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
internal static unsafe class ImGuiExtras
|
internal static unsafe class ImGuiExtras
|
||||||
{
|
{
|
||||||
|
|||||||
+113
-33
@@ -2,12 +2,16 @@ using Craftimizer.Utils;
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using ImPlotNET;
|
||||||
|
using MathNet.Numerics.Statistics;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -130,37 +134,6 @@ internal static class ImGuiUtils
|
|||||||
ImGui.PopID();
|
ImGui.PopID();
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct EndUnconditionally : ImRaii.IEndObject, IDisposable
|
|
||||||
{
|
|
||||||
private Action EndAction { get; }
|
|
||||||
|
|
||||||
public bool Success { get; }
|
|
||||||
|
|
||||||
public bool Disposed { get; private set; }
|
|
||||||
|
|
||||||
public EndUnconditionally(Action endAction, bool success)
|
|
||||||
{
|
|
||||||
EndAction = endAction;
|
|
||||||
Success = success;
|
|
||||||
Disposed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!Disposed)
|
|
||||||
{
|
|
||||||
EndAction();
|
|
||||||
Disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ImRaii.IEndObject GroupPanel(string name, float width, out float internalWidth)
|
|
||||||
{
|
|
||||||
internalWidth = BeginGroupPanel(name, width);
|
|
||||||
return new EndUnconditionally(EndGroupPanel, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector2 UnitCircle(float theta)
|
private static Vector2 UnitCircle(float theta)
|
||||||
{
|
{
|
||||||
var (s, c) = MathF.SinCos(theta);
|
var (s, c) = MathF.SinCos(theta);
|
||||||
@@ -168,6 +141,7 @@ internal static class ImGuiUtils
|
|||||||
return new Vector2(c, -s);
|
return new Vector2(c, -s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static float Lerp(float a, float b, float t) =>
|
private static float Lerp(float a, float b, float t) =>
|
||||||
MathF.FusedMultiplyAdd(b - a, t, a);
|
MathF.FusedMultiplyAdd(b - a, t, a);
|
||||||
|
|
||||||
@@ -251,6 +225,72 @@ internal static class ImGuiUtils
|
|||||||
Arc(MathF.PI / 2, MathF.PI / 2 - MathF.Tau * Math.Clamp(value, 0, 1), radiusInner, radiusOuter, backgroundColor, filledColor);
|
Arc(MathF.PI / 2, MathF.PI / 2 - MathF.Tau * Math.Clamp(value, 0, 1), radiusInner, radiusOuter, backgroundColor, filledColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class ViolinData
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Point
|
||||||
|
{
|
||||||
|
public float X, Y, Y2;
|
||||||
|
|
||||||
|
public Point(float x, float y, float y2)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Y2 = y2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<Point> Data => (DataArray ?? Array.Empty<Point>()).AsSpan();
|
||||||
|
private Point[]? DataArray { get; set; }
|
||||||
|
public readonly float Min;
|
||||||
|
public readonly float Max;
|
||||||
|
|
||||||
|
public ViolinData(IEnumerable<int> samples, float min, float max, int resolution, double bandwidth)
|
||||||
|
{
|
||||||
|
Min = min;
|
||||||
|
Max = max;
|
||||||
|
bandwidth *= Max - Min;
|
||||||
|
var samplesList = samples.AsParallel().Select(s => (double)s).ToArray();
|
||||||
|
_ = Task.Run(() => {
|
||||||
|
var s = Stopwatch.StartNew();
|
||||||
|
var data = ParallelEnumerable.Range(0, resolution + 1)
|
||||||
|
.Select(n => Lerp(min, max, n / (float)resolution))
|
||||||
|
.Select(n => (n, (float)KernelDensity.EstimateGaussian(n, bandwidth, samplesList)))
|
||||||
|
.Select(n => new Point(n.n, n.Item2, -n.Item2));
|
||||||
|
DataArray = data.ToArray();
|
||||||
|
s.Stop();
|
||||||
|
Log.Debug($"Violin plot processing took {s.Elapsed.TotalMilliseconds:0.00}ms");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ViolinPlot(in ViolinData data, Vector2 size)
|
||||||
|
{
|
||||||
|
using var padding = ImRaii2.PushStyle(ImPlotStyleVar.PlotPadding, Vector2.Zero);
|
||||||
|
using var plotBg = ImRaii2.PushColor(ImPlotCol.PlotBg, Vector4.Zero);
|
||||||
|
using var fill = ImRaii2.PushColor(ImPlotCol.Fill, Vector4.One.WithAlpha(.5f));
|
||||||
|
|
||||||
|
using var plot = ImRaii2.Plot("##violin", size, ImPlotFlags.CanvasOnly | ImPlotFlags.NoInputs | ImPlotFlags.NoChild | ImPlotFlags.NoFrame);
|
||||||
|
if (plot)
|
||||||
|
{
|
||||||
|
ImPlot.SetupAxes(null, null, ImPlotAxisFlags.NoDecorations, ImPlotAxisFlags.NoDecorations | ImPlotAxisFlags.AutoFit);
|
||||||
|
ImPlot.SetupAxisLimits(ImAxis.X1, data.Min, data.Max, ImPlotCond.Always);
|
||||||
|
ImPlot.SetupFinish();
|
||||||
|
|
||||||
|
if (data.Data is { } points && !points.IsEmpty)
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var label_id = stackalloc byte[] { (byte)'\0' };
|
||||||
|
fixed (ViolinData.Point* p = points)
|
||||||
|
{
|
||||||
|
ImPlotNative.ImPlot_PlotShaded_FloatPtrFloatPtrFloatPtr(label_id, &p->X, &p->Y, &p->Y2, points.Length, ImPlotShadedFlags.None, 0, sizeof(ViolinData.Point));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class SearchableComboData<T> where T : class
|
private sealed class SearchableComboData<T> where T : class
|
||||||
{
|
{
|
||||||
public readonly ImmutableArray<T> items;
|
public readonly ImmutableArray<T> items;
|
||||||
@@ -467,10 +507,50 @@ internal static class ImGuiUtils
|
|||||||
return ImGuiExtras.InputTextEx(label, hint, ref input, maxLength, size, flags | Multiline, callback, user_data);
|
return ImGuiExtras.InputTextEx(label, hint, ref input, maxLength, size, flags | Multiline, callback, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IconButtonSized(FontAwesomeIcon icon, Vector2 size)
|
private static Vector2 GetIconSize(FontAwesomeIcon icon)
|
||||||
{
|
{
|
||||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||||
var ret = ImGui.Button(icon.ToIconString(), size);
|
return ImGui.CalcTextSize(icon.ToIconString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DrawCenteredIcon(FontAwesomeIcon icon, Vector2 offset, Vector2 size)
|
||||||
|
{
|
||||||
|
var iconSize = GetIconSize(icon);
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
Vector2 iconOffset;
|
||||||
|
if (iconSize.X > iconSize.Y)
|
||||||
|
{
|
||||||
|
scale = size.X / iconSize.X;
|
||||||
|
iconOffset = new(0, (size.Y - (iconSize.Y * scale)) / 2f);
|
||||||
|
}
|
||||||
|
else if (iconSize.Y > iconSize.X)
|
||||||
|
{
|
||||||
|
scale = size.Y / iconSize.Y;
|
||||||
|
iconOffset = new((size.X - (iconSize.X * scale)) / 2f, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scale = size.X / iconSize.X;
|
||||||
|
iconOffset = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.GetWindowDrawList().AddText(UiBuilder.IconFont, UiBuilder.IconFont.FontSize * scale, offset + iconOffset, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IconButtonSquare(FontAwesomeIcon icon, float size = -1)
|
||||||
|
{
|
||||||
|
var ret = false;
|
||||||
|
|
||||||
|
var buttonSize = new Vector2(size == -1 ? ImGui.GetFrameHeight() : size);
|
||||||
|
var pos = ImGui.GetCursorScreenPos();
|
||||||
|
var spacing = new Vector2(ImGui.GetStyle().FramePadding.Y);
|
||||||
|
|
||||||
|
if (ImGui.Button($"###{icon.ToIconString()}", buttonSize))
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
DrawCenteredIcon(icon, pos + spacing, buttonSize - spacing * 2);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImPlotNET;
|
||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Craftimizer.Plugin;
|
||||||
|
|
||||||
|
public static class ImRaii2
|
||||||
|
{
|
||||||
|
private struct EndUnconditionally : ImRaii.IEndObject, IDisposable
|
||||||
|
{
|
||||||
|
private Action EndAction { get; }
|
||||||
|
|
||||||
|
public bool Success { get; }
|
||||||
|
|
||||||
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
|
public EndUnconditionally(Action endAction, bool success)
|
||||||
|
{
|
||||||
|
EndAction = endAction;
|
||||||
|
Success = success;
|
||||||
|
Disposed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!Disposed)
|
||||||
|
{
|
||||||
|
EndAction();
|
||||||
|
Disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct EndConditionally : ImRaii.IEndObject, IDisposable
|
||||||
|
{
|
||||||
|
public bool Success { get; }
|
||||||
|
|
||||||
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
|
private Action EndAction { get; }
|
||||||
|
|
||||||
|
public EndConditionally(Action endAction, bool success)
|
||||||
|
{
|
||||||
|
EndAction = endAction;
|
||||||
|
Success = success;
|
||||||
|
Disposed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!Disposed)
|
||||||
|
{
|
||||||
|
if (Success)
|
||||||
|
{
|
||||||
|
EndAction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImRaii.IEndObject GroupPanel(string name, float width, out float internalWidth)
|
||||||
|
{
|
||||||
|
internalWidth = ImGuiUtils.BeginGroupPanel(name, width);
|
||||||
|
return new EndUnconditionally(ImGuiUtils.EndGroupPanel, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImRaii.IEndObject Plot(string title_id, Vector2 size, ImPlotFlags flags)
|
||||||
|
{
|
||||||
|
return new EndConditionally(new Action(ImPlot.EndPlot), ImPlot.BeginPlot(title_id, size, flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImRaii.IEndObject PushStyle(ImPlotStyleVar idx, Vector2 val)
|
||||||
|
{
|
||||||
|
ImPlot.PushStyleVar(idx, val);
|
||||||
|
return new EndUnconditionally(ImPlot.PopStyleVar, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImRaii.IEndObject PushStyle(ImPlotStyleVar idx, float val)
|
||||||
|
{
|
||||||
|
ImPlot.PushStyleVar(idx, val);
|
||||||
|
return new EndUnconditionally(ImPlot.PopStyleVar, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImRaii.IEndObject PushColor(ImPlotCol idx, Vector4 col)
|
||||||
|
{
|
||||||
|
ImPlot.PushStyleColor(idx, col);
|
||||||
|
return new EndUnconditionally(ImPlot.PopStyleColor, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ public sealed class MacroClipboard : Window, IDisposable
|
|||||||
private void DrawMacro(int idx, string macro)
|
private void DrawMacro(int idx, string macro)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(idx);
|
using var id = ImRaii.PushId(idx);
|
||||||
using var panel = ImGuiUtils.GroupPanel($"Macro {idx + 1}", -1, out var availWidth);
|
using var panel = ImRaii2.GroupPanel($"Macro {idx + 1}", -1, out var availWidth);
|
||||||
|
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ public sealed class MacroClipboard : Window, IDisposable
|
|||||||
ImGui.SetCursorPos(buttonCursor);
|
ImGui.SetCursorPos(buttonCursor);
|
||||||
{
|
{
|
||||||
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(buttonActive ? ImGuiCol.ButtonActive : ImGuiCol.ButtonHovered), buttonHovered);
|
using var color = ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(buttonActive ? ImGuiCol.ButtonActive : ImGuiCol.ButtonHovered), buttonHovered);
|
||||||
ImGuiUtils.IconButtonSized(FontAwesomeIcon.Paste, new(ImGui.GetFrameHeight()));
|
ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste);
|
||||||
if (buttonClicked)
|
if (buttonClicked)
|
||||||
{
|
{
|
||||||
ImGui.SetClipboardText(macro);
|
ImGui.SetClipboardText(macro);
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ using System.Numerics;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Sim = Craftimizer.Simulator.SimulatorNoRandom;
|
using Sim = Craftimizer.Simulator.Simulator;
|
||||||
|
using SimNoRandom = Craftimizer.Simulator.SimulatorNoRandom;
|
||||||
|
|
||||||
namespace Craftimizer.Windows;
|
namespace Craftimizer.Windows;
|
||||||
|
|
||||||
@@ -76,22 +77,125 @@ 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 sealed class ParamReliability
|
||||||
|
{
|
||||||
|
private List<int> DataList { get; }
|
||||||
|
private ImGuiUtils.ViolinData? ViolinData { get; set; }
|
||||||
|
|
||||||
|
public int Max { get; private set; }
|
||||||
|
public int Min { get; private set; }
|
||||||
|
public float Median { get; private set; }
|
||||||
|
public float Average { get; private set; }
|
||||||
|
|
||||||
|
public ParamReliability()
|
||||||
|
{
|
||||||
|
DataList = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(int value)
|
||||||
|
{
|
||||||
|
DataList.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinalizeData()
|
||||||
|
{
|
||||||
|
if (DataList.Count == 0)
|
||||||
|
{
|
||||||
|
Average = Median = Max = Min = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Max = DataList.Max();
|
||||||
|
Min = DataList.Min();
|
||||||
|
if (DataList.Count % 2 == 0)
|
||||||
|
Median = (float)DataList.Order().Skip(DataList.Count / 2 - 1).Take(2).Average();
|
||||||
|
else
|
||||||
|
Median = DataList.Order().ElementAt(DataList.Count / 2);
|
||||||
|
Average = (float)DataList.Average();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImGuiUtils.ViolinData? GetViolinData(float barMax, int resolution, double bandwidth) =>
|
||||||
|
ViolinData ??=
|
||||||
|
Min != Max ?
|
||||||
|
new(DataList, 0, barMax, resolution, bandwidth) :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var reqQual = recipeData.Recipe.RequiredQuality;
|
||||||
|
getParam = s => (int)((float)s.Quality / reqQual * 100);
|
||||||
|
}
|
||||||
|
else if (recipeData.RecipeInfo.MaxQuality > 0)
|
||||||
|
getParam = s => s.HQPercent;
|
||||||
|
else
|
||||||
|
getParam = s => 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < iterCount; ++i)
|
||||||
|
{
|
||||||
|
var sim = new Sim();
|
||||||
|
var (_, state, _) = sim.ExecuteMultiple(startState, actions);
|
||||||
|
Progress.Add(state.Progress);
|
||||||
|
Quality.Add(state.Quality);
|
||||||
|
Param.Add(getParam(state));
|
||||||
|
}
|
||||||
|
Progress.FinalizeData();
|
||||||
|
Quality.FinalizeData();
|
||||||
|
Param.FinalizeData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private sealed record SimulatedActionStep
|
private sealed record SimulatedActionStep
|
||||||
{
|
{
|
||||||
public ActionType Action { get; init; }
|
public ActionType Action { get; }
|
||||||
// State *after* executing the action
|
// State *after* executing the action
|
||||||
public ActionResponse Response { get; set; }
|
public ActionResponse Response { get; private set; }
|
||||||
public SimulationState State { get; set; }
|
public SimulationState State { get; private set; }
|
||||||
|
private SimulationReliablity? Reliability { get; set; }
|
||||||
|
|
||||||
|
public SimulatedActionStep(ActionType action, Sim sim, in SimulationState lastState, out SimulationState newState)
|
||||||
|
{
|
||||||
|
Action = action;
|
||||||
|
newState = Recalculate(sim, lastState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationState Recalculate(Sim sim, in SimulationState lastState)
|
||||||
|
{
|
||||||
|
(Response, State) = sim.Execute(lastState, Action);
|
||||||
|
Reliability = null;
|
||||||
|
return State;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationReliablity GetReliability(in SimulationState initialState, IEnumerable<ActionType> actionSet, RecipeData recipeData) =>
|
||||||
|
Reliability ??=
|
||||||
|
new(initialState, actionSet, Service.Configuration.ReliabilitySimulationCount, recipeData);
|
||||||
};
|
};
|
||||||
|
|
||||||
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].GetReliability(InitialState, Macro.Select(m => m.Action), RecipeData) : 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; }
|
||||||
|
|
||||||
private CancellationTokenSource? SolverTokenSource { get; set; }
|
private CancellationTokenSource? SolverTokenSource { get; set; }
|
||||||
private Exception? SolverException { get; set; }
|
private Exception? SolverException { get; set; }
|
||||||
private int? SolverStartStepCount { get; set; }
|
private int? SolverStartStepCount { get; set; }
|
||||||
|
private object? SolverQueueLock { get; set; }
|
||||||
|
private List<SimulatedActionStep>? SolverQueuedSteps { get; set; }
|
||||||
private bool SolverRunning => SolverTokenSource != null;
|
private bool SolverRunning => SolverTokenSource != null;
|
||||||
|
|
||||||
private IDalamudTextureWrap ExpertBadge { get; }
|
private IDalamudTextureWrap ExpertBadge { get; }
|
||||||
@@ -114,7 +218,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
private CancellationTokenSource? popupImportUrlTokenSource;
|
private CancellationTokenSource? popupImportUrlTokenSource;
|
||||||
private MacroImport.RetrievedMacro? popupImportUrlMacro;
|
private MacroImport.RetrievedMacro? popupImportUrlMacro;
|
||||||
|
|
||||||
public MacroEditor(CharacterStats characterStats, RecipeData recipeData, CrafterBuffs buffs, IEnumerable<ActionType> actions, Action<IEnumerable<ActionType>>? setter) : base("Craftimizer Macro Editor", WindowFlags, false)
|
public MacroEditor(CharacterStats characterStats, RecipeData recipeData, CrafterBuffs buffs, IEnumerable<ActionType> actions, Action<IEnumerable<ActionType>>? setter) : base("Craftimizer Macro Editor", WindowFlags)
|
||||||
{
|
{
|
||||||
CharacterStats = characterStats;
|
CharacterStats = characterStats;
|
||||||
RecipeData = recipeData;
|
RecipeData = recipeData;
|
||||||
@@ -156,6 +260,11 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
SolverTokenSource?.Cancel();
|
SolverTokenSource?.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
TryFlushSolvedSteps();
|
||||||
|
}
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
var modifiedInput = false;
|
var modifiedInput = false;
|
||||||
@@ -298,24 +407,39 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
var imageButtonPadding = (int)(ImGui.GetStyle().FramePadding.Y / 2f);
|
var imageButtonPadding = (int)(ImGui.GetStyle().FramePadding.Y / 2f);
|
||||||
var imageButtonSize = imageSize - imageButtonPadding * 2;
|
var imageButtonSize = imageSize - imageButtonPadding * 2;
|
||||||
{
|
{
|
||||||
var v = CharacterStats.HasSplendorousBuff;
|
var splendorousLevel = 90;
|
||||||
var tint = v ? Vector4.One : disabledTint;
|
if (CharacterStats.HasSplendorousBuff && splendorousLevel > CharacterStats.Level)
|
||||||
if (ImGui.ImageButton(SplendorousBadge.ImGuiHandle, new Vector2(imageButtonSize), default, Vector2.One, imageButtonPadding, default, tint))
|
CharacterStats = CharacterStats with { HasSplendorousBuff = false };
|
||||||
CharacterStats = CharacterStats with { HasSplendorousBuff = !v };
|
|
||||||
if (ImGui.IsItemHovered())
|
using (var d = ImRaii.Disabled(splendorousLevel > CharacterStats.Level))
|
||||||
|
{
|
||||||
|
var v = CharacterStats.HasSplendorousBuff;
|
||||||
|
var tint = v ? Vector4.One : disabledTint;
|
||||||
|
if (ImGui.ImageButton(SplendorousBadge.ImGuiHandle, new Vector2(imageButtonSize), default, Vector2.One, imageButtonPadding, default, tint))
|
||||||
|
CharacterStats = CharacterStats with { HasSplendorousBuff = !v };
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
ImGui.SetTooltip(CharacterStats.HasSplendorousBuff ? $"Splendorous Tool" : "No Splendorous Tool");
|
ImGui.SetTooltip(CharacterStats.HasSplendorousBuff ? $"Splendorous Tool" : "No Splendorous Tool");
|
||||||
}
|
}
|
||||||
ImGui.SameLine(0, 5);
|
ImGui.SameLine(0, 5);
|
||||||
bool? newIsSpecialist = null;
|
bool? newIsSpecialist = null;
|
||||||
{
|
{
|
||||||
var v = CharacterStats.IsSpecialist;
|
var v = CharacterStats.IsSpecialist;
|
||||||
var tint = new Vector4(0.99f, 0.97f, 0.62f, 1f) * (v ? Vector4.One : disabledTint);
|
|
||||||
if (ImGui.ImageButton(SpecialistBadge.ImGuiHandle, new Vector2(imageButtonSize), default, Vector2.One, imageButtonPadding, default, tint))
|
var specialistLevel = 55;
|
||||||
|
if (CharacterStats.IsSpecialist && specialistLevel > CharacterStats.Level)
|
||||||
|
newIsSpecialist = v = false;
|
||||||
|
|
||||||
|
using (var d = ImRaii.Disabled(specialistLevel > CharacterStats.Level))
|
||||||
{
|
{
|
||||||
v = !v;
|
var tint = new Vector4(0.99f, 0.97f, 0.62f, 1f) * (v ? Vector4.One : disabledTint);
|
||||||
newIsSpecialist = v;
|
if (ImGui.ImageButton(SpecialistBadge.ImGuiHandle, new Vector2(imageButtonSize), default, Vector2.One, imageButtonPadding, default, tint))
|
||||||
|
{
|
||||||
|
v = !v;
|
||||||
|
newIsSpecialist = v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
ImGui.SetTooltip(v ? $"Specialist" : "Not a Specialist");
|
ImGui.SetTooltip(v ? $"Specialist" : "Not a Specialist");
|
||||||
}
|
}
|
||||||
ImGui.SameLine(0, 5);
|
ImGui.SameLine(0, 5);
|
||||||
@@ -904,7 +1028,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
|
|
||||||
private void DrawActionHotbars()
|
private void DrawActionHotbars()
|
||||||
{
|
{
|
||||||
var sim = new Sim(State);
|
var sim = CreateSim(State);
|
||||||
|
|
||||||
var imageSize = ImGui.GetFrameHeight() * 2;
|
var imageSize = ImGui.GetFrameHeight() * 2;
|
||||||
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
||||||
@@ -919,7 +1043,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var actions = category.GetActions();
|
var actions = category.GetActions();
|
||||||
using var panel = ImGuiUtils.GroupPanel(category.GetDisplayName(), -1, out var availSpace);
|
using var panel = ImRaii2.GroupPanel(category.GetDisplayName(), -1, out var availSpace);
|
||||||
var itemsPerRow = (int)MathF.Floor((availSpace + spacing) / (imageSize + spacing));
|
var itemsPerRow = (int)MathF.Floor((availSpace + spacing) / (imageSize + spacing));
|
||||||
var itemCount = actions.Count;
|
var itemCount = actions.Count;
|
||||||
var iterCount = (int)(Math.Ceiling((float)itemCount / itemsPerRow) * itemsPerRow);
|
var iterCount = (int)(Math.Ceiling((float)itemCount / itemsPerRow) * itemsPerRow);
|
||||||
@@ -971,23 +1095,26 @@ 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));
|
{
|
||||||
|
var qualityPercent = (float)State.Quality / RecipeData.Recipe.RequiredQuality * 100;
|
||||||
|
datas.Add(new("Quality %%", Colors.HQ, Reliability.Param, qualityPercent, 100, $"{qualityPercent: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);
|
||||||
@@ -995,7 +1122,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Buffs", -1, out _))
|
using (var panel = ImRaii2.GroupPanel("Buffs", -1, out _))
|
||||||
{
|
{
|
||||||
using var _font = ImRaii.PushFont(AxisFont.ImFont);
|
using var _font = ImRaii.PushFont(AxisFont.ImFont);
|
||||||
|
|
||||||
@@ -1034,7 +1161,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;
|
||||||
@@ -1053,13 +1180,16 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
var barSize = totalSize - textSize - spacing;
|
var barSize = totalSize - textSize - spacing;
|
||||||
foreach (var bar in bars)
|
foreach (var bar in bars)
|
||||||
{
|
{
|
||||||
using var panel = ImGuiUtils.GroupPanel(bar.Name, totalSize, out _);
|
using var panel = ImRaii2.GroupPanel(bar.Name, totalSize, out _);
|
||||||
if (bar.Condition is { } condition)
|
if (bar.Condition is { } condition)
|
||||||
{
|
{
|
||||||
|
var pos = ImGui.GetCursorPos();
|
||||||
using (var g = ImRaii.Group())
|
using (var g = ImRaii.Group())
|
||||||
{
|
{
|
||||||
|
var availSize = totalSize - (spacing + ImGui.GetFrameHeight());
|
||||||
var size = ImGui.GetFrameHeight() + spacing + ImGui.CalcTextSize(condition.Name()).X;
|
var size = ImGui.GetFrameHeight() + spacing + ImGui.CalcTextSize(condition.Name()).X;
|
||||||
ImGuiUtils.AlignCentered(size, totalSize);
|
|
||||||
|
ImGuiUtils.AlignCentered(size, availSize);
|
||||||
ImGui.GetWindowDrawList().AddCircleFilled(
|
ImGui.GetWindowDrawList().AddCircleFilled(
|
||||||
ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetFrameHeight() / 2),
|
ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetFrameHeight() / 2),
|
||||||
ImGui.GetFrameHeight() / 2,
|
ImGui.GetFrameHeight() / 2,
|
||||||
@@ -1070,12 +1200,49 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
ImGui.Text(condition.Name());
|
ImGui.Text(condition.Name());
|
||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip(condition.Description(CharacterStats.HasSplendorousBuff));
|
ImGui.SetTooltip(condition.Description(CharacterStats.HasSplendorousBuff).Replace("%", "%%"));
|
||||||
|
|
||||||
|
ImGui.SetCursorPos(pos);
|
||||||
|
ImGuiUtils.AlignRight(ImGui.GetFrameHeight(), totalSize);
|
||||||
|
|
||||||
|
using (var disabled = ImRaii.Disabled(SolverRunning))
|
||||||
|
{
|
||||||
|
using var tint = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled), !Service.Configuration.ConditionRandomness);
|
||||||
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Dice))
|
||||||
|
{
|
||||||
|
Service.Configuration.ConditionRandomness ^= true;
|
||||||
|
Service.Configuration.Save();
|
||||||
|
|
||||||
|
RecalculateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
|
ImGui.SetTooltip("Condition Randomness\n" +
|
||||||
|
"Allows the condition to fluctuate randomly like a real craft.\n" +
|
||||||
|
"Turns off when generating a macro.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var pos = ImGui.GetCursorPos();
|
||||||
using (var color = ImRaii.PushColor(ImGuiCol.PlotHistogram, bar.Color))
|
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);
|
||||||
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenOverlapped))
|
||||||
|
{
|
||||||
|
if (bar.Reliability is { } reliability)
|
||||||
|
{
|
||||||
|
if (reliability.GetViolinData(bar.Max, (int)(barSize / 5), 0.02) is { } violinData)
|
||||||
|
{
|
||||||
|
ImGui.SetCursorPos(pos);
|
||||||
|
ImGuiUtils.ViolinPlot(violinData, new(barSize, ImGui.GetFrameHeight()));
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
ImGui.SetTooltip(
|
||||||
|
$"Min: {reliability.Min}\n" +
|
||||||
|
$"Med: {reliability.Median:0.##}\n" +
|
||||||
|
$"Avg: {reliability.Average:0.##}\n" +
|
||||||
|
$"Max: {reliability.Max}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ImGui.SameLine(0, spacing);
|
ImGui.SameLine(0, spacing);
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
if (bar.Caption is { } caption)
|
if (bar.Caption is { } caption)
|
||||||
@@ -1098,7 +1265,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
var imageSize = ImGui.GetFrameHeight() * 2;
|
var imageSize = ImGui.GetFrameHeight() * 2;
|
||||||
var lastState = InitialState;
|
var lastState = InitialState;
|
||||||
|
|
||||||
using var panel = ImGuiUtils.GroupPanel("Macro", -1, out var availSpace);
|
using var panel = ImRaii2.GroupPanel("Macro", -1, out var availSpace);
|
||||||
ImGui.Dummy(new(0, imageSize));
|
ImGui.Dummy(new(0, imageSize));
|
||||||
ImGui.SameLine(0, 0);
|
ImGui.SameLine(0, 0);
|
||||||
|
|
||||||
@@ -1131,8 +1298,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
{
|
{
|
||||||
var sim = new Sim(lastState);
|
ImGui.SetTooltip($"{action.GetName(RecipeData!.ClassJob)}\n{actionBase.GetTooltip(CreateSim(lastState), true)}");
|
||||||
ImGui.SetTooltip($"{action.GetName(RecipeData!.ClassJob)}\n{actionBase.GetTooltip(sim, true)}");
|
|
||||||
}
|
}
|
||||||
lastState = state;
|
lastState = state;
|
||||||
}
|
}
|
||||||
@@ -1197,14 +1363,14 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
"can vary wildly depending on the solver's settings.");
|
"can vary wildly depending on the solver's settings.");
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Paste, new(height)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste))
|
||||||
Service.Plugin.CopyMacro(Macro.Select(s => s.Action).ToArray());
|
Service.Plugin.CopyMacro(Macro.Select(s => s.Action).ToArray());
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Copy to Clipboard");
|
ImGui.SetTooltip("Copy to Clipboard");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
||||||
{
|
{
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.FileImport, new(height)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.FileImport))
|
||||||
ShowImportPopup();
|
ShowImportPopup();
|
||||||
}
|
}
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
@@ -1215,7 +1381,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
{
|
{
|
||||||
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
||||||
{
|
{
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Undo, new(height)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Undo))
|
||||||
{
|
{
|
||||||
SolverStartStepCount = null;
|
SolverStartStepCount = null;
|
||||||
Macro.Clear();
|
Macro.Clear();
|
||||||
@@ -1229,7 +1395,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
using (var _disabled = ImRaii.Disabled(SolverRunning))
|
||||||
{
|
{
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Trash, new(height)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Trash))
|
||||||
{
|
{
|
||||||
SolverStartStepCount = null;
|
SolverStartStepCount = null;
|
||||||
Macro.Clear();
|
Macro.Clear();
|
||||||
@@ -1293,7 +1459,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
{
|
{
|
||||||
bool submittedText, submittedUrl;
|
bool submittedText, submittedUrl;
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("##text", -1, out var availWidth))
|
using (var panel = ImRaii2.GroupPanel("##text", -1, out var availWidth))
|
||||||
{
|
{
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGuiUtils.TextCentered("Paste your macro here");
|
ImGuiUtils.TextCentered("Paste your macro here");
|
||||||
@@ -1305,7 +1471,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
submittedText = ImGui.Button("Import", new(availWidth, 0));
|
submittedText = ImGui.Button("Import", new(availWidth, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("##url", -1, out var availWidth))
|
using (var panel = ImRaii2.GroupPanel("##url", -1, out var availWidth))
|
||||||
{
|
{
|
||||||
var availOffset = ImGui.GetContentRegionAvail().X - availWidth;
|
var availOffset = ImGui.GetContentRegionAvail().X - availWidth;
|
||||||
|
|
||||||
@@ -1405,7 +1571,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
if (popupImportUrlMacro is { Name: var name, Actions: var actions })
|
if (popupImportUrlMacro is { Name: var name, Actions: var actions })
|
||||||
{
|
{
|
||||||
Macro.Clear();
|
Macro.Clear();
|
||||||
foreach(var action in actions)
|
foreach (var action in actions)
|
||||||
AddStep(action);
|
AddStep(action);
|
||||||
Service.PluginInterface.UiBuilder.AddNotification($"Imported macro \"{name}\"", "Craftimizer Macro Imported", NotificationType.Success);
|
Service.PluginInterface.UiBuilder.AddNotification($"Imported macro \"{name}\"", "Craftimizer Macro Imported", NotificationType.Success);
|
||||||
|
|
||||||
@@ -1420,13 +1586,32 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
popupImportUrlTokenSource = null;
|
popupImportUrlTokenSource = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateBestMacro()
|
private void CalculateBestMacro()
|
||||||
{
|
{
|
||||||
SolverTokenSource?.Cancel();
|
SolverTokenSource?.Cancel();
|
||||||
SolverTokenSource = new();
|
SolverTokenSource = new();
|
||||||
SolverException = null;
|
SolverException = null;
|
||||||
|
if (SolverQueueLock is { })
|
||||||
|
{
|
||||||
|
lock (SolverQueueLock)
|
||||||
|
{
|
||||||
|
SolverQueuedSteps!.Clear();
|
||||||
|
SolverQueueLock = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SolverQueueLock = new();
|
||||||
|
SolverQueuedSteps ??= new();
|
||||||
|
|
||||||
RevertPreviousMacro();
|
RevertPreviousMacro();
|
||||||
|
|
||||||
|
if (Service.Configuration.ConditionRandomness)
|
||||||
|
{
|
||||||
|
Service.Configuration.ConditionRandomness = false;
|
||||||
|
Service.Configuration.Save();
|
||||||
|
RecalculateState();
|
||||||
|
}
|
||||||
|
|
||||||
SolverStartStepCount = Macro.Count;
|
SolverStartStepCount = Macro.Count;
|
||||||
|
|
||||||
var token = SolverTokenSource.Token;
|
var token = SolverTokenSource.Token;
|
||||||
@@ -1462,7 +1647,7 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
|
|
||||||
var solver = new Solver.Solver(config, state) { Token = token };
|
var solver = new Solver.Solver(config, state) { Token = token };
|
||||||
solver.OnLog += Log.Debug;
|
solver.OnLog += Log.Debug;
|
||||||
solver.OnNewAction += a => AddStep(a, isSolver: true);
|
solver.OnNewAction += QueueSolverStep;
|
||||||
solver.Start();
|
solver.Start();
|
||||||
_ = solver.GetTask().GetAwaiter().GetResult();
|
_ = solver.GetTask().GetAwaiter().GetResult();
|
||||||
|
|
||||||
@@ -1483,36 +1668,72 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
private void RecalculateState()
|
private void RecalculateState()
|
||||||
{
|
{
|
||||||
InitialState = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo, StartingQuality));
|
InitialState = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo, StartingQuality));
|
||||||
var sim = new Sim(InitialState);
|
var sim = CreateSim();
|
||||||
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].Recalculate(sim, lastState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddStep(ActionType action, int index = -1, bool isSolver = false)
|
private static Sim CreateSim() =>
|
||||||
|
Service.Configuration.ConditionRandomness ? new Sim() : new SimNoRandom();
|
||||||
|
|
||||||
|
private static Sim CreateSim(in SimulationState state) =>
|
||||||
|
Service.Configuration.ConditionRandomness ? new Sim() { State = state } : new SimNoRandom() { State = state };
|
||||||
|
|
||||||
|
private void AddStep(ActionType action, int index = -1)
|
||||||
{
|
{
|
||||||
if (index < -1 || index >= Macro.Count)
|
if (index < -1 || index >= Macro.Count)
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
if (!isSolver && SolverRunning)
|
if (SolverRunning)
|
||||||
throw new InvalidOperationException("Cannot add steps while solver is running");
|
throw new InvalidOperationException("Cannot add steps while solver is running");
|
||||||
if (!SolverRunning)
|
if (!SolverRunning)
|
||||||
SolverStartStepCount = null;
|
SolverStartStepCount = null;
|
||||||
|
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
var sim = new Sim(State);
|
var sim = CreateSim();
|
||||||
var resp = sim.Execute(State, action);
|
Macro.Add(new(action, sim, State, out _));
|
||||||
Macro.Add(new() { Action = action, Response = resp.Response, State = resp.NewState });
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
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 = CreateSim();
|
||||||
var resp = sim.Execute(state, action);
|
Macro.Insert(index, new(action, sim, state, out state));
|
||||||
Macro.Insert(index, new() { Action = action, Response = resp.Response, 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].Recalculate(sim, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void QueueSolverStep(ActionType action)
|
||||||
|
{
|
||||||
|
if (!SolverRunning)
|
||||||
|
throw new InvalidOperationException("Cannot queue steps while solver isn't running");
|
||||||
|
lock (SolverQueueLock!)
|
||||||
|
{
|
||||||
|
var lastState = SolverQueuedSteps!.Count > 0 ? SolverQueuedSteps[^1].State : State;
|
||||||
|
SolverQueuedSteps.Add(new(action, CreateSim(), lastState, out _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryFlushSolvedSteps()
|
||||||
|
{
|
||||||
|
if (SolverQueueLock == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (SolverQueueLock!)
|
||||||
|
{
|
||||||
|
if (SolverQueuedSteps!.Count > 0)
|
||||||
|
{
|
||||||
|
Macro.AddRange(SolverQueuedSteps);
|
||||||
|
SolverQueuedSteps.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SolverRunning)
|
||||||
|
{
|
||||||
|
SolverQueuedSteps.Clear();
|
||||||
|
SolverQueueLock = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1527,9 +1748,9 @@ public sealed class MacroEditor : Window, IDisposable
|
|||||||
Macro.RemoveAt(index);
|
Macro.RemoveAt(index);
|
||||||
|
|
||||||
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 = CreateSim();
|
||||||
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].Recalculate(sim, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
|
|
||||||
var stateNullable = GetMacroState(macro);
|
var stateNullable = GetMacroState(macro);
|
||||||
|
|
||||||
using var panel = ImGuiUtils.GroupPanel(macro.Name, -1, out var availWidth);
|
using var panel = ImRaii2.GroupPanel(macro.Name, -1, out var availWidth);
|
||||||
var stepsAvailWidthOffset = ImGui.GetContentRegionAvail().X - availWidth;
|
var stepsAvailWidthOffset = ImGui.GetContentRegionAvail().X - availWidth;
|
||||||
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
var spacing = ImGui.GetStyle().ItemSpacing.Y;
|
||||||
var miniRowHeight = (windowHeight - spacing) / 2f;
|
var miniRowHeight = (windowHeight - spacing) / 2f;
|
||||||
@@ -197,23 +197,23 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
{
|
{
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Paste, new(miniRowHeight)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste, miniRowHeight))
|
||||||
Service.Plugin.CopyMacro(macro.Actions);
|
Service.Plugin.CopyMacro(macro.Actions);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Copy to Clipboard");
|
ImGui.SetTooltip("Copy to Clipboard");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Trash, new(miniRowHeight)) && ImGui.GetIO().KeyShift)
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Trash, miniRowHeight) && ImGui.GetIO().KeyShift)
|
||||||
Service.Configuration.RemoveMacro(macro);
|
Service.Configuration.RemoveMacro(macro);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Delete (Hold Shift)");
|
ImGui.SetTooltip("Delete (Hold Shift)");
|
||||||
|
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.PencilAlt, new(miniRowHeight)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.PencilAlt, miniRowHeight))
|
||||||
ShowRenamePopup(macro);
|
ShowRenamePopup(macro);
|
||||||
DrawRenamePopup(macro);
|
DrawRenamePopup(macro);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Rename");
|
ImGui.SetTooltip("Rename");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Edit, new(miniRowHeight)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Edit, miniRowHeight))
|
||||||
OpenEditor(macro);
|
OpenEditor(macro);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Open in Simulator");
|
ImGui.SetTooltip("Open in Simulator");
|
||||||
@@ -329,7 +329,7 @@ public sealed class MacroList : Window, IDisposable
|
|||||||
return state;
|
return state;
|
||||||
|
|
||||||
state = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo));
|
state = new SimulationState(new(CharacterStats, RecipeData.RecipeInfo));
|
||||||
var sim = new Sim(state);
|
var sim = new Sim();
|
||||||
(_, state, _) = sim.ExecuteMultiple(state, macro.Actions);
|
(_, state, _) = sim.ExecuteMultiple(state, macro.Actions);
|
||||||
return MacroStateCache[macro] = state;
|
return MacroStateCache[macro] = state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var panelWidth = availWidth - ImGui.GetStyle().ItemSpacing.X * 2;
|
var panelWidth = availWidth - ImGui.GetStyle().ItemSpacing.X * 2;
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Best Saved Macro", panelWidth, out _))
|
using (var panel = ImRaii2.GroupPanel("Best Saved Macro", panelWidth, out _))
|
||||||
{
|
{
|
||||||
var stepsPanelWidthOffset = ImGui.GetContentRegionAvail().X - panelWidth;
|
var stepsPanelWidthOffset = ImGui.GetContentRegionAvail().X - panelWidth;
|
||||||
if (BestSavedMacro is { } savedMacro)
|
if (BestSavedMacro is { } savedMacro)
|
||||||
@@ -228,7 +228,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable
|
|||||||
DrawMacro(null, null, stepsPanelWidthOffset, true);
|
DrawMacro(null, null, stepsPanelWidthOffset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Suggested Macro", panelWidth, out _))
|
using (var panel = ImRaii2.GroupPanel("Suggested Macro", panelWidth, out _))
|
||||||
{
|
{
|
||||||
var stepsPanelWidthOffset = ImGui.GetContentRegionAvail().X - panelWidth;
|
var stepsPanelWidthOffset = ImGui.GetContentRegionAvail().X - panelWidth;
|
||||||
if (BestSuggestedMacro is { } suggestedMacro)
|
if (BestSuggestedMacro is { } suggestedMacro)
|
||||||
@@ -644,11 +644,11 @@ public sealed unsafe class RecipeNote : Window, IDisposable
|
|||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
{
|
{
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Edit, new(miniRowHeight)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Edit, miniRowHeight))
|
||||||
Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), macro.Actions, setter);
|
Service.Plugin.OpenMacroEditor(CharacterStats!, RecipeData!, new(Service.ClientState.LocalPlayer!.StatusList), macro.Actions, setter);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Open in Simulator");
|
ImGui.SetTooltip("Open in Simulator");
|
||||||
if (ImGuiUtils.IconButtonSized(FontAwesomeIcon.Paste, new(miniRowHeight)))
|
if (ImGuiUtils.IconButtonSquare(FontAwesomeIcon.Paste, miniRowHeight))
|
||||||
Service.Plugin.CopyMacro(macro.Actions);
|
Service.Plugin.CopyMacro(macro.Actions);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Copy to Clipboard");
|
ImGui.SetTooltip("Copy to Clipboard");
|
||||||
@@ -824,7 +824,7 @@ public sealed unsafe class RecipeNote : Window, IDisposable
|
|||||||
var state = new SimulationState(input);
|
var state = new SimulationState(input);
|
||||||
var config = Service.Configuration.SimulatorSolverConfig;
|
var config = Service.Configuration.SimulatorSolverConfig;
|
||||||
var mctsConfig = new MCTSConfig(config);
|
var mctsConfig = new MCTSConfig(config);
|
||||||
var simulator = new SimulatorNoRandom(state);
|
var simulator = new SimulatorNoRandom();
|
||||||
List<Macro> macros = new(Service.Configuration.Macros);
|
List<Macro> macros = new(Service.Configuration.Macros);
|
||||||
|
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ImGui.SetTooltip("Disabled temporarily for testing");
|
ImGui.SetTooltip("Disabled temporarily for testing");
|
||||||
|
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Show Only One Macro Stat",
|
"Show Only One Macro Stat in Crafting Log",
|
||||||
"Only one stat will be shown for a macro. If a craft will be finished, quality\n" +
|
"Only one stat will be shown for a macro. If a craft will be finished, quality\n" +
|
||||||
"is shown. Otherwise, progress is shown. Durability and remaining CP will be\n" +
|
"is shown. Otherwise, progress is shown. Durability and remaining CP will be\n" +
|
||||||
"hidden.",
|
"hidden.",
|
||||||
@@ -200,9 +200,21 @@ public sealed class Settings : Window, IDisposable
|
|||||||
ref isDirty
|
ref isDirty
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DrawOption(
|
||||||
|
"Reliability Trial Count",
|
||||||
|
"When testing for reliability of a macro in the editor, this many trials will be\n" +
|
||||||
|
"run. You should set this value to at least 100 to get a reliable spread of data.\n" +
|
||||||
|
"If it's too low, you may not find an outlier, and the average might be skewed.",
|
||||||
|
Config.ReliabilitySimulationCount,
|
||||||
|
5,
|
||||||
|
5000,
|
||||||
|
v => Config.ReliabilitySimulationCount = v,
|
||||||
|
ref isDirty
|
||||||
|
);
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Copying Settings", -1, out _))
|
using (var panel = ImRaii2.GroupPanel("Copying Settings", -1, out _))
|
||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Macro Copy Method",
|
"Macro Copy Method",
|
||||||
@@ -380,7 +392,7 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
var config = configRef;
|
var config = configRef;
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("General", -1, out _))
|
using (var panel = ImRaii2.GroupPanel("General", -1, out _))
|
||||||
{
|
{
|
||||||
if (ImGui.Button("Reset to Default", OptionButtonSize))
|
if (ImGui.Button("Reset to Default", OptionButtonSize))
|
||||||
{
|
{
|
||||||
@@ -501,7 +513,7 @@ public sealed class Settings : Window, IDisposable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Advanced", -1, out _))
|
using (var panel = ImRaii2.GroupPanel("Advanced", -1, out _))
|
||||||
{
|
{
|
||||||
DrawOption(
|
DrawOption(
|
||||||
"Score Storage Threshold",
|
"Score Storage Threshold",
|
||||||
@@ -538,7 +550,7 @@ public sealed class Settings : Window, IDisposable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var panel = ImGuiUtils.GroupPanel("Score Weights (Advanced)", -1, out _))
|
using (var panel = ImRaii2.GroupPanel("Score Weights (Advanced)", -1, out _))
|
||||||
{
|
{
|
||||||
ImGui.TextWrapped("All values should add up to 1. Otherwise, the Score Storage Threshold should be changed.");
|
ImGui.TextWrapped("All values should add up to 1. Otherwise, the Score Storage Threshold should be changed.");
|
||||||
ImGuiHelpers.ScaledDummy(10);
|
ImGuiHelpers.ScaledDummy(10);
|
||||||
@@ -629,25 +641,6 @@ public sealed class Settings : Window, IDisposable
|
|||||||
|
|
||||||
var isDirty = false;
|
var isDirty = false;
|
||||||
|
|
||||||
using (var g = ImRaii.Group())
|
|
||||||
{
|
|
||||||
using var d = ImRaii.Disabled();
|
|
||||||
DrawOption(
|
|
||||||
"Condition Randomness",
|
|
||||||
"Allows the simulator condition to fluctuate randomly like a real craft.\n" +
|
|
||||||
"Turns off when generating a macro.",
|
|
||||||
Config.ConditionRandomness,
|
|
||||||
v => Config.ConditionRandomness = v,
|
|
||||||
ref isDirty
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip("Disabled temporarily for testing");
|
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
|
||||||
ImGui.Separator();
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
|
||||||
|
|
||||||
var solverConfig = Config.SimulatorSolverConfig;
|
var solverConfig = Config.SimulatorSolverConfig;
|
||||||
DrawSolverConfig(ref solverConfig, SolverConfig.SimulatorDefault, out var isSolverDirty);
|
DrawSolverConfig(ref solverConfig, SolverConfig.SimulatorDefault, out var isSolverDirty);
|
||||||
if (isSolverDirty)
|
if (isSolverDirty)
|
||||||
|
|||||||
@@ -8,6 +8,12 @@
|
|||||||
"resolved": "2.1.12",
|
"resolved": "2.1.12",
|
||||||
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||||
},
|
},
|
||||||
|
"MathNet.Numerics": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[5.0.0, )",
|
||||||
|
"resolved": "5.0.0",
|
||||||
|
"contentHash": "pg1W2VwaEQMAiTpGK840hZgzavnqjlCMTVSbtVCXVyT+7AX4mc1o89SPv4TBlAjhgCOo9c1Y+jZ5m3ti2YgGgA=="
|
||||||
|
},
|
||||||
"Meziantou.Analyzer": {
|
"Meziantou.Analyzer": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.0.106, )",
|
"requested": "[2.0.106, )",
|
||||||
|
|||||||
+19
-27
@@ -6,20 +6,22 @@ namespace Craftimizer.Simulator;
|
|||||||
|
|
||||||
public class Simulator
|
public class Simulator
|
||||||
{
|
{
|
||||||
protected SimulationState State;
|
public SimulationState State { init => state = value; }
|
||||||
|
|
||||||
public SimulationInput Input => State.Input;
|
private SimulationState state;
|
||||||
public ref int ActionCount => ref State.ActionCount;
|
|
||||||
public ref int StepCount => ref State.StepCount;
|
|
||||||
public ref int Progress => ref State.Progress;
|
|
||||||
public ref int Quality => ref State.Quality;
|
|
||||||
public ref int Durability => ref State.Durability;
|
|
||||||
public ref int CP => ref State.CP;
|
|
||||||
public ref Condition Condition => ref State.Condition;
|
|
||||||
public ref Effects ActiveEffects => ref State.ActiveEffects;
|
|
||||||
public ref ActionStates ActionStates => ref State.ActionStates;
|
|
||||||
|
|
||||||
public bool IsFirstStep => State.StepCount == 0;
|
public SimulationInput Input => state.Input;
|
||||||
|
public ref int ActionCount => ref state.ActionCount;
|
||||||
|
public ref int StepCount => ref state.StepCount;
|
||||||
|
public ref int Progress => ref state.Progress;
|
||||||
|
public ref int Quality => ref state.Quality;
|
||||||
|
public ref int Durability => ref state.Durability;
|
||||||
|
public ref int CP => ref state.CP;
|
||||||
|
public ref Condition Condition => ref state.Condition;
|
||||||
|
public ref Effects ActiveEffects => ref state.ActiveEffects;
|
||||||
|
public ref ActionStates ActionStates => ref state.ActionStates;
|
||||||
|
|
||||||
|
public bool IsFirstStep => state.StepCount == 0;
|
||||||
|
|
||||||
public virtual CompletionState CompletionState {
|
public virtual CompletionState CompletionState {
|
||||||
get
|
get
|
||||||
@@ -35,20 +37,10 @@ public class Simulator
|
|||||||
|
|
||||||
public IEnumerable<ActionType> AvailableActions => ActionUtils.AvailableActions(this);
|
public IEnumerable<ActionType> AvailableActions => ActionUtils.AvailableActions(this);
|
||||||
|
|
||||||
public Simulator(in SimulationState state)
|
|
||||||
{
|
|
||||||
State = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetState(in SimulationState state)
|
|
||||||
{
|
|
||||||
State = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (ActionResponse Response, SimulationState NewState) Execute(in SimulationState state, ActionType action)
|
public (ActionResponse Response, SimulationState NewState) Execute(in SimulationState state, ActionType action)
|
||||||
{
|
{
|
||||||
State = state;
|
this.state = state;
|
||||||
return (Execute(action), State);
|
return (Execute(action), this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ActionResponse Execute(ActionType action)
|
private ActionResponse Execute(ActionType action)
|
||||||
@@ -77,16 +69,16 @@ public class Simulator
|
|||||||
|
|
||||||
public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(in SimulationState state, IEnumerable<ActionType> actions)
|
public (ActionResponse Response, SimulationState NewState, int FailedActionIdx) ExecuteMultiple(in SimulationState state, IEnumerable<ActionType> actions)
|
||||||
{
|
{
|
||||||
State = state;
|
this.state = state;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach(var action in actions)
|
foreach(var action in actions)
|
||||||
{
|
{
|
||||||
var resp = Execute(action);
|
var resp = Execute(action);
|
||||||
if (resp != ActionResponse.UsedAction)
|
if (resp != ActionResponse.UsedAction)
|
||||||
return (resp, State, i);
|
return (resp, this.state, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return (ActionResponse.UsedAction, State, -1);
|
return (ActionResponse.UsedAction, this.state, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Pure]
|
[Pure]
|
||||||
|
|||||||
@@ -2,10 +2,6 @@ namespace Craftimizer.Simulator;
|
|||||||
|
|
||||||
public class SimulatorNoRandom : Simulator
|
public class SimulatorNoRandom : Simulator
|
||||||
{
|
{
|
||||||
public SimulatorNoRandom(in SimulationState state) : base(state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed override bool RollSuccessRaw(float successRate) => successRate == 1;
|
public sealed override bool RollSuccessRaw(float successRate) => successRate == 1;
|
||||||
public sealed override Condition GetNextRandomCondition() => Condition.Normal;
|
public sealed override Condition GetNextRandomCondition() => Condition.Normal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ public struct ActionSet
|
|||||||
ActionType.DelicateSynthesis,
|
ActionType.DelicateSynthesis,
|
||||||
ActionType.PreparatoryTouch,
|
ActionType.PreparatoryTouch,
|
||||||
ActionType.Reflect,
|
ActionType.Reflect,
|
||||||
ActionType.FocusedTouch,
|
|
||||||
ActionType.FocusedSynthesis,
|
|
||||||
ActionType.PrudentTouch,
|
ActionType.PrudentTouch,
|
||||||
ActionType.Manipulation,
|
ActionType.Manipulation,
|
||||||
ActionType.MuscleMemory,
|
ActionType.MuscleMemory,
|
||||||
@@ -37,7 +35,6 @@ public struct ActionSet
|
|||||||
ActionType.StandardTouch,
|
ActionType.StandardTouch,
|
||||||
ActionType.Veneration,
|
ActionType.Veneration,
|
||||||
ActionType.WasteNot,
|
ActionType.WasteNot,
|
||||||
ActionType.Observe,
|
|
||||||
ActionType.MastersMend,
|
ActionType.MastersMend,
|
||||||
ActionType.BasicTouch,
|
ActionType.BasicTouch,
|
||||||
};
|
};
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@ public sealed class MCTS
|
|||||||
public MCTS(in MCTSConfig config, in SimulationState state)
|
public MCTS(in MCTSConfig config, in SimulationState state)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
var sim = new Simulator(state, config.MaxStepCount);
|
var sim = new Simulator(config.MaxStepCount) { State = state };
|
||||||
rootNode = new(new(
|
rootNode = new(new(
|
||||||
state,
|
state,
|
||||||
null,
|
null,
|
||||||
@@ -280,7 +280,7 @@ public sealed class MCTS
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Search(int iterations, CancellationToken token)
|
public void Search(int iterations, CancellationToken token)
|
||||||
{
|
{
|
||||||
Simulator simulator = new(rootNode.State.State, config.MaxStepCount);
|
Simulator simulator = new(config.MaxStepCount);
|
||||||
var random = rootNode.State.State.Input.Random;
|
var random = rootNode.State.State.Input.Random;
|
||||||
var n = 0;
|
var n = 0;
|
||||||
for (var i = 0; i < iterations || MaxScore == 0; i++)
|
for (var i = 0; i < iterations || MaxScore == 0; i++)
|
||||||
|
|||||||
+1
-15
@@ -20,7 +20,7 @@ internal sealed class Simulator : SimulatorNoRandom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Simulator(in SimulationState state, int maxStepCount) : base(state)
|
public Simulator(int maxStepCount)
|
||||||
{
|
{
|
||||||
this.maxStepCount = maxStepCount;
|
this.maxStepCount = maxStepCount;
|
||||||
}
|
}
|
||||||
@@ -42,22 +42,12 @@ internal sealed class Simulator : SimulatorNoRandom
|
|||||||
if (Quality >= Input.Recipe.MaxQuality && baseAction.IncreasesQuality)
|
if (Quality >= Input.Recipe.MaxQuality && baseAction.IncreasesQuality)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (action == ActionType.Observe &&
|
|
||||||
ActionStates.Observed)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (strict)
|
if (strict)
|
||||||
{
|
{
|
||||||
// always use Trained Eye if it's available
|
// always use Trained Eye if it's available
|
||||||
if (action == ActionType.TrainedEye)
|
if (action == ActionType.TrainedEye)
|
||||||
return baseAction.CanUse(this);
|
return baseAction.CanUse(this);
|
||||||
|
|
||||||
// only allow Focused moves after Observe
|
|
||||||
if (ActionStates.Observed &&
|
|
||||||
action != ActionType.FocusedSynthesis &&
|
|
||||||
action != ActionType.FocusedTouch)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// don't allow quality moves under Muscle Memory for difficult crafts
|
// don't allow quality moves under Muscle Memory for difficult crafts
|
||||||
if (Input.Recipe.ClassJobLevel == 90 &&
|
if (Input.Recipe.ClassJobLevel == 90 &&
|
||||||
HasEffect(EffectType.MuscleMemory) &&
|
HasEffect(EffectType.MuscleMemory) &&
|
||||||
@@ -116,10 +106,6 @@ internal sealed class Simulator : SimulatorNoRandom
|
|||||||
(HasEffect(EffectType.WasteNot) || HasEffect(EffectType.WasteNot2)))
|
(HasEffect(EffectType.WasteNot) || HasEffect(EffectType.WasteNot2)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (action == ActionType.Observe &&
|
|
||||||
CP < 12)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (action == ActionType.MastersMend &&
|
if (action == ActionType.MastersMend &&
|
||||||
Input.Recipe.MaxDurability - Durability < 25)
|
Input.Recipe.MaxDurability - Durability < 25)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+3
-3
@@ -116,7 +116,7 @@ public sealed class Solver : IDisposable
|
|||||||
var bestSims = new List<(float Score, SolverSolution Result)>();
|
var bestSims = new List<(float Score, SolverSolution Result)>();
|
||||||
|
|
||||||
var state = State;
|
var state = State;
|
||||||
var sim = new Simulator(state, Config.MaxStepCount);
|
var sim = new Simulator(Config.MaxStepCount);
|
||||||
|
|
||||||
var activeStates = new List<SolverSolution>() { new(Array.Empty<ActionType>(), state) };
|
var activeStates = new List<SolverSolution>() { new(Array.Empty<ActionType>(), state) };
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ public sealed class Solver : IDisposable
|
|||||||
{
|
{
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var state = State;
|
var state = State;
|
||||||
var sim = new Simulator(state, Config.MaxStepCount);
|
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Token.ThrowIfCancellationRequested();
|
Token.ThrowIfCancellationRequested();
|
||||||
@@ -303,7 +303,7 @@ public sealed class Solver : IDisposable
|
|||||||
{
|
{
|
||||||
var actions = new List<ActionType>();
|
var actions = new List<ActionType>();
|
||||||
var state = State;
|
var state = State;
|
||||||
var sim = new Simulator(state, Config.MaxStepCount);
|
var sim = new Simulator(Config.MaxStepCount) { State = state };
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Token.ThrowIfCancellationRequested();
|
Token.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class SimulatorTests
|
|||||||
int progress, int quality,
|
int progress, int quality,
|
||||||
int durability, int cp)
|
int durability, int cp)
|
||||||
{
|
{
|
||||||
var simulator = new SimulatorNoRandom(new(input));
|
var simulator = new SimulatorNoRandom();
|
||||||
var (_, state, _) = simulator.ExecuteMultiple(new(input), actions);
|
var (_, state, _) = simulator.ExecuteMultiple(new(input), actions);
|
||||||
Assert.AreEqual(progress, state.Progress);
|
Assert.AreEqual(progress, state.Progress);
|
||||||
Assert.AreEqual(quality, state.Quality);
|
Assert.AreEqual(quality, state.Quality);
|
||||||
@@ -170,7 +170,7 @@ public class SimulatorTests
|
|||||||
},
|
},
|
||||||
0, 4064, 15, 332);
|
0, 4064, 15, 332);
|
||||||
Assert.AreEqual(10, state.ActiveEffects.InnerQuiet);
|
Assert.AreEqual(10, state.ActiveEffects.InnerQuiet);
|
||||||
Assert.IsTrue(ActionType.TrainedFinesse.Base().CanUse(new SimulatorNoRandom(state)));
|
Assert.IsTrue(ActionType.TrainedFinesse.Base().CanUse(new SimulatorNoRandom() { State = state }));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|||||||
@@ -83,16 +83,16 @@ public class ActionSetTests
|
|||||||
set.AddAction(ActionType.BasicSynthesis);
|
set.AddAction(ActionType.BasicSynthesis);
|
||||||
set.AddAction(ActionType.ByregotsBlessing);
|
set.AddAction(ActionType.ByregotsBlessing);
|
||||||
set.AddAction(ActionType.DelicateSynthesis);
|
set.AddAction(ActionType.DelicateSynthesis);
|
||||||
set.AddAction(ActionType.FocusedTouch);
|
set.AddAction(ActionType.Reflect);
|
||||||
|
|
||||||
Assert.AreEqual(4, set.Count);
|
Assert.AreEqual(4, set.Count);
|
||||||
|
|
||||||
Assert.AreEqual(ActionType.DelicateSynthesis, set.ElementAt(0));
|
Assert.AreEqual(ActionType.DelicateSynthesis, set.ElementAt(0));
|
||||||
Assert.AreEqual(ActionType.FocusedTouch, set.ElementAt(1));
|
Assert.AreEqual(ActionType.Reflect, set.ElementAt(1));
|
||||||
Assert.AreEqual(ActionType.ByregotsBlessing, set.ElementAt(2));
|
Assert.AreEqual(ActionType.ByregotsBlessing, set.ElementAt(2));
|
||||||
Assert.AreEqual(ActionType.BasicSynthesis, set.ElementAt(3));
|
Assert.AreEqual(ActionType.BasicSynthesis, set.ElementAt(3));
|
||||||
|
|
||||||
set.RemoveAction(ActionType.FocusedTouch);
|
set.RemoveAction(ActionType.Reflect);
|
||||||
|
|
||||||
Assert.AreEqual(3, set.Count);
|
Assert.AreEqual(3, set.Count);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user