From 7787ae32d543fdf15066794f7c165fe452e4c17e Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Thu, 13 Jul 2023 12:08:17 +0200 Subject: [PATCH] Combo actions for better macro quality --- Benchmark/Program.cs | 19 +++++- Craftimizer/Windows/SimulatorWindowDrawer.cs | 6 +- Simulator/ActionCategory.cs | 1 + Simulator/Actions/ActionType.cs | 9 +++ Simulator/Actions/AdvancedTouchCombo.cs | 30 +++++++++ Simulator/Actions/FocusedSynthesisCombo.cs | 28 ++++++++ Simulator/Actions/FocusedTouchCombo.cs | 28 ++++++++ Simulator/Actions/StandardTouchCombo.cs | 69 ++++++++++++++++++++ Simulator/Simulator.cs | 9 ++- Solver/Crafty/Simulator.cs | 9 +++ Solver/Crafty/Solver.cs | 12 ++-- 11 files changed, 210 insertions(+), 10 deletions(-) create mode 100644 Simulator/Actions/AdvancedTouchCombo.cs create mode 100644 Simulator/Actions/FocusedSynthesisCombo.cs create mode 100644 Simulator/Actions/FocusedTouchCombo.cs create mode 100644 Simulator/Actions/StandardTouchCombo.cs diff --git a/Benchmark/Program.cs b/Benchmark/Program.cs index bde8adf..da6ca6a 100644 --- a/Benchmark/Program.cs +++ b/Benchmark/Program.cs @@ -1,4 +1,5 @@ using Craftimizer.Simulator; +using Craftimizer.Simulator.Actions; using Craftimizer.Solver.Crafty; using System.Diagnostics; @@ -52,7 +53,23 @@ internal static class Program var sim = new SimulatorNoRandom(new(input)); (_, var state) = sim.Execute(new(input), ActionType.MuscleMemory); - Console.WriteLine($"{state.Quality} {state.CP} {state.Progress}"); + (_, state) = sim.Execute(state, ActionType.PrudentTouch); + (_, state) = sim.Execute(state, ActionType.Manipulation); + (_, state) = sim.Execute(state, ActionType.Veneration); + (_, state) = sim.Execute(state, ActionType.WasteNot); + (_, state) = sim.Execute(state, ActionType.Groundwork); + (_, state) = sim.Execute(state, ActionType.Groundwork); + (_, state) = sim.Execute(state, ActionType.Groundwork); + (_, state) = sim.Execute(state, ActionType.Innovation); + (_, state) = sim.Execute(state, ActionType.PrudentTouch); + (_, state) = sim.Execute(state, ActionType.AdvancedTouchCombo); + (_, state) = sim.Execute(state, ActionType.Manipulation); + (_, state) = sim.Execute(state, ActionType.Innovation); + (_, state) = sim.Execute(state, ActionType.PrudentTouch); + (_, state) = sim.Execute(state, ActionType.AdvancedTouchCombo); + (_, state) = sim.Execute(state, ActionType.GreatStrides); + + Console.WriteLine($"{state.Quality} {state.CP} {state.Progress} {state.Durability}"); //return; var (_, s) = Solver.Crafty.Solver.SearchStepwiseFurcated(config, state, a => Console.WriteLine(a)); Console.WriteLine($"Qual: {s.Quality}/{s.Input.Recipe.MaxQuality}"); diff --git a/Craftimizer/Windows/SimulatorWindowDrawer.cs b/Craftimizer/Windows/SimulatorWindowDrawer.cs index 689f91b..965b198 100644 --- a/Craftimizer/Windows/SimulatorWindowDrawer.cs +++ b/Craftimizer/Windows/SimulatorWindowDrawer.cs @@ -38,7 +38,11 @@ public sealed partial class SimulatorWindow : Window, IDisposable static SimulatorWindow() { - SortedActions = Enum.GetValues().GroupBy(a => a.Category()).Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray())).ToArray(); + SortedActions = Enum.GetValues() + .Where(a => a.Category() != ActionCategory.Combo) + .GroupBy(a => a.Category()) + .Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray())) + .ToArray(); } public override void Draw() diff --git a/Simulator/ActionCategory.cs b/Simulator/ActionCategory.cs index 3bb2632..24d671b 100644 --- a/Simulator/ActionCategory.cs +++ b/Simulator/ActionCategory.cs @@ -7,6 +7,7 @@ public enum ActionCategory Quality, Durability, Buffs, + Combo, Other } diff --git a/Simulator/Actions/ActionType.cs b/Simulator/Actions/ActionType.cs index c89699a..bb852f9 100644 --- a/Simulator/Actions/ActionType.cs +++ b/Simulator/Actions/ActionType.cs @@ -38,6 +38,11 @@ public enum ActionType : byte Veneration, WasteNot, WasteNot2, + + StandardTouchCombo, + AdvancedTouchCombo, + FocusedSynthesisCombo, + FocusedTouchCombo, } public static class ActionUtils @@ -106,6 +111,10 @@ public static class ActionUtils ActionType.Veneration => "Veneration", ActionType.WasteNot => "Waste Not", ActionType.WasteNot2 => "Waste Not II", + ActionType.StandardTouchCombo => "Standard Touch Combo", + ActionType.AdvancedTouchCombo => "Advanced Touch Combo", + ActionType.FocusedSynthesisCombo => "Focused Synthesis Combo", + ActionType.FocusedTouchCombo => "Focused Touch Combo", _ => me.ToString(), }; } diff --git a/Simulator/Actions/AdvancedTouchCombo.cs b/Simulator/Actions/AdvancedTouchCombo.cs new file mode 100644 index 0000000..5bfb274 --- /dev/null +++ b/Simulator/Actions/AdvancedTouchCombo.cs @@ -0,0 +1,30 @@ +namespace Craftimizer.Simulator.Actions; + +// Basic Touch -> Standard Touch -> Advanced Touch +internal sealed class AdvancedTouchCombo : BaseAction +{ + public override ActionCategory Category => ActionCategory.Combo; + public override int Level => 84; + public override uint ActionId => 100411; + + public override bool IncreasesQuality => true; + + public override int CPCost(Simulator s) => 18 + 18 + 18; + + public override bool CanUse(Simulator s) => + // BasicTouch.DurabilityCost vv vv StandardTouch.DurabilityCost + base.CanUse(s) && StandardTouchCombo.VerifyDurability3(s, 10, 10); + + private static readonly BasicTouch ActionA = new(); + private static readonly StandardTouch ActionB = new(); + private static readonly AdvancedTouch ActionC = new(); + public override void Use(Simulator s) + { + s.ExecuteForced(ActionType.BasicTouch, ActionA); + s.ExecuteForced(ActionType.StandardTouch, ActionB); + ActionC.Use(s); + } + + public override string GetTooltip(Simulator s, bool addUsability) => + $"{ActionA.GetTooltip(s, addUsability)}\n{ActionB.GetTooltip(s, addUsability)}\n{ActionC.GetTooltip(s, addUsability)}"; +} diff --git a/Simulator/Actions/FocusedSynthesisCombo.cs b/Simulator/Actions/FocusedSynthesisCombo.cs new file mode 100644 index 0000000..7570d91 --- /dev/null +++ b/Simulator/Actions/FocusedSynthesisCombo.cs @@ -0,0 +1,28 @@ +namespace Craftimizer.Simulator.Actions; + +// Observe -> Focused Synthesis +internal sealed class FocusedSynthesisCombo : BaseAction +{ + public override ActionCategory Category => ActionCategory.Combo; + public override int Level => 67; + public override uint ActionId => 100235; + + public override bool IncreasesProgress => true; + + public override int CPCost(Simulator s) => 7 + 5; + + public override bool CanUse(Simulator s) => + // Observe.DurabilityCost v + base.CanUse(s) && StandardTouchCombo.VerifyDurability2(s, 0); + + private static readonly Observe ActionA = new(); + private static readonly FocusedSynthesis ActionB = new(); + public override void Use(Simulator s) + { + s.ExecuteForced(ActionType.Observe, ActionA); + ActionB.Use(s); + } + + public override string GetTooltip(Simulator s, bool addUsability) => + $"{ActionA.GetTooltip(s, addUsability)}\n{ActionB.GetTooltip(s, addUsability)}"; +} diff --git a/Simulator/Actions/FocusedTouchCombo.cs b/Simulator/Actions/FocusedTouchCombo.cs new file mode 100644 index 0000000..e545493 --- /dev/null +++ b/Simulator/Actions/FocusedTouchCombo.cs @@ -0,0 +1,28 @@ +namespace Craftimizer.Simulator.Actions; + +// Observe -> Focused Touch +internal sealed class FocusedTouchCombo : BaseAction +{ + public override ActionCategory Category => ActionCategory.Combo; + public override int Level => 68; + public override uint ActionId => 100243; + + public override bool IncreasesQuality => true; + + public override int CPCost(Simulator s) => 7 + 18; + + public override bool CanUse(Simulator s) => + // Observe.DurabilityCost v + base.CanUse(s) && StandardTouchCombo.VerifyDurability2(s, 0); + + private static readonly Observe ActionA = new(); + private static readonly FocusedTouch ActionB = new(); + public override void Use(Simulator s) + { + s.ExecuteForced(ActionType.Observe, ActionA); + ActionB.Use(s); + } + + public override string GetTooltip(Simulator s, bool addUsability) => + $"{ActionA.GetTooltip(s, addUsability)}\n{ActionB.GetTooltip(s, addUsability)}"; +} diff --git a/Simulator/Actions/StandardTouchCombo.cs b/Simulator/Actions/StandardTouchCombo.cs new file mode 100644 index 0000000..2cf4725 --- /dev/null +++ b/Simulator/Actions/StandardTouchCombo.cs @@ -0,0 +1,69 @@ +namespace Craftimizer.Simulator.Actions; + +// Basic Touch -> Standard Touch +internal sealed class StandardTouchCombo : BaseAction +{ + public override ActionCategory Category => ActionCategory.Combo; + public override int Level => 18; + public override uint ActionId => 100004; + + public override bool IncreasesQuality => true; + + public override int CPCost(Simulator s) => 18 + 18; + + public override bool CanUse(Simulator s) => + // BasicTouch.DurabilityCost vv + base.CanUse(s) && VerifyDurability2(s, 10); + + private static readonly BasicTouch ActionA = new(); + private static readonly StandardTouch ActionB = new(); + public override void Use(Simulator s) + { + s.ExecuteForced(ActionType.BasicTouch, ActionA); + ActionB.Use(s); + } + + public override string GetTooltip(Simulator s, bool addUsability) => + $"{ActionA.GetTooltip(s, addUsability)}\n{ActionB.GetTooltip(s, addUsability)}"; + + public static bool VerifyDurability2(Simulator s, int durabilityA) + { + var d = s.Durability; + var wasteNots = s.HasEffect(EffectType.WasteNot) || s.HasEffect(EffectType.WasteNot2); + + // -A + d -= (int)MathF.Ceiling(durabilityA * (wasteNots ? .5f : 1f)); + if (d <= 0) + return false; + + // If we can do the first action and still have durability left to survive to the next + // step (even before the Manipulation modifier), we can certainly do the next action. + return true; + } + + public static bool VerifyDurability3(Simulator s, int durabilityA, int durabilityB) + { + var d = s.Durability; + var wasteNots = Math.Max(s.GetEffectDuration(EffectType.WasteNot), s.GetEffectDuration(EffectType.WasteNot2)); + var manips = s.GetEffectDuration(EffectType.Manipulation); + + d -= (int)MathF.Ceiling(durabilityA * wasteNots > 0 ? .5f : 1f); + if (d <= 0) + return false; + + if (manips > 0) + d += 5; + + if (wasteNots > 0) + wasteNots--; + + d -= (int)MathF.Ceiling(durabilityB * wasteNots > 0 ? .5f : 1f); + + if (d <= 0) + return false; + + // If we can do the second action and still have durability left to survive to the next + // step (even before the Manipulation modifier), we can certainly do the next action. + return true; + } +} diff --git a/Simulator/Simulator.cs b/Simulator/Simulator.cs index ffc2f8d..f132105 100644 --- a/Simulator/Simulator.cs +++ b/Simulator/Simulator.cs @@ -57,13 +57,18 @@ public class Simulator return ActionResponse.CannotUseAction; } + ExecuteForced(action, baseAction); + + return ActionResponse.UsedAction; + } + + public void ExecuteForced(ActionType action, BaseAction baseAction) + { baseAction.Use(this); ActionStates.MutateState(action); ActionCount++; ActiveEffects.DecrementDuration(); - - return ActionResponse.UsedAction; } public int GetEffectStrength(EffectType effect) => diff --git a/Solver/Crafty/Simulator.cs b/Solver/Crafty/Simulator.cs index 04d7574..4aceeb5 100644 --- a/Solver/Crafty/Simulator.cs +++ b/Solver/Crafty/Simulator.cs @@ -19,6 +19,10 @@ public sealed class Simulator : SimulatorNoRandom public static readonly ActionType[] AcceptedActions = new[] { + ActionType.StandardTouchCombo, + ActionType.AdvancedTouchCombo, + ActionType.FocusedTouchCombo, + ActionType.FocusedSynthesisCombo, ActionType.TrainedFinesse, ActionType.PrudentSynthesis, ActionType.Groundwork, @@ -101,6 +105,11 @@ public sealed class Simulator : SimulatorNoRandom CP > 10) return false; + // don't allow combo actions if the combo is already in progress + if (ActionStates.TouchComboIdx != 0 && + (action == ActionType.StandardTouchCombo || action == ActionType.AdvancedTouchCombo)) + return false; + // don't allow pure quality moves under Veneration if (HasEffect(EffectType.Veneration) && !baseAction.IncreasesProgress && diff --git a/Solver/Crafty/Solver.cs b/Solver/Crafty/Solver.cs index 6043a1b..d0354d6 100644 --- a/Solver/Crafty/Solver.cs +++ b/Solver/Crafty/Solver.cs @@ -298,7 +298,7 @@ public sealed class Solver } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Search(CancellationToken token, int iterations) + private void Search(int iterations, CancellationToken token) { Simulator simulator = new(rootNode.State.State, config.MaxStepCount); var random = rootNode.State.State.Input.Random; @@ -360,7 +360,7 @@ public sealed class Solver Task.Run(() => { var solver = new Solver(config, activeStates[stateIdx].State); - solver.Search(token, config.Iterations / config.ForkCount); + solver.Search(config.Iterations / config.ForkCount, token); return (solver.MaxScore, stateIdx, solver.Solution()); }, token) ); @@ -461,7 +461,7 @@ public sealed class Solver tasks[i] = Task.Run(() => { var solver = new Solver(config, state); - solver.Search(token, config.Iterations / config.ForkCount); + solver.Search(config.Iterations / config.ForkCount, token); return (solver.MaxScore, solver.Solution()); }, token); Task.WaitAll(tasks, CancellationToken.None); @@ -504,7 +504,7 @@ public sealed class Solver var solver = new Solver(config, state); var s = Stopwatch.StartNew(); - solver.Search(token, config.Iterations); + solver.Search(config.Iterations, token); s.Stop(); var (solution_actions, solution_node) = solver.Solution(); @@ -536,7 +536,7 @@ public sealed class Solver tasks[i] = Task.Run(() => { var solver = new Solver(config, state); - solver.Search(token, config.Iterations / config.ForkCount); + solver.Search(config.Iterations / config.ForkCount, token); return (solver.MaxScore, solver.Solution()); }, token); Task.WaitAll(tasks, CancellationToken.None); @@ -551,7 +551,7 @@ public sealed class Solver public static (List Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationState state, CancellationToken token = default) { var solver = new Solver(config, state); - solver.Search(token, config.Iterations); + solver.Search(config.Iterations, token); var (solution_actions, solution_node) = solver.Solution(); return (solution_actions, solution_node.State); }