Added costs for "slow" bracketing (to prioritize jumps), twisted backwards foot.

Removed costs for JUMP, CROWDED_BRACKET, and OTHER
Added cutoffs for counting footswitches and doublesteps below a certain speed as tech
This commit is contained in:
Michael Votaw
2025-02-11 19:39:03 -08:00
committed by teejusb
parent f70cb4c6aa
commit 5f7720a8b8
4 changed files with 136 additions and 52 deletions
+95 -42
View File
@@ -91,7 +91,7 @@ float* StepParityCost::getActionCost(State * initialState, State * resultState,
costs[COST_HOLDSWITCH] += calcHoldSwitchCost( initialState, resultState, row, combinedColumns, columnCount);
costs[COST_BRACKETTAP] += calcBracketTapCost( initialState, resultState, row, leftHeel, leftToe, rightHeel, rightToe, elapsedTime, columnCount);
costs[COST_OTHER] += calcMovingFootWhileOtherIsntOnPadCost( initialState, resultState, columnCount);
// costs[COST_OTHER] += calcMovingFootWhileOtherIsntOnPadCost( initialState, resultState, columnCount);
bool movedLeft =
resultState->didTheFootMove[LEFT_HEEL] ||
@@ -121,20 +121,17 @@ float* StepParityCost::getActionCost(State * initialState, State * resultState,
costs[COST_BRACKETJACK] += calcBracketJackCost( initialState, resultState, rows, rowIndex, movedLeft, movedRight, jackedLeft, jackedRight, didJump, columnCount);
costs[COST_DOUBLESTEP] += calcDoublestepCost(initialState, resultState, rows, rowIndex, movedLeft, movedRight, jackedLeft, jackedRight, didJump, columnCount);
costs[COST_JUMP] += calcJumpCost( row, movedLeft, movedRight, elapsedTime, columnCount);
// costs[COST_JUMP] += calcJumpCost( row, movedLeft, movedRight, elapsedTime, columnCount);
costs[COST_SLOW_BRACKET] += calcSlowBracketCost(row, movedLeft, movedRight, elapsedTime);
costs[COST_TWISTED_FOOT] += calcTwistedFootCost(resultState);
costs[COST_FACING] += calcFacingCosts( initialState, resultState, combinedColumns, columnCount);
costs[COST_SPIN] += calcSpinCosts(initialState, resultState, combinedColumns, columnCount);
costs[COST_FOOTSWITCH] += caclFootswitchCost( initialState, resultState, row, combinedColumns, elapsedTime, columnCount);
costs[COST_SIDESWITCH] += calcSideswitchCost( initialState, resultState, columnCount);
costs[COST_MISSED_FOOTSWITCH] += calcMissedFootswitchCost( row, jackedLeft, jackedRight, columnCount);
// To do: small weighting for swapping heel with toe or toe with heel (both add up)
// To do: huge weighting for having foot direction opposite of eachother (can't twist one leg 180 degrees)
costs[COST_MISSED_FOOTSWITCH] += calcMissedFootswitchCost( row, jackedLeft, jackedRight, columnCount)
costs[COST_JACK] += calcJackCost( movedLeft, movedRight, jackedLeft, jackedRight, elapsedTime, columnCount);
// To do: weighting for moving a foot a far distance in a fast time
costs[COST_DISTANCE] += calcBigMovementsQuicklyCost( initialState, resultState, elapsedTime, columnCount);
costs[COST_CROWDED_BRACKET] += calcCrowdedBracketCost(initialState, resultState, elapsedTime, columnCount);
// costs[COST_CROWDED_BRACKET] += calcCrowdedBracketCost(initialState, resultState, elapsedTime, columnCount);
// I don't like that we're updating columns here like this.
// We're basically updating columns with the final position of the feet
@@ -405,6 +402,45 @@ float StepParityCost::calcJumpCost(Row & row, bool movedLeft, bool movedRight, f
return cost;
}
// Jumps should be prioritized over brackets below a certain speed
float StepParityCost::calcSlowBracketCost(Row & row, bool movedLeft, bool movedRight, float elapsedTime)
{
float cost = 0;
if(elapsedTime > SLOW_BRACKET_THRESHOLD && movedLeft != movedRight &&
std::count_if(row.notes.begin(), row.notes.end(), [](StepParity::IntermediateNoteData note)
{ return note.type != TapNoteType_Empty; }) >= 2)
{
float timediff = elapsedTime - SLOW_BRACKET_THRESHOLD;
cost += timediff * SLOW_BRACKET;
}
return cost;
}
// Does this placement result in one of the feet being twisted around?
// This should probably be getting filtered out as an invalid positioning before
// we even get to calculating costs.
float StepParityCost::calcTwistedFootCost(State * resultState)
{
float cost = 0;
int leftHeel = resultState->whereTheFeetAre[LEFT_HEEL];
int leftToe = resultState->whereTheFeetAre[LEFT_TOE];
int rightHeel = resultState->whereTheFeetAre[RIGHT_HEEL];
int rightToe = resultState->whereTheFeetAre[RIGHT_TOE];
StagePoint leftPos = averagePoint(leftHeel, leftToe);
StagePoint rightPos = averagePoint(rightHeel, rightToe);
bool crossedOver = rightPos.x < leftPos.x;
bool rightBackwards = rightHeel != -1 && rightToe != -1 ? layout[rightToe].y < layout[rightHeel].y : false;
bool leftBackwards = leftHeel != -1 && leftToe != -1 ? layout[leftToe].y < layout[leftHeel].y : false;
if(!crossedOver && (rightBackwards || leftBackwards))
{
cost += TWISTED_FOOT;
}
return cost;
}
float StepParityCost::calcMissedFootswitchCost(Row & row, bool jackedLeft, bool jackedRight, int columnCount)
{
float cost = 0;
@@ -554,40 +590,39 @@ float StepParityCost::calcSpinCosts(State * initialState, State * resultState, s
return cost;
}
// Footswitches are harder the slower they are.
// Add a penalty when they get slower than 8ths at 120bpm (0.25 seconds)
// Footswitches are harder to do when they get too slow.
// Notes with an elapsed time greater than this will incur a penalty
float StepParityCost::caclFootswitchCost(State * initialState, State * resultState, Row & row, std::vector<StepParity::Foot> & combinedColumns, float elapsedTime, int columnCount)
{
float cost = 0;
// ignore footswitch with 24 or less distance (8th note); penalise slower footswitches based on distance
if (elapsedTime >= 0.25) {
if (elapsedTime >= SLOW_FOOTSWITCH_THRESHOLD && elapsedTime < SLOW_FOOTSWITCH_IGNORE) {
// footswitching has no penalty if there's a mine nearby
if (
std::all_of(row.mines.begin(), row.mines.end(), [](int mine)
{ return mine == 0; }) &&
std::all_of(row.fakeMines.begin(), row.fakeMines.end(), [](int mine)
{ return mine == 0; }))
{
float timeScaled = elapsedTime - 0.25;
for (int i = 0; i < columnCount; i++)
if (
std::all_of(row.mines.begin(), row.mines.end(), [](int mine)
{ return mine == 0; }) &&
std::all_of(row.fakeMines.begin(), row.fakeMines.end(), [](int mine)
{ return mine == 0; }))
{
if (
initialState->columns[i] == NONE ||
resultState->columns[i] == NONE)
continue;
float timeScaled = elapsedTime - SLOW_FOOTSWITCH_THRESHOLD;
if (
initialState->columns[i] != resultState->columns[i] &&
!resultState->didTheFootMove[initialState->columns[i]])
for (int i = 0; i < columnCount; i++)
{
cost += pow(timeScaled / 2.0f, 2) * FOOTSWITCH;
break;
if (
initialState->columns[i] == NONE ||
resultState->columns[i] == NONE)
continue;
if (
initialState->columns[i] != resultState->columns[i] &&
initialState->columns[i] != OTHER_PART_OF_FOOT[resultState->columns[i]]
)
{
cost += (timeScaled / (SLOW_FOOTSWITCH_THRESHOLD + timeScaled)) * FOOTSWITCH;
break;
}
}
}
}
}
return cost;
}
@@ -637,17 +672,35 @@ float StepParityCost::calcBigMovementsQuicklyCost(State * initialState, State *
for (StepParity::Foot foot : resultState->movedFeet)
{
if(foot == NONE)
{
continue;
int idxFoot = initialState->whereTheFeetAre[foot];
if (idxFoot == -1)
}
int initialPosition = initialState->whereTheFeetAre[foot];
if(initialPosition == -1)
{
continue;
cost +=
(sqrt(
getDistanceSq(
layout[idxFoot],
layout[resultState->whereTheFeetAre[foot]])) *
DISTANCE) /
elapsedTime;
}
int resultPosition = resultState->whereTheFeetAre[foot];
// If we're bracketing something, and the toes are now where the heel
// was, then we don't need to worry about it, we're not actually moving
// the foot very far
bool isBracketing = resultState->whereTheFeetAre[OTHER_PART_OF_FOOT[foot]] != -1;
if(isBracketing && resultState->whereTheFeetAre[OTHER_PART_OF_FOOT[foot]] == initialPosition)
{
continue;
}
float dist = (sqrt(getDistanceSq(layout[initialPosition], layout[resultPosition])) * DISTANCE) / elapsedTime;
// Otherwise if we're still bracketing, this is probably a less drastic movement
if(isBracketing)
{
dist = dist * 0.2;
}
cost += dist;
}
return cost;