Combo actions for better macro quality

This commit is contained in:
Asriel Camora
2023-07-13 12:08:17 +02:00
parent 49b84e966e
commit 7787ae32d5
11 changed files with 210 additions and 10 deletions
+18 -1
View File
@@ -1,4 +1,5 @@
using Craftimizer.Simulator; using Craftimizer.Simulator;
using Craftimizer.Simulator.Actions;
using Craftimizer.Solver.Crafty; using Craftimizer.Solver.Crafty;
using System.Diagnostics; using System.Diagnostics;
@@ -52,7 +53,23 @@ internal static class Program
var sim = new SimulatorNoRandom(new(input)); var sim = new SimulatorNoRandom(new(input));
(_, var state) = sim.Execute(new(input), ActionType.MuscleMemory); (_, 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; //return;
var (_, s) = Solver.Crafty.Solver.SearchStepwiseFurcated(config, state, a => Console.WriteLine(a)); var (_, s) = Solver.Crafty.Solver.SearchStepwiseFurcated(config, state, a => Console.WriteLine(a));
Console.WriteLine($"Qual: {s.Quality}/{s.Input.Recipe.MaxQuality}"); Console.WriteLine($"Qual: {s.Quality}/{s.Input.Recipe.MaxQuality}");
+5 -1
View File
@@ -38,7 +38,11 @@ public sealed partial class SimulatorWindow : Window, IDisposable
static SimulatorWindow() static SimulatorWindow()
{ {
SortedActions = Enum.GetValues<ActionType>().GroupBy(a => a.Category()).Select(g => (g.Key, g.OrderBy(a => a.Level()).ToArray())).ToArray(); SortedActions = Enum.GetValues<ActionType>()
.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() public override void Draw()
+1
View File
@@ -7,6 +7,7 @@ public enum ActionCategory
Quality, Quality,
Durability, Durability,
Buffs, Buffs,
Combo,
Other Other
} }
+9
View File
@@ -38,6 +38,11 @@ public enum ActionType : byte
Veneration, Veneration,
WasteNot, WasteNot,
WasteNot2, WasteNot2,
StandardTouchCombo,
AdvancedTouchCombo,
FocusedSynthesisCombo,
FocusedTouchCombo,
} }
public static class ActionUtils public static class ActionUtils
@@ -106,6 +111,10 @@ public static class ActionUtils
ActionType.Veneration => "Veneration", ActionType.Veneration => "Veneration",
ActionType.WasteNot => "Waste Not", ActionType.WasteNot => "Waste Not",
ActionType.WasteNot2 => "Waste Not II", 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(), _ => me.ToString(),
}; };
} }
+30
View File
@@ -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)}";
}
@@ -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)}";
}
+28
View File
@@ -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)}";
}
+69
View File
@@ -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;
}
}
+7 -2
View File
@@ -57,13 +57,18 @@ public class Simulator
return ActionResponse.CannotUseAction; return ActionResponse.CannotUseAction;
} }
ExecuteForced(action, baseAction);
return ActionResponse.UsedAction;
}
public void ExecuteForced(ActionType action, BaseAction baseAction)
{
baseAction.Use(this); baseAction.Use(this);
ActionStates.MutateState(action); ActionStates.MutateState(action);
ActionCount++; ActionCount++;
ActiveEffects.DecrementDuration(); ActiveEffects.DecrementDuration();
return ActionResponse.UsedAction;
} }
public int GetEffectStrength(EffectType effect) => public int GetEffectStrength(EffectType effect) =>
+9
View File
@@ -19,6 +19,10 @@ public sealed class Simulator : SimulatorNoRandom
public static readonly ActionType[] AcceptedActions = new[] public static readonly ActionType[] AcceptedActions = new[]
{ {
ActionType.StandardTouchCombo,
ActionType.AdvancedTouchCombo,
ActionType.FocusedTouchCombo,
ActionType.FocusedSynthesisCombo,
ActionType.TrainedFinesse, ActionType.TrainedFinesse,
ActionType.PrudentSynthesis, ActionType.PrudentSynthesis,
ActionType.Groundwork, ActionType.Groundwork,
@@ -101,6 +105,11 @@ public sealed class Simulator : SimulatorNoRandom
CP > 10) CP > 10)
return false; 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 // don't allow pure quality moves under Veneration
if (HasEffect(EffectType.Veneration) && if (HasEffect(EffectType.Veneration) &&
!baseAction.IncreasesProgress && !baseAction.IncreasesProgress &&
+6 -6
View File
@@ -298,7 +298,7 @@ public sealed class Solver
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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); Simulator simulator = new(rootNode.State.State, config.MaxStepCount);
var random = rootNode.State.State.Input.Random; var random = rootNode.State.State.Input.Random;
@@ -360,7 +360,7 @@ public sealed class Solver
Task.Run(() => Task.Run(() =>
{ {
var solver = new Solver(config, activeStates[stateIdx].State); 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()); return (solver.MaxScore, stateIdx, solver.Solution());
}, token) }, token)
); );
@@ -461,7 +461,7 @@ public sealed class Solver
tasks[i] = Task.Run(() => tasks[i] = Task.Run(() =>
{ {
var solver = new Solver(config, state); 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()); return (solver.MaxScore, solver.Solution());
}, token); }, token);
Task.WaitAll(tasks, CancellationToken.None); Task.WaitAll(tasks, CancellationToken.None);
@@ -504,7 +504,7 @@ public sealed class Solver
var solver = new Solver(config, state); var solver = new Solver(config, state);
var s = Stopwatch.StartNew(); var s = Stopwatch.StartNew();
solver.Search(token, config.Iterations); solver.Search(config.Iterations, token);
s.Stop(); s.Stop();
var (solution_actions, solution_node) = solver.Solution(); var (solution_actions, solution_node) = solver.Solution();
@@ -536,7 +536,7 @@ public sealed class Solver
tasks[i] = Task.Run(() => tasks[i] = Task.Run(() =>
{ {
var solver = new Solver(config, state); 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()); return (solver.MaxScore, solver.Solution());
}, token); }, token);
Task.WaitAll(tasks, CancellationToken.None); Task.WaitAll(tasks, CancellationToken.None);
@@ -551,7 +551,7 @@ public sealed class Solver
public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationState state, CancellationToken token = default) public static (List<ActionType> Actions, SimulationState State) SearchOneshot(SolverConfig config, SimulationState state, CancellationToken token = default)
{ {
var solver = new Solver(config, state); var solver = new Solver(config, state);
solver.Search(token, config.Iterations); solver.Search(config.Iterations, token);
var (solution_actions, solution_node) = solver.Solution(); var (solution_actions, solution_node) = solver.Solution();
return (solution_actions, solution_node.State); return (solution_actions, solution_node.State);
} }