heat/cool prediction: continued important algorithm bug fix for prediction. Added configurable padding ticks for afk/inefficiency.
This commit is contained in:
@@ -502,6 +502,31 @@ public interface EasyGiantsFoundryConfig extends Config
|
||||
description = "Advanced Settings",
|
||||
position = 5
|
||||
)
|
||||
String generalSettings = "generalSettings";
|
||||
String advancedSettings = "generalSettings";
|
||||
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "heatActionBuffer", // renamed to reset player's settings for previous bugged implementation
|
||||
name = "Lava/Waterfall Padding Ticks",
|
||||
description = "Units in ticks; buffers more than optimal heat when in lava/waterfall calculations to compensate for heat decay when the player is afk or running/walking slower than optimal.",
|
||||
position = 0,
|
||||
section = advancedSettings
|
||||
)
|
||||
default int heatActionPadTicks()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "debugging",
|
||||
name = "Show Debugging",
|
||||
description = "Shows debugging visuals used for development",
|
||||
position = 0,
|
||||
section = advancedSettings,
|
||||
warning = "Only used for development."
|
||||
)
|
||||
default boolean debugging()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,27 +290,27 @@ public class EasyGiantsFoundryPlugin extends Plugin
|
||||
// start the HeatActionStateMachine when varbit begins to update in onVarbitChanged()
|
||||
if (event.getMenuOption().startsWith("Heat-preform"))
|
||||
{
|
||||
state.heatingCoolingState.stop();
|
||||
state.heatingCoolingState.setup(7, 0, "heats");
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(false, true, "heats");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Dunk-preform"))
|
||||
{
|
||||
state.heatingCoolingState.stop();
|
||||
state.heatingCoolingState.setup(27, 2, "dunks");
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(true, true, "dunks");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Cool-preform"))
|
||||
{
|
||||
state.heatingCoolingState.stop();
|
||||
state.heatingCoolingState.setup(-7, 0, "cools");
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(false, false, "cools");
|
||||
}
|
||||
else if (event.getMenuOption().startsWith("Quench-preform"))
|
||||
{
|
||||
state.heatingCoolingState.stop();
|
||||
state.heatingCoolingState.setup(-27, -2, "quenches");
|
||||
state.heatActionStateMachine.stop();
|
||||
state.heatActionStateMachine.setup(true, false, "quenches");
|
||||
}
|
||||
else // canceled heating/cooling, stop the heating state-machine
|
||||
else if (!state.heatActionStateMachine.isIdle()) // canceled heating/cooling, stop the heating state-machine
|
||||
{
|
||||
state.heatingCoolingState.stop();
|
||||
state.heatActionStateMachine.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,8 +333,8 @@ public class EasyGiantsFoundryPlugin extends Plugin
|
||||
|
||||
// show mould score on Mould UI Title
|
||||
Widget mouldParent = client.getWidget(47054850);
|
||||
Integer mouldScore = state.getMouldScore();
|
||||
if (mouldParent != null && mouldScore != null)
|
||||
int mouldScore = state.getMouldScore();
|
||||
if (mouldParent != null && mouldScore >= 0)
|
||||
{
|
||||
Widget title = Objects.requireNonNull(mouldParent.getChild(1));
|
||||
|
||||
@@ -362,23 +362,40 @@ public class EasyGiantsFoundryPlugin extends Plugin
|
||||
state.setMouldScore(-1);
|
||||
}
|
||||
|
||||
|
||||
// start the heating state-machine when the varbit updates
|
||||
// if heat varbit updated and the user clicked, start the state-machine
|
||||
if (event.getVarbitId() == VARBIT_HEAT && state.heatingCoolingState.getActionName() != null)
|
||||
if (event.getVarbitId() == VARBIT_HEAT)
|
||||
{
|
||||
// ignore passive heat decay, one heat per two ticks
|
||||
if (event.getValue() - previousHeat != -1)
|
||||
int delta = event.getValue() - previousHeat;
|
||||
// sign check: num * num > 0 == same sign
|
||||
if (delta != -1)
|
||||
{
|
||||
// if the state-machine is idle, start it
|
||||
if (state.heatingCoolingState.isIdle())
|
||||
if (state.heatActionStateMachine.getActionname() != null)
|
||||
{
|
||||
state.heatingCoolingState.start(state, config, state.getHeatAmount());
|
||||
// if the state-machine is idle, start it
|
||||
if (state.heatActionStateMachine.isIdle())
|
||||
{
|
||||
state.heatActionStateMachine.start(state, config, previousHeat);
|
||||
}
|
||||
state.heatActionStateMachine.onTick();
|
||||
}
|
||||
|
||||
state.heatingCoolingState.onTick();
|
||||
if (config.debugging())
|
||||
{
|
||||
client.addChatMessage(ChatMessageType.GAMEMESSAGE, "",
|
||||
"Heat: <col=FF0000>" + event.getValue() + "</col>" +
|
||||
"Delta: <col=00FFFF>" + delta + "</col> " +
|
||||
"Heating Ticks: <col=00FFFF>" + state.heatActionStateMachine.heatingTicks + "</col>" +
|
||||
" Cooling Ticks: <col=00FFFF>" + state.heatActionStateMachine.coolingTicks + "</col>" +
|
||||
" Remaining Ticks: <col=00FFFF>" + state.heatActionStateMachine.getRemainingDuration(), "");
|
||||
}
|
||||
}
|
||||
|
||||
// client.addChatMessage(ChatMessageType.GAMEMESSAGE, "", "Delta: <col=00FFFF>" + delta + "</col> ", "");
|
||||
previousHeat = event.getValue();
|
||||
}
|
||||
previousHeat = event.getValue();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
|
||||
@@ -335,5 +335,5 @@ public class EasyGiantsFoundryState
|
||||
return actions;
|
||||
}
|
||||
|
||||
public HeatActionStateMachine heatingCoolingState = new HeatActionStateMachine();
|
||||
public HeatActionStateMachine heatActionStateMachine = new HeatActionStateMachine();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public class FoundryOverlay3D extends Overlay
|
||||
{
|
||||
|
||||
private static final int HAND_IN_WIDGET = 49414221;
|
||||
private static final int CRUCIBLE_CAPACITY = 28;
|
||||
private final ModelOutlineRenderer modelOutlineRenderer;
|
||||
|
||||
GameObject tripHammer;
|
||||
@@ -132,7 +133,7 @@ public class FoundryOverlay3D extends Overlay
|
||||
|
||||
Stage stage = state.getCurrentStage();
|
||||
GameObject stageObject = getStageObject(stage);
|
||||
if (stageObject == null)
|
||||
if (stageObject == null || graphics == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -151,21 +152,13 @@ public class FoundryOverlay3D extends Overlay
|
||||
drawObjectOutline(graphics, stageObject, color);
|
||||
}
|
||||
|
||||
if ((stage.getHeat() != heat || !state.heatingCoolingState.isIdle()) && config.highlightWaterAndLava())
|
||||
// !state.heatingCoolingState.isIdle()
|
||||
// if the stage heat is already in range, but player still wants to do heat changes
|
||||
if ((stage.getHeat() != heat || !state.heatActionStateMachine.isIdle()) && config.highlightWaterAndLava())
|
||||
{
|
||||
drawHeatChangers(graphics);
|
||||
}
|
||||
|
||||
if (state.heatingCoolingState.isCooling())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, waterfall);
|
||||
}
|
||||
if (state.heatingCoolingState.isHeating())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, lavaPool);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -195,26 +188,87 @@ public class FoundryOverlay3D extends Overlay
|
||||
modelOutlineRenderer.drawOutline(stageObject, config.borderThickness(), _color, config.borderFeather());
|
||||
}
|
||||
|
||||
private void drawHeatChangerOverlay(
|
||||
private void drawHeatChangerPreviewOverlay(
|
||||
Graphics2D graphics,
|
||||
GameObject stageObject
|
||||
GameObject stageObject,
|
||||
boolean isLava
|
||||
)
|
||||
{
|
||||
if (!config.drawLavaWaterInfoOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.heatingCoolingState.isIdle())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int sign = isLava ? 1 : -1;
|
||||
int fastVelocity = 27 * sign;
|
||||
int slowVelocity = 7 * sign;
|
||||
int fastAccelBonus = 2 * sign;
|
||||
int slowAccelBonus = 0;
|
||||
|
||||
HeatActionSolver.DurationResult fastResult =
|
||||
HeatActionSolver.solve(
|
||||
state.getCurrentStage(),
|
||||
state.getCurrentHeatRange(),
|
||||
state.getActionsLeftInStage(),
|
||||
state.getHeatAmount(),
|
||||
true,
|
||||
isLava,
|
||||
config.heatActionPadTicks() * 2
|
||||
);
|
||||
|
||||
final int fastDuration = fastResult.getDuration();
|
||||
HeatActionSolver.DurationResult slowResult =
|
||||
HeatActionSolver.solve(
|
||||
state.getCurrentStage(),
|
||||
state.getCurrentHeatRange(),
|
||||
state.getActionsLeftInStage(),
|
||||
state.getHeatAmount(),
|
||||
false,
|
||||
isLava,
|
||||
config.heatActionPadTicks() * 2
|
||||
);
|
||||
final int slowDuration = slowResult.getDuration();
|
||||
|
||||
final String fastName = isLava ? "dunks" : "quenches";
|
||||
final String slowName = isLava ? "heats" : "cools";
|
||||
|
||||
String text;
|
||||
text = String.format("%d %s",
|
||||
state.heatingCoolingState.getRemainingDuration(),
|
||||
state.heatingCoolingState.getActionName()
|
||||
);
|
||||
if (config.debugging())
|
||||
{
|
||||
text = String.format("%d %s (predicted: %d) or %d %s (predicted: %d) (overshoot: %s goal-in-range: %s)",
|
||||
fastDuration, fastName, fastResult.getPredictedHeat(), slowDuration, slowName, slowResult.getPredictedHeat(), slowResult.isOvershooting(), fastResult.isGoalInRange());
|
||||
}
|
||||
else
|
||||
{
|
||||
text = String.format("%d %s or %d %s ",
|
||||
fastDuration, fastName, slowDuration, slowName);
|
||||
}
|
||||
|
||||
LocalPoint stageLoc = stageObject.getLocalLocation();
|
||||
stageLoc = new LocalPoint(stageLoc.getX(), stageLoc.getY());
|
||||
|
||||
Point pos = Perspective.getCanvasTextLocation(client, graphics, stageLoc, text, 50);
|
||||
Color color = config.lavaWaterfallColour();
|
||||
|
||||
OverlayUtil.renderTextLocation(graphics, pos, text, color);
|
||||
}
|
||||
|
||||
private void drawHeatChangerOverlay(Graphics2D graphics, GameObject stageObject)
|
||||
{
|
||||
|
||||
String text;
|
||||
if (config.debugging())
|
||||
{
|
||||
text = String.format("%d %s (overshoot: %s) [goal-in-range: %s]",
|
||||
state.heatActionStateMachine.getRemainingDuration(),
|
||||
state.heatActionStateMachine.getActionname(),
|
||||
state.heatActionStateMachine.isOverShooting(),
|
||||
state.heatActionStateMachine.isGoalInRange()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = String.format("%d %s",
|
||||
state.heatActionStateMachine.getRemainingDuration(),
|
||||
state.heatActionStateMachine.getActionname()
|
||||
);
|
||||
}
|
||||
|
||||
LocalPoint stageLoc = stageObject.getLocalLocation();
|
||||
stageLoc = new LocalPoint(stageLoc.getX(), stageLoc.getY());
|
||||
@@ -230,11 +284,13 @@ public class FoundryOverlay3D extends Overlay
|
||||
int change = state.getHeatChangeNeeded();
|
||||
Shape shape = null;
|
||||
|
||||
if (change < 0 || state.heatingCoolingState.isCooling())
|
||||
boolean isLava = change > 0;
|
||||
boolean isWaterfall = change < 0;
|
||||
if (isWaterfall || state.heatActionStateMachine.isCooling())
|
||||
{
|
||||
shape = waterfall.getClickbox();
|
||||
}
|
||||
else if (change > 0 || state.heatingCoolingState.isHeating())
|
||||
else if (isLava || state.heatActionStateMachine.isHeating())
|
||||
{
|
||||
shape = lavaPool.getClickbox();
|
||||
}
|
||||
@@ -254,9 +310,28 @@ public class FoundryOverlay3D extends Overlay
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(shape);
|
||||
}
|
||||
|
||||
if (config.drawLavaWaterInfoOverlay())
|
||||
{
|
||||
if (state.heatActionStateMachine.isCooling())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, waterfall);
|
||||
}
|
||||
else if (isWaterfall)
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, waterfall, false);
|
||||
}
|
||||
if (state.heatActionStateMachine.isHeating())
|
||||
{
|
||||
drawHeatChangerOverlay(graphics, lavaPool);
|
||||
}
|
||||
else if (isLava)
|
||||
{
|
||||
drawHeatChangerPreviewOverlay(graphics, lavaPool, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static final int CRUCIBLE_CAPACITY = 28;
|
||||
|
||||
private void drawCrucibleContent(Graphics2D graphics)
|
||||
{
|
||||
@@ -430,6 +505,8 @@ public class FoundryOverlay3D extends Overlay
|
||||
|
||||
private void drawActionOverlay(Graphics2D graphics, GameObject gameObject)
|
||||
{
|
||||
|
||||
|
||||
int actionsLeft = state.getActionsLeftInStage();
|
||||
int heatLeft = state.getActionsForHeatLevel();
|
||||
|
||||
@@ -440,6 +517,10 @@ public class FoundryOverlay3D extends Overlay
|
||||
LocalPoint textLocation = gameObject.getLocalLocation();
|
||||
textLocation = new LocalPoint(textLocation.getX(), textLocation.getY());
|
||||
Point canvasLocation = Perspective.getCanvasTextLocation(client, graphics, textLocation, text, 250);
|
||||
if (canvasLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OverlayUtil.renderTextLocation(graphics, canvasLocation, text, getHeatColor(actionsLeft, heatLeft));
|
||||
}
|
||||
if (config.drawActionLeftOverlay())
|
||||
@@ -449,6 +530,10 @@ public class FoundryOverlay3D extends Overlay
|
||||
LocalPoint textLocation = gameObject.getLocalLocation();
|
||||
textLocation = new LocalPoint(textLocation.getX(), textLocation.getY());
|
||||
Point canvasLocation = Perspective.getCanvasTextLocation(client, graphics, textLocation, text, 250);
|
||||
if (canvasLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
canvasLocation = new Point(canvasLocation.getX(), canvasLocation.getY() + 10);
|
||||
OverlayUtil.renderTextLocation(graphics, canvasLocation, text, getHeatColor(actionsLeft, heatLeft));
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ package com.toofifty.easygiantsfoundry;
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.List;
|
||||
|
||||
import com.toofifty.easygiantsfoundry.enums.Stage;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* Solves the heating/cooling action and predicts tick duration (index)
|
||||
* the naming convention is focused on the algorithm rather than in-game terminology for the context.
|
||||
* <p>
|
||||
* the dx_n refers to successive derivatives of an ordinary-differential-equations
|
||||
* https://en.wikipedia.org/wiki/Ordinary_differential_equation
|
||||
* also known as distance (dx0), speed (dx1), and acceleration (dx2).
|
||||
* also known as position (dx0), speed (dx1), and acceleration (dx2).
|
||||
* <p>
|
||||
* dx0 - players current heat at tick
|
||||
* dx1 - dx0_current - dx0_last_tick, aka the first derivative
|
||||
@@ -18,7 +21,17 @@ package com.toofifty.easygiantsfoundry;
|
||||
* for context, here's what dx1 extracted directly from in-game dunking looks like.
|
||||
* the purpose of the HeatActionSolver.java is to accurately model this data.
|
||||
* int[] dx1 = {
|
||||
* 27,
|
||||
* 7,
|
||||
* 8,
|
||||
* 9,
|
||||
* 11,
|
||||
* 13,
|
||||
* 15,
|
||||
* 17,
|
||||
* 19,
|
||||
* 21,
|
||||
* 24,
|
||||
* 27, -- dunk/quench starts here
|
||||
* 30,
|
||||
* 33,
|
||||
* 37,
|
||||
@@ -77,156 +90,254 @@ subscribe(VarbitChanged.class, ev ->
|
||||
public class HeatActionSolver
|
||||
{
|
||||
|
||||
/**
|
||||
* <p><b>Warning:</b> this method prefers overshooting goal. For example, if goal is 957,
|
||||
* it will return index that reaches >957.</p>
|
||||
*
|
||||
* <p>This may be desirable if we're aiming to heat just over range minimum;
|
||||
* for example if the stage is heating (grind stone),</p>
|
||||
*
|
||||
* <p>but undesirable when heating to just below range maximum;
|
||||
* for example if the stage is cooling (hammer.)</p>
|
||||
*
|
||||
* <p>
|
||||
* Make sure to subtract 1 tick from duration, if so.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param goal the desired heat destination
|
||||
* @param dx1_init initial speed of heating/cooling. currently 7 for heat/cool, 27 for dunk/quench.
|
||||
* @param dx2_offset bonus acceleration. currently, 0 for heat/cool, 2 for dunk/quench.
|
||||
* @return Index here refers to tick. So an index of 10 means the goal can be reached in 10 ticks.
|
||||
*/
|
||||
public static int findDuration(int goal, int dx1_init, int dx2_offset)
|
||||
public static final int[] DX_1 = new int[]{
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
11,
|
||||
13,
|
||||
15,
|
||||
17,
|
||||
19,
|
||||
21,
|
||||
24,
|
||||
27, // -- dunk/quench starts here
|
||||
30,
|
||||
33,
|
||||
37,
|
||||
41,
|
||||
45,
|
||||
49,
|
||||
53,
|
||||
57,
|
||||
62,
|
||||
67,
|
||||
72,
|
||||
77,
|
||||
83,
|
||||
89,
|
||||
95,
|
||||
91, // last one will always overshoot 1000
|
||||
};
|
||||
public static final int MAX_INDEX = DX_1.length;
|
||||
public static final int FAST_INDEX = 10;
|
||||
|
||||
@Value(staticConstructor = "of")
|
||||
public static class SolveResult
|
||||
{
|
||||
int index;
|
||||
int dx0;
|
||||
int dx1;
|
||||
int dx2;
|
||||
}
|
||||
|
||||
private static SolveResult heatingSolve(int start, int goal, boolean overshoot, int max, boolean isFast)
|
||||
{
|
||||
return relativeSolve(goal - start, overshoot, max - start, isFast, -1);
|
||||
}
|
||||
|
||||
private static SolveResult coolingSolve(int start, int goal, boolean overshoot, int min, boolean isFast)
|
||||
{
|
||||
return relativeSolve(start - goal, overshoot, start - min, isFast, 1);
|
||||
}
|
||||
|
||||
private static SolveResult relativeSolve(int goal, boolean overshoot, int max, boolean isFast, int decayValue)
|
||||
{
|
||||
|
||||
int index = isFast ? FAST_INDEX : 0;
|
||||
int dx0 = 0;
|
||||
int dx1 = dx1_init;
|
||||
int count_index = 0;
|
||||
for (int dx2 = 1; dx0 <= goal; dx2++)
|
||||
{ // Start from 1 up to the count inclusive
|
||||
int repetitions;
|
||||
if (dx2 == 1)
|
||||
|
||||
boolean decay = false;
|
||||
|
||||
while (true) {
|
||||
|
||||
if (index >= MAX_INDEX)
|
||||
{
|
||||
repetitions = 2; // The first number appears twice
|
||||
break;
|
||||
}
|
||||
else if (dx2 % 2 == 0)
|
||||
|
||||
if (!overshoot && dx0 + DX_1[index] > goal)
|
||||
{
|
||||
repetitions = 6; // Even numbers appear six times
|
||||
break;
|
||||
}
|
||||
else
|
||||
else if (overshoot && dx0 >= goal)
|
||||
{
|
||||
repetitions = 4; // Odd numbers (after 1) appear four times
|
||||
break;
|
||||
}
|
||||
for (int j = 0; j < repetitions && dx0 <= goal; j++)
|
||||
|
||||
if (dx0 + DX_1[index] >= max)
|
||||
{
|
||||
dx0 += dx1;
|
||||
dx1 += dx2 + dx2_offset; // Sum the current number 'repetitions' times
|
||||
count_index += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (decay)
|
||||
{
|
||||
dx0 -= decayValue;
|
||||
}
|
||||
|
||||
dx0 += DX_1[index];
|
||||
++index;
|
||||
decay = !decay;
|
||||
}
|
||||
return count_index;
|
||||
|
||||
if (isFast)
|
||||
{
|
||||
index -= FAST_INDEX;
|
||||
}
|
||||
|
||||
return SolveResult.of(index, dx0, DX_1[index], -1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We can use the pattern to get the dx2 at a specific index numerically
|
||||
*
|
||||
* @param index the index/tick we want to calculate dx2 at
|
||||
* @return the acceleration of heating/cooling at index/tick
|
||||
*/
|
||||
public static int getDx2AtIndex(int index)
|
||||
@Value(staticConstructor = "of")
|
||||
public static class DurationResult
|
||||
{
|
||||
if (index <= 1) return 1;
|
||||
|
||||
index -= 2;
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
// e,e,e,e,e,e,o,o,o,o
|
||||
|
||||
int block = index / 10;
|
||||
int block_idx = index % 10;
|
||||
int number = block * 2;
|
||||
if (block_idx <= 5)
|
||||
{
|
||||
return number + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return number + 3;
|
||||
}
|
||||
int duration;
|
||||
boolean goalInRange;
|
||||
boolean overshooting;
|
||||
int predictedHeat;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We can use the pattern to get the dx1 at a specific index numerically
|
||||
*
|
||||
* @param index the index/tick we want to calculate the speed of heating/cooling
|
||||
* @param constant the initial speed of heating/cooling.
|
||||
* @return the speed of heating at index/tick
|
||||
*/
|
||||
public static int getDx1AtIndex(int index, int constant)
|
||||
public static DurationResult solve(
|
||||
Stage stage,
|
||||
int[] range,
|
||||
int actionLeftInStage,
|
||||
int start,
|
||||
boolean isFast,
|
||||
boolean isActionHeating,
|
||||
int padding)
|
||||
{
|
||||
int _dx1 = constant;
|
||||
for (int i = 0; i < index; ++i)
|
||||
|
||||
final boolean isStageHeating = stage.isHeating();
|
||||
|
||||
// adding 1.8s/6ticks worth of padding so preform doesn't decay out of range
|
||||
// average distance from lava+waterfall around 6 ticks
|
||||
// preform decays 1 heat every 2 ticks
|
||||
final int min = range[0] + padding;
|
||||
final int max = range[1] + padding;
|
||||
|
||||
final int actionsLeft_DeltaHeat = actionLeftInStage * stage.getHeatChange();
|
||||
|
||||
int estimatedDuration = 0;
|
||||
|
||||
final boolean goalInRange;
|
||||
boolean overshoot = false;
|
||||
|
||||
SolveResult result = null;
|
||||
|
||||
// case actions are all cooling, heating is mirrored version
|
||||
|
||||
// goal: in-range // stage: heating
|
||||
// overshoot goal
|
||||
// <----------|stop|<---------------- heat
|
||||
// ------|min|----------goal-------|max|
|
||||
// stage ---------------->
|
||||
|
||||
// goal: out-range // stage: heating
|
||||
// undershoot min
|
||||
// ...----------|stop|<--------------------- heat
|
||||
// -goal---|min|---------------------|max|
|
||||
// stage ----------------->
|
||||
|
||||
// goal: in-range // stage: cooling
|
||||
// undershoot goal
|
||||
// <-------------------------|stop|<--------------- heat
|
||||
// ------|min|----------goal-------|max|
|
||||
// <---------------- stage
|
||||
|
||||
// goal: out-range // stage: cooling
|
||||
// overshoot max
|
||||
// <--------------------|stop|<--------------- heat
|
||||
// --------|min|---------------------|max|----goal
|
||||
// <---------------- stage
|
||||
|
||||
if (isActionHeating)
|
||||
{
|
||||
_dx1 += getDx2AtIndex(i);
|
||||
int goal = min - actionsLeft_DeltaHeat;
|
||||
goalInRange = goal >= min && goal <= max;
|
||||
|
||||
if (isStageHeating)
|
||||
{
|
||||
|
||||
if (start <= max)
|
||||
{
|
||||
overshoot = !goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = min;
|
||||
}
|
||||
|
||||
result = heatingSolve(start, goal, overshoot, max, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
else // cooling stage
|
||||
{
|
||||
// actionsLeft_DeltaHeat is negative here
|
||||
if (start <= max)
|
||||
{
|
||||
overshoot = goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = max;
|
||||
}
|
||||
|
||||
result = heatingSolve(start, goal, overshoot, max, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // cooling action
|
||||
{
|
||||
int goal = max - actionsLeft_DeltaHeat;
|
||||
goalInRange = goal >= min && goal <= max;
|
||||
|
||||
if (isStageHeating)
|
||||
{
|
||||
if (start >= min)
|
||||
{
|
||||
overshoot = goalInRange;
|
||||
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = min;
|
||||
}
|
||||
|
||||
result = coolingSolve(start, goal, overshoot, min, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
else // cooling stage cooling action
|
||||
{
|
||||
if (start >= min)
|
||||
{
|
||||
overshoot = !goalInRange;
|
||||
if (!goalInRange)
|
||||
{
|
||||
goal = max;
|
||||
}
|
||||
|
||||
result = coolingSolve(start, goal, overshoot, min, isFast);
|
||||
|
||||
estimatedDuration = result.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _dx1;
|
||||
int dx0 = result == null ? 0 : result.dx0;
|
||||
if (!isActionHeating)
|
||||
{
|
||||
dx0 *= -1;
|
||||
}
|
||||
|
||||
|
||||
return DurationResult.of(estimatedDuration, goalInRange, overshoot, start + dx0);
|
||||
}
|
||||
|
||||
// Methods below are functional, but only used to for debugging & development
|
||||
|
||||
// public static int getDx0AtIndex(int index, int constant)
|
||||
// {
|
||||
// int dx0 = 0;
|
||||
// int dx1 = getDx1AtIndex(0, constant);
|
||||
// for (int i = 0; i < index; i++)
|
||||
// { // Start from 1 up to the count inclusive
|
||||
// int dx2 = getDx2AtIndex(i);
|
||||
// dx1 += dx2; // Sum the current number 'repetitions' times
|
||||
// dx0 += dx1;
|
||||
// }
|
||||
// return dx0;
|
||||
// }
|
||||
|
||||
// We iteratively generate dx2 into a list
|
||||
// public static List<Integer> generateDx2List(int count)
|
||||
// {
|
||||
// List<Integer> pattern = new ArrayList<>(); // This will hold our pattern
|
||||
// for (int n = 1, i = 0; i < count; n++)
|
||||
// { // Start from 1 up to the count inclusive
|
||||
// int repetitions;
|
||||
// if (n == 1)
|
||||
// {
|
||||
// repetitions = 2; // The first number appears twice
|
||||
// } else if (n % 2 == 0)
|
||||
// {
|
||||
// repetitions = 6; // Even numbers appear six times
|
||||
// } else
|
||||
// {
|
||||
// repetitions = 4; // Odd numbers (after 1) appear four times
|
||||
// }
|
||||
// for (int j = 0; j < repetitions && i < count; j++, i++)
|
||||
// {
|
||||
// pattern.add(n); // Append the current number 'repetitions' times
|
||||
// }
|
||||
// }
|
||||
// return pattern;
|
||||
// }
|
||||
|
||||
// public static int findDx0IndexContinue(int goal, int constant, int init_index)
|
||||
// {
|
||||
// int dx0 = getDx0AtIndex(init_index, constant);
|
||||
// int dx1 = getDx1AtIndex(init_index, constant);
|
||||
// int count_index = init_index;
|
||||
// for (; dx0 <= goal; count_index++)
|
||||
// { // Start from 1 up to the count inclusive
|
||||
// int dx2 = getDx2AtIndex(count_index);
|
||||
// dx1 += dx2; // Sum the current number 'repetitions' times
|
||||
// dx0 += dx1;
|
||||
// }
|
||||
// return count_index - init_index;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.toofifty.easygiantsfoundry;
|
||||
|
||||
import com.toofifty.easygiantsfoundry.enums.Stage;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -14,46 +13,45 @@ public class HeatActionStateMachine
|
||||
/**
|
||||
* Tick counter for heating, -1 means not currently heating.
|
||||
*/
|
||||
int HeatingTicks = -1;
|
||||
int heatingTicks = -1;
|
||||
|
||||
/**
|
||||
* Tick counter for cooling, -1 means not currently cooling.
|
||||
*/
|
||||
int CoolingTicks = -1;
|
||||
int coolingTicks = -1;
|
||||
|
||||
/**
|
||||
* The velocity of the heating/cooling action.
|
||||
*/
|
||||
int Velocity;
|
||||
boolean actionFast;
|
||||
|
||||
/**
|
||||
* The acceleration bonus of the heating/cooling action.
|
||||
*/
|
||||
int AccelerationBonus;
|
||||
boolean actionHeating;
|
||||
|
||||
/**
|
||||
* The starting heat amount of the heating/cooling action.
|
||||
*/
|
||||
int StartingHeat;
|
||||
int startingHeat;
|
||||
|
||||
/**
|
||||
* The estimated tick duration of the heating/cooling action.
|
||||
*/
|
||||
int EstimatedDuration;
|
||||
int estimatedDuration;
|
||||
|
||||
/**
|
||||
* The goal heat amount of the heating/cooling action.
|
||||
*/
|
||||
int GoalHeat = 0;
|
||||
int goalHeat = 0;
|
||||
|
||||
// debug
|
||||
boolean goalInRange;
|
||||
boolean isOverShooting;
|
||||
int predictedHeat;
|
||||
|
||||
/**
|
||||
* The last action the player clicked on. Used for ui overlay to display.
|
||||
* When null, the state-machine will stop() and reset.
|
||||
*/
|
||||
String ActionName = null;
|
||||
String actionname = null;
|
||||
|
||||
private EasyGiantsFoundryState State;
|
||||
private EasyGiantsFoundryConfig Config;
|
||||
private EasyGiantsFoundryState state;
|
||||
private EasyGiantsFoundryConfig config;
|
||||
|
||||
/**
|
||||
* Start the state-machine with the given parameters.
|
||||
@@ -69,21 +67,22 @@ public class HeatActionStateMachine
|
||||
public void start(EasyGiantsFoundryState state, EasyGiantsFoundryConfig config, int startingHeat)
|
||||
{
|
||||
// use Velocity to determine if heating or cooling
|
||||
if (Velocity > 0)
|
||||
if (actionHeating)
|
||||
{
|
||||
HeatingTicks = 0;
|
||||
CoolingTicks = -1;
|
||||
heatingTicks = 0;
|
||||
coolingTicks = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
CoolingTicks = 0;
|
||||
HeatingTicks = -1;
|
||||
heatingTicks = -1;
|
||||
coolingTicks = 0;
|
||||
}
|
||||
StartingHeat = startingHeat - Velocity;
|
||||
State = state;
|
||||
Config = config;
|
||||
|
||||
calculateEstimates();
|
||||
this.startingHeat = startingHeat;
|
||||
this.state = state;
|
||||
this.config = config;
|
||||
|
||||
updateEstimates();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,11 +94,11 @@ public class HeatActionStateMachine
|
||||
{
|
||||
if (isHeating())
|
||||
{
|
||||
return Math.max(0, EstimatedDuration - HeatingTicks);
|
||||
return Math.max(0, (estimatedDuration - heatingTicks));
|
||||
}
|
||||
else if (isCooling())
|
||||
{
|
||||
return Math.max(0, EstimatedDuration - CoolingTicks);
|
||||
return Math.max(0, (estimatedDuration - coolingTicks));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,113 +110,26 @@ public class HeatActionStateMachine
|
||||
* Core logic. Runs once on {@link HeatActionStateMachine#start} and assumes synchronization with the game.
|
||||
* Calculate the estimated duration and goal heat amount of the heating/cooling action.
|
||||
*/
|
||||
public void calculateEstimates()
|
||||
public void updateEstimates()
|
||||
{
|
||||
// 0: left/min 1: right/max
|
||||
int[] range = State.getCurrentHeatRange();
|
||||
int stageMin = range[0];
|
||||
int stageMax = range[1];
|
||||
|
||||
Stage stage = State.getCurrentStage();
|
||||
int actionsLeft = State.getActionsLeftInStage();
|
||||
int actionsLeft_DeltaHeat = (actionsLeft+1) * stage.getHeatChange();
|
||||
HeatActionSolver.DurationResult result =
|
||||
HeatActionSolver.solve(
|
||||
getState().getCurrentStage(),
|
||||
getState().getCurrentHeatRange(),
|
||||
getState().getActionsLeftInStage(),
|
||||
getStartingHeat(),
|
||||
actionFast,
|
||||
isHeating(),
|
||||
config.heatActionPadTicks() * 2
|
||||
);
|
||||
|
||||
if (isHeating())
|
||||
{
|
||||
if (stage.isHeating())
|
||||
{
|
||||
GoalHeat = Math.max(stageMin, stageMax - actionsLeft_DeltaHeat);
|
||||
if (StartingHeat < GoalHeat)
|
||||
{
|
||||
int duration = HeatActionSolver.findDuration(
|
||||
GoalHeat - StartingHeat,
|
||||
Velocity, AccelerationBonus
|
||||
);
|
||||
goalInRange = result.isGoalInRange();
|
||||
isOverShooting = result.isOvershooting();
|
||||
|
||||
// compensate for heat decay during (1 heat every 2 ticks)
|
||||
GoalHeat += duration / 2;
|
||||
predictedHeat = result.getPredictedHeat();
|
||||
|
||||
EstimatedDuration = HeatActionSolver.findDuration(
|
||||
GoalHeat - StartingHeat,
|
||||
Velocity, AccelerationBonus
|
||||
);
|
||||
}
|
||||
else // overheating
|
||||
{
|
||||
EstimatedDuration = 0;
|
||||
}
|
||||
}
|
||||
else // is cooling
|
||||
{
|
||||
// actionsLeft_DeltaHeat is negative here
|
||||
GoalHeat = Math.min(stageMax, stageMin - actionsLeft_DeltaHeat);
|
||||
if (StartingHeat < GoalHeat)
|
||||
{
|
||||
int duration = HeatActionSolver.findDuration(
|
||||
GoalHeat - StartingHeat,
|
||||
Velocity, AccelerationBonus
|
||||
) - 1;
|
||||
|
||||
GoalHeat -= duration / 2;
|
||||
|
||||
EstimatedDuration = HeatActionSolver.findDuration(
|
||||
GoalHeat - StartingHeat,
|
||||
Velocity, AccelerationBonus
|
||||
) - 1;
|
||||
}
|
||||
else // cold enough
|
||||
{
|
||||
EstimatedDuration = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isCooling())
|
||||
{
|
||||
if (stage.isHeating()) {
|
||||
GoalHeat = Math.max(stageMin, stageMax - actionsLeft_DeltaHeat);
|
||||
if (StartingHeat > GoalHeat)
|
||||
{
|
||||
int duration = HeatActionSolver.findDuration(
|
||||
StartingHeat - GoalHeat,
|
||||
Math.abs(Velocity), Math.abs(AccelerationBonus)
|
||||
) - 1;
|
||||
|
||||
GoalHeat += duration / 2;
|
||||
|
||||
EstimatedDuration = HeatActionSolver.findDuration(
|
||||
(StartingHeat - GoalHeat),
|
||||
Math.abs(Velocity), Math.abs(AccelerationBonus)
|
||||
) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
EstimatedDuration = 0;
|
||||
}
|
||||
}
|
||||
// Heating Stage
|
||||
else {
|
||||
GoalHeat = Math.max(stageMax, stageMin + actionsLeft_DeltaHeat);
|
||||
if (StartingHeat > GoalHeat) // too hot
|
||||
{
|
||||
int duration = HeatActionSolver.findDuration(
|
||||
StartingHeat - GoalHeat,
|
||||
Math.abs(Velocity), Math.abs(AccelerationBonus)
|
||||
);
|
||||
|
||||
GoalHeat -= duration / 2;
|
||||
|
||||
EstimatedDuration = HeatActionSolver.findDuration(
|
||||
StartingHeat - GoalHeat,
|
||||
Math.abs(Velocity), Math.abs(AccelerationBonus)
|
||||
);
|
||||
}
|
||||
else // hot enough
|
||||
{
|
||||
EstimatedDuration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
estimatedDuration = result.getDuration();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,11 +139,11 @@ public class HeatActionStateMachine
|
||||
* @param accelerationBonus the acceleration bonus of the heating/cooling action. Usually 0 for slow, 2 for fast.
|
||||
* @param actionName the name of the action to display in the ui overlay
|
||||
*/
|
||||
public void setup(int velocity, int accelerationBonus, String actionName)
|
||||
public void setup(boolean isFast, boolean isHeating, String actionName)
|
||||
{
|
||||
Velocity = velocity;
|
||||
AccelerationBonus = accelerationBonus;
|
||||
ActionName = actionName;
|
||||
actionFast = isFast;
|
||||
actionHeating = isHeating;
|
||||
actionname = actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,9 +151,9 @@ public class HeatActionStateMachine
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
HeatingTicks = -1;
|
||||
CoolingTicks = -1;
|
||||
ActionName = null;
|
||||
heatingTicks = -1;
|
||||
coolingTicks = -1;
|
||||
actionname = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +163,7 @@ public class HeatActionStateMachine
|
||||
*/
|
||||
public boolean isHeating()
|
||||
{
|
||||
return HeatingTicks >= 0;
|
||||
return heatingTicks >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +173,7 @@ public class HeatActionStateMachine
|
||||
*/
|
||||
public boolean isCooling()
|
||||
{
|
||||
return CoolingTicks >= 0;
|
||||
return coolingTicks >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -279,25 +191,30 @@ public class HeatActionStateMachine
|
||||
*/
|
||||
public void onTick()
|
||||
{
|
||||
if (isIdle()) return;
|
||||
|
||||
if (isHeating())
|
||||
{
|
||||
HeatingTicks++;
|
||||
if (HeatingTicks >= EstimatedDuration)
|
||||
if (heatingTicks >= estimatedDuration)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
heatingTicks++;
|
||||
}
|
||||
}
|
||||
if (isCooling())
|
||||
else if (isCooling())
|
||||
{
|
||||
CoolingTicks++;
|
||||
if (CoolingTicks >= EstimatedDuration)
|
||||
if (coolingTicks >= estimatedDuration)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
coolingTicks++;
|
||||
}
|
||||
}
|
||||
// log.info("\nReal Heat: " + State.getHeatAmount()
|
||||
// + "\nGoal Heat - StartingHeat: " + (GoalHeat - StartingHeat)
|
||||
// + "\nDuration: " + EstimatedDuration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
package com.toofifty.easygiantsfoundry;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
// playground to test HeatSolver
|
||||
public class HeatSolverTest
|
||||
{
|
||||
// @Test
|
||||
// public void TestHeatSolver_dx2_Iterative()
|
||||
// {
|
||||
// final int[] answer =
|
||||
// {1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6};
|
||||
// List<Integer> produced = HeatActionSolver.generateDx2List(answer.length);
|
||||
//
|
||||
// System.err.println("Expected Length: " + answer.length + " Length: " + produced.size());
|
||||
// // print produced
|
||||
// for (Integer integer : produced)
|
||||
// {
|
||||
// System.err.print(integer + ",");
|
||||
// }
|
||||
// System.err.println();
|
||||
// // compare
|
||||
// for (int i = 0; i < answer.length; i++)
|
||||
// {
|
||||
// assertEquals("Asserting dx2 n=" + i, answer[i], produced.get(i).intValue());
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_dx2_Numerical()
|
||||
{
|
||||
final int[] answer =
|
||||
{1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6};
|
||||
// test getDx2AtIndex
|
||||
for (int i = 0; i < answer.length; i++)
|
||||
{
|
||||
assertEquals("Asserting dx2 n=" + i, answer[i], HeatActionSolver.getDx2AtIndex(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_dx1()
|
||||
{
|
||||
final int c = 7;
|
||||
final int[] answer =
|
||||
// {1,1,2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6};
|
||||
{7, 8, 9, 11, 13, 15, 17, 19, 21, 24, 27, 30, 33, 37, 41, 45, 49, 53, 57, 62, 67, 72, 77, 83, 89};
|
||||
for (int i = 0; i < answer.length; i++)
|
||||
{
|
||||
assertEquals("Asserting dx1 n=" + i + " c=" + c + " answer=" + answer[i], answer[i], HeatActionSolver.getDx1AtIndex(i, c));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_dx2_from_groundtruth()
|
||||
{
|
||||
// runelite-shell script for retrieving heating/cooling delta.
|
||||
// copy-paste into developer-tools -> Shell
|
||||
|
||||
|
||||
// ground-truth answer from game
|
||||
final int[] answer =
|
||||
{7, 8, 9, 11, 13, 15, 17, 19, 21, 24, 27, 30, 33, 37, 41, 45, 49, 53, 57, 62, 67, 72, 77, 83, 89};
|
||||
for (int i = 0; i < answer.length - 1; i++)
|
||||
{
|
||||
System.err.print(answer[i + 1] - answer[i] + ",");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_Dx0()
|
||||
{
|
||||
final int[] answer_dx1 =
|
||||
{7, 8, 9, 11, 13, 15, 17, 19, 21, 24, 27, 30, 33, 37, 41, 45, 49, 53, 57, 62, 67, 72, 77, 83, 89};
|
||||
List<Integer> answer_dx0 = new ArrayList<>();
|
||||
|
||||
|
||||
int sum = 0;
|
||||
for (int i = 0; i < answer_dx1.length; i++)
|
||||
{
|
||||
sum += answer_dx1[i];
|
||||
answer_dx0.add(sum);
|
||||
}
|
||||
|
||||
System.err.println(answer_dx0);
|
||||
|
||||
for (int i = 0; i < answer_dx1.length; i++)
|
||||
{
|
||||
TestHeatSolver_Dx0_Helper(answer_dx0.get(i), answer_dx0.get(0), i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_Dx0_Manual()
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
System.err.println("[" + (350 + i) + "]" + HeatActionSolver.findDuration(350 + i, 7, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestHeatSolver_Dx0_2()
|
||||
{
|
||||
// 7->1,15->2,24->3,35->4,48->5,63->6,80->7,99->8,120->9,144->10,171->11,201->12,234->13,271->14,312->15,357->16,406->17,459->18,516->19,578->20,645->21,717->22,794->23,877->24,966->25
|
||||
final int[] answer_dx1 =
|
||||
{7, 8, 9, 11, 13, 15, 17, 19, 21, 24, 27, 30, 33, 37, 41, 45, 49, 53, 57, 62, 67, 72, 77, 83, 89};
|
||||
List<Integer> answer_dx0 = new ArrayList<>();
|
||||
|
||||
|
||||
int sum = 0;
|
||||
for (int i = 0; i < answer_dx1.length; i++)
|
||||
{
|
||||
sum += answer_dx1[i];
|
||||
answer_dx0.add(sum);
|
||||
}
|
||||
|
||||
System.err.println(answer_dx0);
|
||||
|
||||
// System.err.println(
|
||||
// HeatSolver.findDx0IndexContinue(406, 7, 0));
|
||||
// System.err.println(
|
||||
// HeatSolver.findDx0IndexContinue(406, 7, 10));
|
||||
// System.err.println(
|
||||
// HeatSolver.findDx0IndexContinue(406, 7, 17));
|
||||
//
|
||||
// System.err.println(
|
||||
// HeatSolver.findDx0IndexContinue(1000, 7, 0));
|
||||
System.err.println(
|
||||
HeatActionSolver.findDuration(957, 27, 2));
|
||||
// System.err.println(
|
||||
// HeatActionSolver.findDuration(1000, 7, 1));
|
||||
}
|
||||
|
||||
public void TestHeatSolver_Dx0_Helper(int dx0, int constant, int answer_index)
|
||||
{
|
||||
System.err.print(dx0 + "->" + HeatActionSolver.findDuration(dx0, constant, 0) + ",");
|
||||
|
||||
// test calcDx0Index
|
||||
assertEquals("Asserting dx0 for index " + answer_index,
|
||||
answer_index, HeatActionSolver.findDuration(dx0, constant, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void Calc()
|
||||
{
|
||||
int[] dx1 = {
|
||||
27,
|
||||
30,
|
||||
33,
|
||||
37,
|
||||
41,
|
||||
45,
|
||||
49,
|
||||
53,
|
||||
57,
|
||||
62,
|
||||
67,
|
||||
72,
|
||||
77,
|
||||
83,
|
||||
89,
|
||||
95,
|
||||
91,
|
||||
};
|
||||
|
||||
List<Integer> dx2 = new ArrayList<>();
|
||||
for (int i = 0; i < dx1.length - 1; i++)
|
||||
{
|
||||
dx2.add(dx1[i+1] - dx1[i]);
|
||||
}
|
||||
|
||||
System.err.println(dx2);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user