Several "structural" changes, and some memory optimizations:
- Replaced use of bare '-1' values with StepParity::INVALID_COLUMN - Removed StepParityGraph object, moved its responsibilities to StepParityGenerator - Removed some unnecessary data from State object, added 'combinedColumns' and 'whatNoteTheFootIsHitting' - Created stateCache to allow reuse of state objects - Fixed a very small bug with TechCounts (missing 'previousPreviousHeel != INVALID_COLUMN')
This commit is contained in:
+87
-147
@@ -20,23 +20,18 @@ bool isEmpty(const std::vector<T> & vec, int columnCount) {
|
||||
}
|
||||
|
||||
|
||||
float StepParityCost::getActionCost(State * initialState, State * resultState, std::vector<Row>& rows, int rowIndex)
|
||||
float StepParityCost::getActionCost(State * initialState, State * resultState, std::vector<Row>& rows, int rowIndex, float elapsedTime)
|
||||
{
|
||||
Row &row = rows[rowIndex];
|
||||
int columnCount = row.columnCount;
|
||||
float elapsedTime = resultState->second - initialState->second;
|
||||
|
||||
float cost = 0;
|
||||
|
||||
std::vector<StepParity::Foot> combinedColumns(columnCount, NONE);
|
||||
|
||||
mergeInitialAndResultPosition(initialState, resultState, combinedColumns, columnCount);
|
||||
|
||||
// Mine weighting
|
||||
int leftHeel = -1;
|
||||
int leftToe = -1;
|
||||
int rightHeel = -1;
|
||||
int rightToe = -1;
|
||||
int leftHeel = INVALID_COLUMN;
|
||||
int leftToe = INVALID_COLUMN;
|
||||
int rightHeel = INVALID_COLUMN;
|
||||
int rightToe = INVALID_COLUMN;
|
||||
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
switch (resultState->columns[i]) {
|
||||
@@ -81,79 +76,24 @@ float StepParityCost::getActionCost(State * initialState, State * resultState, s
|
||||
bool jackedLeft = didJackLeft(initialState, resultState, leftHeel, leftToe, movedLeft, didJump, columnCount);
|
||||
bool jackedRight = didJackRight(initialState, resultState, rightHeel, rightToe, movedRight, didJump, columnCount);
|
||||
|
||||
cost += calcMineCost( initialState, resultState, row, combinedColumns, columnCount);
|
||||
cost += calcHoldSwitchCost( initialState, resultState, row, combinedColumns, columnCount);
|
||||
cost += calcMineCost( initialState, resultState, row, columnCount);
|
||||
cost += calcHoldSwitchCost( initialState, resultState, row, columnCount);
|
||||
cost += calcBracketTapCost( initialState, resultState, row, leftHeel, leftToe, rightHeel, rightToe, elapsedTime, columnCount);
|
||||
cost += calcBracketJackCost( initialState, resultState, rows, rowIndex, movedLeft, movedRight, jackedLeft, jackedRight, didJump, columnCount);
|
||||
cost += calcDoublestepCost(initialState, resultState, rows, rowIndex, movedLeft, movedRight, jackedLeft, jackedRight, didJump, columnCount);
|
||||
cost += calcSlowBracketCost(row, movedLeft, movedRight, elapsedTime);
|
||||
cost += calcTwistedFootCost(resultState);
|
||||
cost += calcFacingCosts( initialState, resultState, combinedColumns, columnCount);
|
||||
cost += calcSpinCosts(initialState, resultState, combinedColumns, columnCount);
|
||||
cost += caclFootswitchCost( initialState, resultState, row, combinedColumns, elapsedTime, columnCount);
|
||||
cost += calcFacingCosts( initialState, resultState, columnCount);
|
||||
cost += calcSpinCosts(initialState, resultState, columnCount);
|
||||
cost += caclFootswitchCost( initialState, resultState, row, elapsedTime, columnCount);
|
||||
cost += calcSideswitchCost( initialState, resultState, columnCount);
|
||||
cost += calcMissedFootswitchCost( row, jackedLeft, jackedRight, columnCount);
|
||||
cost += calcJackCost( movedLeft, movedRight, jackedLeft, jackedRight, elapsedTime, columnCount);
|
||||
cost += calcBigMovementsQuicklyCost( 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
|
||||
// for the next iteration when this is initialState
|
||||
resultState->columns = combinedColumns;
|
||||
for(int i = 0; i < columnCount; i++)
|
||||
{
|
||||
if(combinedColumns[i] >= NONE)
|
||||
{
|
||||
resultState->whereTheFeetAre[combinedColumns[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
// This merges the `columns` properties of initialState and resultState, which
|
||||
// fully represents the player's position on the dance stage.
|
||||
// For example:
|
||||
// initialState.columns = [1,0,0,3]
|
||||
// resultState.columns = [0,1,0,0]
|
||||
// combinedColumns = [0,1,0,3]
|
||||
// This eventually gets saved back to resultState
|
||||
void StepParityCost::mergeInitialAndResultPosition(State * initialState, State * resultState, std::vector<StepParity::Foot> & combinedColumns, int columnCount)
|
||||
{
|
||||
// Merge initial + result position
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
// copy in data from resultState over the top which overrides it, as long as it's not nothing
|
||||
if (resultState->columns[i] != NONE) {
|
||||
combinedColumns[i] = resultState->columns[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy in data from initialState, if it wasn't moved
|
||||
if (
|
||||
initialState->columns[i] == LEFT_HEEL ||
|
||||
initialState->columns[i] == RIGHT_HEEL
|
||||
) {
|
||||
if (!resultState->didTheFootMove[initialState->columns[i]]) {
|
||||
combinedColumns[i] = initialState->columns[i];
|
||||
}
|
||||
} else if (initialState->columns[i] == LEFT_TOE) {
|
||||
if (
|
||||
!resultState->didTheFootMove[LEFT_TOE] &&
|
||||
!resultState->didTheFootMove[LEFT_HEEL]
|
||||
) {
|
||||
combinedColumns[i] = initialState->columns[i];
|
||||
}
|
||||
} else if (initialState->columns[i] == RIGHT_TOE) {
|
||||
if (
|
||||
!resultState->didTheFootMove[RIGHT_TOE] &&
|
||||
!resultState->didTheFootMove[RIGHT_HEEL]
|
||||
) {
|
||||
combinedColumns[i] = initialState->columns[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the cost of avoiding a mine before the current step
|
||||
// If a mine occurred just before a step, add to the cost
|
||||
// ex:
|
||||
@@ -162,12 +102,12 @@ void StepParityCost::mergeInitialAndResultPosition(State * initialState, State *
|
||||
//
|
||||
// 00M0
|
||||
// 0100 <- no cost
|
||||
float StepParityCost::calcMineCost(State * initialState, State * resultState, Row &row, std::vector<StepParity::Foot>& combinedColumns, int columnCount)
|
||||
float StepParityCost::calcMineCost(State * initialState, State * resultState, Row &row, int columnCount)
|
||||
{
|
||||
float cost = 0;
|
||||
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
if (combinedColumns[i] != NONE && row.mines[i] != 0) {
|
||||
if (resultState->combinedColumns[i] != NONE && row.mines[i] != 0) {
|
||||
cost += MINE;
|
||||
break;
|
||||
}
|
||||
@@ -179,7 +119,7 @@ float StepParityCost::calcMineCost(State * initialState, State * resultState, Ro
|
||||
// Multiply the HOLDSWITCH cost by the distance that the "intial" foot
|
||||
// that was holding the note had to travel to it's new position.
|
||||
// If the initial foot doesn't move anywhere, then don't mulitply it by anything.
|
||||
float StepParityCost::calcHoldSwitchCost(State * initialState, State * resultState, Row &row, std::vector<StepParity::Foot> & combinedColumns, int columnCount)
|
||||
float StepParityCost::calcHoldSwitchCost(State * initialState, State * resultState, Row &row, int columnCount)
|
||||
{
|
||||
float cost = 0;
|
||||
for (int c = 0; c < columnCount; c++)
|
||||
@@ -187,18 +127,18 @@ float StepParityCost::calcHoldSwitchCost(State * initialState, State * resultSta
|
||||
if (row.holds[c].type == TapNoteType_Empty)
|
||||
continue;
|
||||
if (
|
||||
((combinedColumns[c] == LEFT_HEEL ||
|
||||
combinedColumns[c] == LEFT_TOE) &&
|
||||
initialState->columns[c] != LEFT_TOE &&
|
||||
initialState->columns[c] != LEFT_HEEL) ||
|
||||
((combinedColumns[c] == RIGHT_HEEL ||
|
||||
combinedColumns[c] == RIGHT_TOE) &&
|
||||
initialState->columns[c] != RIGHT_TOE &&
|
||||
initialState->columns[c] != RIGHT_HEEL)) {
|
||||
int previousFoot =initialState->whereTheFeetAre[combinedColumns[c]];
|
||||
((resultState->combinedColumns[c] == LEFT_HEEL ||
|
||||
resultState->combinedColumns[c] == LEFT_TOE) &&
|
||||
initialState->combinedColumns[c] != LEFT_TOE &&
|
||||
initialState->combinedColumns[c] != LEFT_HEEL) ||
|
||||
((resultState->combinedColumns[c] == RIGHT_HEEL ||
|
||||
resultState->combinedColumns[c] == RIGHT_TOE) &&
|
||||
initialState->combinedColumns[c] != RIGHT_TOE &&
|
||||
initialState->combinedColumns[c] != RIGHT_HEEL)) {
|
||||
int previousFoot =initialState->whereTheFeetAre[resultState->combinedColumns[c]];
|
||||
cost +=
|
||||
HOLDSWITCH *
|
||||
(previousFoot == -1
|
||||
(previousFoot == INVALID_COLUMN
|
||||
? 1
|
||||
: sqrt(
|
||||
layout.getDistanceSq(c, previousFoot)
|
||||
@@ -220,7 +160,7 @@ float StepParityCost::calcBracketTapCost(State * initialState, State * resultSta
|
||||
{
|
||||
// Small penalty for trying to jack a bracket during a hold
|
||||
float cost = 0;
|
||||
if (leftHeel != -1 && leftToe != -1)
|
||||
if (leftHeel != INVALID_COLUMN && leftToe != INVALID_COLUMN)
|
||||
{
|
||||
float jackPenalty = 1;
|
||||
if (
|
||||
@@ -240,7 +180,7 @@ float StepParityCost::calcBracketTapCost(State * initialState, State * resultSta
|
||||
}
|
||||
}
|
||||
|
||||
if (rightHeel != -1 && rightToe != -1) {
|
||||
if (rightHeel != INVALID_COLUMN && rightToe != INVALID_COLUMN) {
|
||||
float jackPenalty = 1;
|
||||
if (
|
||||
initialState->didTheFootMove[RIGHT_TOE] ||
|
||||
@@ -271,7 +211,7 @@ float StepParityCost::calcMovingFootWhileOtherIsntOnPadCost(State * initialState
|
||||
{
|
||||
float cost = 0;
|
||||
// Weighting for moving a foot while the other isn't on the pad (so marked doublesteps are less bad than this)
|
||||
if (std::any_of(initialState->columns.begin(), initialState->columns.end(), [](Foot elem) { return elem != NONE; }))
|
||||
if (std::any_of(initialState->combinedColumns.begin(), initialState->combinedColumns.end(), [](Foot elem) { return elem != NONE; }))
|
||||
{
|
||||
for (auto f : resultState->movedFeet)
|
||||
{
|
||||
@@ -281,16 +221,16 @@ float StepParityCost::calcMovingFootWhileOtherIsntOnPadCost(State * initialState
|
||||
case LEFT_TOE:
|
||||
if (
|
||||
!(
|
||||
initialState->whereTheFeetAre[RIGHT_HEEL] != -1 ||
|
||||
initialState->whereTheFeetAre[RIGHT_TOE] != -1))
|
||||
initialState->whereTheFeetAre[RIGHT_HEEL] != INVALID_COLUMN ||
|
||||
initialState->whereTheFeetAre[RIGHT_TOE] != INVALID_COLUMN))
|
||||
cost += OTHER;
|
||||
break;
|
||||
case RIGHT_HEEL:
|
||||
case RIGHT_TOE:
|
||||
if (
|
||||
!(
|
||||
initialState->whereTheFeetAre[LEFT_HEEL] != -1 ||
|
||||
initialState->whereTheFeetAre[LEFT_TOE] != -1))
|
||||
initialState->whereTheFeetAre[LEFT_HEEL] != INVALID_COLUMN ||
|
||||
initialState->whereTheFeetAre[LEFT_TOE] != INVALID_COLUMN))
|
||||
cost += OTHER;
|
||||
break;
|
||||
default:
|
||||
@@ -383,17 +323,17 @@ float StepParityCost::calcSlowBracketCost(Row & row, bool movedLeft, bool movedR
|
||||
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];
|
||||
int leftHeel = resultState->whatNoteTheFootIsHitting[LEFT_HEEL];
|
||||
int leftToe = resultState->whatNoteTheFootIsHitting[LEFT_TOE];
|
||||
int rightHeel = resultState->whatNoteTheFootIsHitting[RIGHT_HEEL];
|
||||
int rightToe = resultState->whatNoteTheFootIsHitting[RIGHT_TOE];
|
||||
|
||||
StagePoint leftPos = layout.averagePoint(leftHeel, leftToe);
|
||||
StagePoint rightPos = layout.averagePoint(rightHeel, rightToe);
|
||||
|
||||
bool crossedOver = rightPos.x < leftPos.x;
|
||||
bool rightBackwards = rightHeel != -1 && rightToe != -1 ? layout.columns[rightToe].y < layout.columns[rightHeel].y : false;
|
||||
bool leftBackwards = leftHeel != -1 && leftToe != -1 ? layout.columns[leftToe].y < layout.columns[leftHeel].y : false;
|
||||
bool rightBackwards = rightHeel != INVALID_COLUMN && rightToe != INVALID_COLUMN ? layout.columns[rightToe].y < layout.columns[rightHeel].y : false;
|
||||
bool leftBackwards = leftHeel != INVALID_COLUMN && leftToe != INVALID_COLUMN ? layout.columns[leftToe].y < layout.columns[leftHeel].y : false;
|
||||
|
||||
if(!crossedOver && (rightBackwards || leftBackwards))
|
||||
{
|
||||
@@ -418,18 +358,18 @@ float StepParityCost::calcMissedFootswitchCost(Row & row, bool jackedLeft, bool
|
||||
return cost;
|
||||
}
|
||||
|
||||
float StepParityCost::calcFacingCosts(State * initialState, State * resultState, std::vector<StepParity::Foot> & combinedColumns, int columnCount)
|
||||
float StepParityCost::calcFacingCosts(State * initialState, State * resultState, int columnCount)
|
||||
{
|
||||
|
||||
float cost = 0;
|
||||
|
||||
float endLeftHeel = -1;
|
||||
float endLeftToe = -1;
|
||||
float endRightHeel = -1;
|
||||
float endRightToe = -1;
|
||||
int endLeftHeel = INVALID_COLUMN;
|
||||
int endLeftToe = INVALID_COLUMN;
|
||||
int endRightHeel = INVALID_COLUMN;
|
||||
int endRightToe = INVALID_COLUMN;
|
||||
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
switch (combinedColumns[i]) {
|
||||
switch (resultState->combinedColumns[i]) {
|
||||
case NONE:
|
||||
break;
|
||||
case LEFT_HEEL:
|
||||
@@ -448,24 +388,24 @@ float StepParityCost::calcFacingCosts(State * initialState, State * resultState,
|
||||
}
|
||||
}
|
||||
|
||||
if (endLeftToe == -1) endLeftToe = endLeftHeel;
|
||||
if (endRightToe == -1) endRightToe = endRightHeel;
|
||||
if (endLeftToe == INVALID_COLUMN) endLeftToe = endLeftHeel;
|
||||
if (endRightToe == INVALID_COLUMN) endRightToe = endRightHeel;
|
||||
|
||||
// facing backwards gives a bit of bad weight (scaled heavily the further back you angle, so crossovers aren't Too bad; less bad than doublesteps)
|
||||
float heelFacing =
|
||||
endLeftHeel != -1 && endRightHeel != -1
|
||||
endLeftHeel != INVALID_COLUMN && endRightHeel != INVALID_COLUMN
|
||||
? layout.getXDifference(endLeftHeel, endRightHeel)
|
||||
: 0;
|
||||
float toeFacing =
|
||||
endLeftToe != -1 && endRightToe != -1
|
||||
endLeftToe != INVALID_COLUMN && endRightToe != INVALID_COLUMN
|
||||
? layout.getXDifference(endLeftToe, endRightToe)
|
||||
: 0;
|
||||
float leftFacing =
|
||||
endLeftHeel != -1 && endLeftToe != -1
|
||||
endLeftHeel != INVALID_COLUMN && endLeftToe != INVALID_COLUMN
|
||||
? layout.getYDifference(endLeftHeel, endLeftToe)
|
||||
: 0;
|
||||
float rightFacing =
|
||||
endRightHeel != -1 && endRightToe != -1
|
||||
endRightHeel != INVALID_COLUMN && endRightToe != INVALID_COLUMN
|
||||
? layout.getYDifference(endRightHeel, endRightToe)
|
||||
: 0;
|
||||
|
||||
@@ -488,17 +428,17 @@ float StepParityCost::calcFacingCosts(State * initialState, State * resultState,
|
||||
return cost;
|
||||
}
|
||||
|
||||
float StepParityCost::calcSpinCosts(State * initialState, State * resultState, std::vector<StepParity::Foot> & combinedColumns, int columnCount)
|
||||
float StepParityCost::calcSpinCosts(State * initialState, State * resultState, int columnCount)
|
||||
{
|
||||
float cost = 0;
|
||||
|
||||
float endLeftHeel = -1;
|
||||
float endLeftToe = -1;
|
||||
float endRightHeel = -1;
|
||||
float endRightToe = -1;
|
||||
int endLeftHeel = INVALID_COLUMN;
|
||||
int endLeftToe = INVALID_COLUMN;
|
||||
int endRightHeel = INVALID_COLUMN;
|
||||
int endRightToe = INVALID_COLUMN;
|
||||
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
switch (combinedColumns[i]) {
|
||||
switch (resultState->combinedColumns[i]) {
|
||||
case NONE:
|
||||
break;
|
||||
case LEFT_HEEL:
|
||||
@@ -517,8 +457,8 @@ float StepParityCost::calcSpinCosts(State * initialState, State * resultState, s
|
||||
}
|
||||
}
|
||||
|
||||
if (endLeftToe == -1) endLeftToe = endLeftHeel;
|
||||
if (endRightToe == -1) endRightToe = endRightHeel;
|
||||
if (endLeftToe == INVALID_COLUMN) endLeftToe = endLeftHeel;
|
||||
if (endRightToe == INVALID_COLUMN) endRightToe = endRightHeel;
|
||||
|
||||
// spin
|
||||
StagePoint previousLeftPos = layout.averagePoint(
|
||||
@@ -553,7 +493,7 @@ float StepParityCost::calcSpinCosts(State * initialState, State * resultState, s
|
||||
|
||||
// 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 StepParityCost::caclFootswitchCost(State * initialState, State * resultState, Row & row, float elapsedTime, int columnCount)
|
||||
{
|
||||
float cost = 0;
|
||||
if (elapsedTime >= SLOW_FOOTSWITCH_THRESHOLD && elapsedTime < SLOW_FOOTSWITCH_IGNORE) {
|
||||
@@ -569,13 +509,13 @@ float StepParityCost::caclFootswitchCost(State * initialState, State * resultSta
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
{
|
||||
if (
|
||||
initialState->columns[i] == NONE ||
|
||||
initialState->combinedColumns[i] == NONE ||
|
||||
resultState->columns[i] == NONE)
|
||||
continue;
|
||||
|
||||
if (
|
||||
initialState->columns[i] != resultState->columns[i] &&
|
||||
initialState->columns[i] != OTHER_PART_OF_FOOT[resultState->columns[i]]
|
||||
initialState->combinedColumns[i] != resultState->columns[i] &&
|
||||
initialState->combinedColumns[i] != OTHER_PART_OF_FOOT[resultState->columns[i]]
|
||||
)
|
||||
{
|
||||
cost += (timeScaled / (SLOW_FOOTSWITCH_THRESHOLD + timeScaled)) * FOOTSWITCH;
|
||||
@@ -593,10 +533,10 @@ float StepParityCost::calcSideswitchCost(State * initialState, State * resultSta
|
||||
for(auto c : layout.sideArrows)
|
||||
{
|
||||
if (
|
||||
initialState->columns[c] != resultState->columns[c] &&
|
||||
initialState->combinedColumns[c] != resultState->columns[c] &&
|
||||
resultState->columns[c] != NONE &&
|
||||
initialState->columns[c] != NONE &&
|
||||
!resultState->didTheFootMove[initialState->columns[c]])
|
||||
initialState->combinedColumns[c] != NONE &&
|
||||
!resultState->didTheFootMove[initialState->combinedColumns[c]])
|
||||
{
|
||||
cost += SIDESWITCH;
|
||||
}
|
||||
@@ -631,19 +571,19 @@ float StepParityCost::calcBigMovementsQuicklyCost(State * initialState, State *
|
||||
}
|
||||
|
||||
int initialPosition = initialState->whereTheFeetAre[foot];
|
||||
if(initialPosition == -1)
|
||||
if(initialPosition == INVALID_COLUMN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int resultPosition = resultState->whereTheFeetAre[foot];
|
||||
int resultPosition = resultState->whatNoteTheFootIsHitting[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)
|
||||
bool isBracketing = resultState->whatNoteTheFootIsHitting[OTHER_PART_OF_FOOT[foot]] != INVALID_COLUMN;
|
||||
if(isBracketing && resultState->whatNoteTheFootIsHitting[OTHER_PART_OF_FOOT[foot]] == initialPosition)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -667,21 +607,21 @@ float StepParityCost::calcCrowdedBracketCost(State * initialState, State * resul
|
||||
{
|
||||
float cost = 0;
|
||||
|
||||
bool resultLeftBracket = resultState->whereTheFeetAre[LEFT_HEEL] > -1 && resultState->whereTheFeetAre[LEFT_TOE] > -1;
|
||||
bool resultRightBracket = resultState->whereTheFeetAre[RIGHT_HEEL] > -1 && resultState->whereTheFeetAre[RIGHT_TOE] > -1;
|
||||
bool resultLeftBracket = resultState->whatNoteTheFootIsHitting[LEFT_HEEL] > INVALID_COLUMN && resultState->whatNoteTheFootIsHitting[LEFT_TOE] > INVALID_COLUMN;
|
||||
bool resultRightBracket = resultState->whatNoteTheFootIsHitting[RIGHT_HEEL] > INVALID_COLUMN && resultState->whatNoteTheFootIsHitting[RIGHT_TOE] > INVALID_COLUMN;
|
||||
|
||||
bool initialLeftBracket = initialState->whereTheFeetAre[LEFT_HEEL] > -1 && initialState->whereTheFeetAre[LEFT_TOE] > -1;
|
||||
bool initialRightBracket = initialState->whereTheFeetAre[RIGHT_HEEL] > -1 && initialState->whereTheFeetAre[RIGHT_TOE] > -1;
|
||||
bool initialLeftBracket = initialState->whereTheFeetAre[LEFT_HEEL] > INVALID_COLUMN && initialState->whereTheFeetAre[LEFT_TOE] > INVALID_COLUMN;
|
||||
bool initialRightBracket = initialState->whereTheFeetAre[RIGHT_HEEL] > INVALID_COLUMN && initialState->whereTheFeetAre[RIGHT_TOE] > INVALID_COLUMN;
|
||||
|
||||
// if we're trying to bracket with left foot, does it overlap the right foot
|
||||
// in previous state?
|
||||
if(
|
||||
(resultLeftBracket)
|
||||
&& (
|
||||
initialState->columns[resultState->whereTheFeetAre[LEFT_HEEL]] == RIGHT_HEEL ||
|
||||
initialState->columns[resultState->whereTheFeetAre[LEFT_HEEL]] == RIGHT_TOE ||
|
||||
initialState->columns[resultState->whereTheFeetAre[LEFT_TOE]] == RIGHT_HEEL ||
|
||||
initialState->columns[resultState->whereTheFeetAre[LEFT_TOE]] == RIGHT_TOE
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[LEFT_HEEL]] == RIGHT_HEEL ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[LEFT_HEEL]] == RIGHT_TOE ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[LEFT_TOE]] == RIGHT_HEEL ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[LEFT_TOE]] == RIGHT_TOE
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -702,10 +642,10 @@ float StepParityCost::calcCrowdedBracketCost(State * initialState, State * resul
|
||||
// and if we're trying to bracket with right foot, does it overlap the left ?
|
||||
if((resultRightBracket )
|
||||
&& (
|
||||
initialState->columns[resultState->whereTheFeetAre[RIGHT_HEEL]] == LEFT_HEEL ||
|
||||
initialState->columns[resultState->whereTheFeetAre[RIGHT_HEEL]] == LEFT_TOE ||
|
||||
initialState->columns[resultState->whereTheFeetAre[RIGHT_TOE]] == LEFT_HEEL ||
|
||||
initialState->columns[resultState->whereTheFeetAre[RIGHT_TOE]] == LEFT_TOE
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[RIGHT_HEEL]] == LEFT_HEEL ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[RIGHT_HEEL]] == LEFT_TOE ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[RIGHT_TOE]] == LEFT_HEEL ||
|
||||
initialState->combinedColumns[resultState->whatNoteTheFootIsHitting[RIGHT_TOE]] == LEFT_TOE
|
||||
)
|
||||
)
|
||||
{
|
||||
@@ -778,8 +718,8 @@ bool StepParityCost::didJackLeft(State * initialState, State * resultState, int
|
||||
if(!didJump && movedLeft)
|
||||
{
|
||||
|
||||
if ( leftHeel > -1 &&
|
||||
initialState->columns[leftHeel] == LEFT_HEEL &&
|
||||
if ( leftHeel > INVALID_COLUMN &&
|
||||
initialState->combinedColumns[leftHeel] == LEFT_HEEL &&
|
||||
!resultState->isTheFootHolding[LEFT_HEEL] &&
|
||||
((initialState->didTheFootMove[LEFT_HEEL] &&
|
||||
!initialState->isTheFootHolding[LEFT_HEEL]) ||
|
||||
@@ -789,8 +729,8 @@ bool StepParityCost::didJackLeft(State * initialState, State * resultState, int
|
||||
jackedLeft = true;
|
||||
}
|
||||
if (
|
||||
leftToe > -1 &&
|
||||
initialState->columns[leftToe] == LEFT_TOE &&
|
||||
leftToe > INVALID_COLUMN &&
|
||||
initialState->combinedColumns[leftToe] == LEFT_TOE &&
|
||||
!resultState->isTheFootHolding[LEFT_TOE] &&
|
||||
((initialState->didTheFootMove[LEFT_HEEL] &&
|
||||
!initialState->isTheFootHolding[LEFT_HEEL]) ||
|
||||
@@ -809,8 +749,8 @@ bool StepParityCost::didJackRight(State * initialState, State * resultState, int
|
||||
bool jackedRight = false;
|
||||
if(!didJump && movedRight)
|
||||
{
|
||||
if ( rightHeel > -1 &&
|
||||
initialState->columns[rightHeel] == RIGHT_HEEL &&
|
||||
if ( rightHeel > INVALID_COLUMN &&
|
||||
initialState->combinedColumns[rightHeel] == RIGHT_HEEL &&
|
||||
!resultState->isTheFootHolding[RIGHT_HEEL] &&
|
||||
((initialState->didTheFootMove[RIGHT_HEEL] &&
|
||||
!initialState->isTheFootHolding[RIGHT_HEEL]) ||
|
||||
@@ -819,8 +759,8 @@ bool StepParityCost::didJackRight(State * initialState, State * resultState, int
|
||||
) {
|
||||
jackedRight = true;
|
||||
}
|
||||
if ( rightToe > -1 &&
|
||||
initialState->columns[rightToe] == RIGHT_TOE &&
|
||||
if ( rightToe > INVALID_COLUMN &&
|
||||
initialState->combinedColumns[rightToe] == RIGHT_TOE &&
|
||||
!resultState->isTheFootHolding[RIGHT_TOE] &&
|
||||
((initialState->didTheFootMove[RIGHT_HEEL] &&
|
||||
!initialState->isTheFootHolding[RIGHT_HEEL]) ||
|
||||
|
||||
@@ -50,12 +50,11 @@ namespace StepParity
|
||||
/// @param rows
|
||||
/// @param rowIndex The index of the row represented by resultState
|
||||
/// @return The computed cost
|
||||
float getActionCost(State * initialState, State * resultState, std::vector<Row> &rows, int rowIndex);
|
||||
float getActionCost(State * initialState, State * resultState, std::vector<Row> &rows, int rowIndex, float elapsedTime);
|
||||
|
||||
private:
|
||||
void mergeInitialAndResultPosition(State * initialState, State * resultState, std::vector<StepParity::Foot> &combinedColumns, int columnCount);
|
||||
float calcMineCost(State * initialState, State * resultState, Row &row, std::vector<StepParity::Foot> &combinedColumns, int columnCount);
|
||||
float calcHoldSwitchCost(State * initialState, State * resultState, Row &row, std::vector<StepParity::Foot> &combinedColumns, int columnCount);
|
||||
float calcMineCost(State * initialState, State * resultState, Row &row, int columnCount);
|
||||
float calcHoldSwitchCost(State * initialState, State * resultState, Row &row, int columnCount);
|
||||
float calcBracketTapCost(State * initialState, State * resultState, Row &row, int leftHeel, int leftToe, int rightHeel, int rightToe, float elapsedTime, int columnCount);
|
||||
float calcMovingFootWhileOtherIsntOnPadCost(State * initialState, State * resultState, int columnCount);
|
||||
float calcBracketJackCost(State * initialState, State * resultState, std::vector<Row> &rows, int rowIndex, bool movedLeft, bool movedRight, bool jackedLeft, bool jackedRight, bool didJump, int columnCount);
|
||||
@@ -64,9 +63,9 @@ namespace StepParity
|
||||
float calcSlowBracketCost(Row & row, bool movedLeft, bool movedRight, float elapsedTime);
|
||||
float calcTwistedFootCost(State * resultState);
|
||||
float calcMissedFootswitchCost(Row &row, bool jackedLeft, bool jackedRight, int columnCount);
|
||||
float calcFacingCosts(State * initialState, State * resultState, std::vector<StepParity::Foot> &combinedColumns, int columnCount);
|
||||
float calcSpinCosts(State * initialState, State * resultState, std::vector<StepParity::Foot> & combinedColumns, int columnCount);
|
||||
float caclFootswitchCost(State * initialState, State * resultState, Row &row, std::vector<StepParity::Foot> &combinedColumns, float elapsedTime, int columnCount);
|
||||
float calcFacingCosts(State * initialState, State * resultState, int columnCount);
|
||||
float calcSpinCosts(State * initialState, State * resultState, int columnCount);
|
||||
float caclFootswitchCost(State * initialState, State * resultState, Row &row, float elapsedTime, int columnCount);
|
||||
float calcSideswitchCost(State * initialState, State * resultState, int columnCount);
|
||||
float calcJackCost(bool movedLeft, bool movedRight, bool jackedLeft, bool jackedRight, float elapsedTime, int columnCount);
|
||||
float calcBigMovementsQuicklyCost(State * initialState, State * resultState, float elapsedTime, int columnCount);
|
||||
|
||||
+16
-219
@@ -3,74 +3,15 @@
|
||||
|
||||
using namespace StepParity;
|
||||
|
||||
// Graph/Node methods
|
||||
int calculateVectorHash(const std::vector<Foot> &vec)
|
||||
{
|
||||
int value = 0;
|
||||
for(Foot f : vec)
|
||||
{
|
||||
value *= 5;
|
||||
value += f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool State::operator==(const State &other) const
|
||||
{
|
||||
return rowIndex == other.rowIndex &&
|
||||
columns == other.columns &&
|
||||
return columns == other.columns &&
|
||||
combinedColumns == other.combinedColumns &&
|
||||
movedFeet == other.movedFeet &&
|
||||
holdFeet == other.holdFeet;
|
||||
}
|
||||
|
||||
bool State::operator<(const State &other) const
|
||||
{
|
||||
|
||||
if(rowIndex != other.rowIndex) {
|
||||
return rowIndex < other.rowIndex;
|
||||
}
|
||||
if(columnsHash != other.columnsHash) {
|
||||
return columnsHash < other.columnsHash;
|
||||
}
|
||||
if(movedFeetHash != other.movedFeetHash) {
|
||||
return movedFeetHash < other.movedFeetHash;
|
||||
}
|
||||
if(holdFeetHash != other.holdFeetHash) {
|
||||
return holdFeetHash < other.holdFeetHash;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void State::calculateHashes()
|
||||
{
|
||||
columnsHash = calculateVectorHash(columns);
|
||||
movedFeetHash = calculateVectorHash(movedFeet);
|
||||
holdFeetHash = calculateVectorHash(holdFeet);
|
||||
}
|
||||
|
||||
StepParityNode * StepParityGraph::addOrGetExistingNode(const State &state)
|
||||
{
|
||||
// This is silly, but the start node has a rowIndex of -1
|
||||
// which doesn't work as an array index.
|
||||
int rowIndex = state.rowIndex + 1;
|
||||
while (static_cast<int>(stateNodeMap.size()) <= rowIndex)
|
||||
{
|
||||
stateNodeMap.push_back(std::map<State, StepParityNode *, StateComparator>());
|
||||
}
|
||||
|
||||
if(stateNodeMap[rowIndex].find(state) == stateNodeMap[rowIndex].end())
|
||||
{
|
||||
StepParityNode* newNode = new StepParityNode(state);
|
||||
newNode->id = int(nodes.size());
|
||||
nodes.push_back(newNode);
|
||||
newNode->state.idx = int(states.size());
|
||||
states.push_back(&(newNode->state));
|
||||
stateNodeMap[rowIndex][state] = newNode;
|
||||
}
|
||||
|
||||
return stateNodeMap[rowIndex][state];
|
||||
}
|
||||
|
||||
// StageLayout
|
||||
bool StageLayout::bracketCheck(int column1, int column2)
|
||||
{
|
||||
@@ -176,166 +117,22 @@ void Row::setFootPlacement(const std::vector<Foot> & footPlacement)
|
||||
}
|
||||
}
|
||||
|
||||
// Json methods
|
||||
template<typename Container>
|
||||
Json::Value FeetToJson(const Container& feets, bool useStrings)
|
||||
bool Row::operator==(const Row& other) const
|
||||
{
|
||||
Json::Value root;
|
||||
for(Foot f: feets)
|
||||
{
|
||||
if(useStrings)
|
||||
{
|
||||
root.append(FEET_LABELS[static_cast<int>(f)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
root.append(static_cast<int>(f));
|
||||
}
|
||||
}
|
||||
return root;
|
||||
return second == other.second &&
|
||||
beat == other.beat &&
|
||||
rowIndex == other.rowIndex &&
|
||||
columnCount == other.columnCount &&
|
||||
noteCount == other.noteCount &&
|
||||
holdTails == other.holdTails &&
|
||||
mines == other.mines &&
|
||||
fakeMines == other.fakeMines &&
|
||||
columns == other.columns &&
|
||||
whereTheFeetAre == other.whereTheFeetAre;
|
||||
|
||||
}
|
||||
|
||||
Json::Value State::ToJson(bool useStrings)
|
||||
bool Row::operator!=(const Row& other) const
|
||||
{
|
||||
Json::Value root;
|
||||
Json::Value jsonColumns = FeetToJson(columns, useStrings);
|
||||
Json::Value jsonMovedFeet = FeetToJson(movedFeet, useStrings);
|
||||
Json::Value jsonHoldFeet = FeetToJson(holdFeet, useStrings);
|
||||
|
||||
root["idx"] = idx;
|
||||
root["columns"] = jsonColumns;
|
||||
root["movedFeet"] = jsonMovedFeet;
|
||||
root["holdFeet"] = jsonHoldFeet;
|
||||
root["second"] = second;
|
||||
root["rowIndex"] = rowIndex;
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value IntermediateNoteData::ToJson(bool useStrings)
|
||||
{
|
||||
Json::Value root;
|
||||
if(useStrings)
|
||||
{
|
||||
root["type"] = TapNoteTypeShortNames[static_cast<int>(type)];
|
||||
root["subtype"] = TapNoteSubTypeShortNames[static_cast<int>(subtype)];
|
||||
root["parity"] = FEET_LABELS[static_cast<int>(parity)];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
root["type"] = static_cast<int>(type);
|
||||
root["subtype"] = static_cast<int>(subtype);
|
||||
root["parity"] = static_cast<int>(parity);
|
||||
}
|
||||
root["col"] = col;
|
||||
root["row"] = row;
|
||||
root["beat"] = beat;
|
||||
root["hold"] = hold_length;
|
||||
root["warped"] = warped;
|
||||
root["fake"] = fake;
|
||||
root["second"] = second;
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value Row::ToJson(bool useStrings)
|
||||
{
|
||||
Json::Value root;
|
||||
Json::Value jsonNotes;
|
||||
Json::Value jsonHolds;
|
||||
Json::Value jsonHoldTails;
|
||||
Json::Value jsonMines;
|
||||
Json::Value jsonFakeMines;
|
||||
|
||||
for (IntermediateNoteData n : notes) { jsonNotes.append(n.ToJson(useStrings)); }
|
||||
for (IntermediateNoteData n : holds) { jsonHolds.append(n.ToJson(useStrings)); }
|
||||
for (int t : holdTails) { jsonHoldTails.append(t); }
|
||||
for (int m : mines) { jsonMines.append(m); }
|
||||
for (int f : fakeMines) { jsonFakeMines.append(f); }
|
||||
|
||||
root["notes"] = jsonNotes;
|
||||
root["holds"] = jsonHolds;
|
||||
root["hold_tails"] = jsonHoldTails;
|
||||
root["mines"] = jsonMines;
|
||||
root["fake_mines"] = jsonFakeMines;
|
||||
root["second"] = second;
|
||||
root["beat"] = beat;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value StepParityNode::ToJson()
|
||||
{
|
||||
Json::Value root;
|
||||
Json::Value jsonNeighbors;
|
||||
|
||||
for (auto it = neighbors.begin(); it != neighbors.end(); it++)
|
||||
{
|
||||
Json::Value n;
|
||||
n["id"] = it->first->id;
|
||||
Json::Value jsonCosts;
|
||||
float cost = it->second;
|
||||
n["cost"] = cost;
|
||||
jsonNeighbors.append(n);
|
||||
}
|
||||
root["id"] = id;
|
||||
root["stateIdx"] = state.idx;
|
||||
root["neighbors"] = jsonNeighbors;
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value Row::ToJsonRows(const std::vector<Row> & rows, bool useStrings)
|
||||
{
|
||||
Json::Value root;
|
||||
for(Row row: rows)
|
||||
{
|
||||
root.append(row.ToJson(useStrings));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value Row::ParityRowsJson(const std::vector<Row> & rows)
|
||||
{
|
||||
Json::Value root;
|
||||
for(Row row: rows)
|
||||
{
|
||||
Json::Value pr;
|
||||
for(IntermediateNoteData note: row.notes)
|
||||
{
|
||||
pr.append(static_cast<int>(note.parity));
|
||||
}
|
||||
root.append(pr);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value StepParityGraph::ToJson()
|
||||
{
|
||||
Json::Value jsonNodes;
|
||||
for(auto node: nodes)
|
||||
{
|
||||
jsonNodes.append(node->ToJson());
|
||||
}
|
||||
Json::Value jsonStates;
|
||||
for(auto state: states)
|
||||
{
|
||||
jsonStates.append(state->ToJson(false));
|
||||
}
|
||||
|
||||
Json::Value root;
|
||||
root["nodes"] = jsonNodes;
|
||||
root["states"] = jsonStates;
|
||||
return root;
|
||||
}
|
||||
|
||||
Json::Value StepParityGraph::NodeStateJson()
|
||||
{
|
||||
Json::Value root;
|
||||
for(auto node: nodes)
|
||||
{
|
||||
Json::Value nodeJson;
|
||||
nodeJson["id"] = node->id;
|
||||
nodeJson["state"] = node->state.ToJson(false);
|
||||
root.append(nodeJson);
|
||||
}
|
||||
return root;
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
+21
-92
@@ -3,13 +3,12 @@
|
||||
|
||||
#include "GameConstantsAndTypes.h"
|
||||
#include "NoteData.h"
|
||||
#include "json/json.h"
|
||||
#include "JsonUtil.h"
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace StepParity {
|
||||
|
||||
const int INVALID_COLUMN = -1;
|
||||
const float CLM_SECOND_INVALID = -1;
|
||||
|
||||
enum Foot
|
||||
@@ -119,49 +118,34 @@ namespace StepParity {
|
||||
/// @brief Represents a specific possible state of the player's position
|
||||
/// for a given row of the step chart.
|
||||
struct State {
|
||||
FootPlacement columns; // The position of the player
|
||||
FootPlacement columns; // what the feet are hitting on this row
|
||||
FootPlacement combinedColumns; // The resulting position of the player
|
||||
FootPlacement movedFeet; // Any feet that have moved from the previous state to this one
|
||||
FootPlacement holdFeet; // Any feet that stayed in place due to a hold/roll note.
|
||||
float second; // The time of the song represented by this state
|
||||
int rowIndex; // The index of the row represented by this state
|
||||
int idx;
|
||||
|
||||
int whereTheFeetAre[NUM_Foot]; // the inverse of columns
|
||||
int whereTheFeetAre[NUM_Foot]; // the inverse of combinedColumns
|
||||
int whatNoteTheFootIsHitting[NUM_Foot]; // the inverse of columns
|
||||
bool didTheFootMove[NUM_Foot]; // the inverse of movedFeet
|
||||
bool isTheFootHolding[NUM_Foot]; //inverse of holdFeet
|
||||
// These hashes are used in operator<() to speed up the comparison of the vectors.
|
||||
// Their values are computed by calculateHashes(), which is used in StepParityGenerator::buildStateGraph().
|
||||
int columnsHash = 0;
|
||||
int movedFeetHash = 0;
|
||||
int holdFeetHash = 0;
|
||||
|
||||
State()
|
||||
{
|
||||
State(4);
|
||||
}
|
||||
|
||||
State(int columnCount)
|
||||
{
|
||||
columns = FootPlacement(columnCount, NONE);
|
||||
combinedColumns = FootPlacement(columnCount, NONE);
|
||||
movedFeet = FootPlacement(columnCount, NONE);
|
||||
holdFeet = FootPlacement(columnCount, NONE);
|
||||
second = 0;
|
||||
rowIndex = 0;
|
||||
idx = -1;
|
||||
|
||||
for(int i = 0; i < NUM_Foot; i++)
|
||||
{
|
||||
whereTheFeetAre[i] = -1;
|
||||
whatNoteTheFootIsHitting[i] = INVALID_COLUMN;
|
||||
whereTheFeetAre[i] = INVALID_COLUMN;
|
||||
didTheFootMove[i] = false;
|
||||
isTheFootHolding[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value ToJson(bool useStrings);
|
||||
|
||||
bool operator==(const State& other) const;
|
||||
bool operator<(const State& other) const;
|
||||
|
||||
void calculateHashes();
|
||||
bool operator==(const State& other) const;
|
||||
};
|
||||
|
||||
/// @brief A convenience struct used to encapsulate data from NoteData in an
|
||||
@@ -179,7 +163,6 @@ namespace StepParity {
|
||||
float second = false; // time into the song on which the note occurs
|
||||
|
||||
Foot parity = NONE; // Which foot (and which part of the foot) will most likely be used
|
||||
Json::Value ToJson(bool useStrings);
|
||||
};
|
||||
|
||||
|
||||
@@ -223,14 +206,13 @@ namespace StepParity {
|
||||
mines = std::vector<float>(columnCount, 0);
|
||||
fakeMines = std::vector<float>(columnCount, 0);
|
||||
columns = std::vector<StepParity::Foot>(columnCount, StepParity::NONE);
|
||||
whereTheFeetAre = std::vector<int>(StepParity::NUM_Foot, -1);
|
||||
whereTheFeetAre = std::vector<int>(StepParity::NUM_Foot, INVALID_COLUMN);
|
||||
}
|
||||
|
||||
void setFootPlacement(const std::vector<Foot> & footPlacement);
|
||||
|
||||
Json::Value ToJson(bool useStrings);
|
||||
static Json::Value ToJsonRows(const std::vector<Row> & rows, bool useStrings);
|
||||
static Json::Value ParityRowsJson(const std::vector<Row> & rows);
|
||||
|
||||
bool operator==(const Row& other) const;
|
||||
bool operator!=(const Row& other) const;
|
||||
};
|
||||
|
||||
/// @brief A counter used while creating rows
|
||||
@@ -279,8 +261,10 @@ namespace StepParity {
|
||||
{
|
||||
// The index of this node in its graph
|
||||
int id = 0;
|
||||
State state;
|
||||
|
||||
State * state;
|
||||
int rowIndex = 0;
|
||||
float second = 0;
|
||||
|
||||
// Connections to, and the cost of moving to, the connected nodes
|
||||
std::unordered_map<StepParityNode *, float> neighbors;
|
||||
|
||||
@@ -288,73 +272,18 @@ namespace StepParity {
|
||||
{
|
||||
neighbors.clear();
|
||||
}
|
||||
StepParityNode(const State &_state)
|
||||
StepParityNode(State *_state, float _second, int _rowIndex)
|
||||
{
|
||||
state = _state;
|
||||
rowIndex = _rowIndex;
|
||||
second = _second;
|
||||
}
|
||||
|
||||
Json::Value ToJson();
|
||||
|
||||
int neighborCount()
|
||||
{
|
||||
return static_cast<int>(neighbors.size());
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief A comparator, needed in order use State objects as the key in a std::map.
|
||||
struct StateComparator
|
||||
{
|
||||
bool operator()(const State& lhs, const State& rhs) const {
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief A graph, representing all of the possible states for a step chart.
|
||||
class StepParityGraph
|
||||
{
|
||||
private:
|
||||
std::vector<StepParityNode *> nodes;
|
||||
std::vector<State *> states;
|
||||
std::vector<std::map<State, StepParityNode *, StateComparator>> stateNodeMap;
|
||||
|
||||
public:
|
||||
// This represents the very start of the song, before any notes
|
||||
StepParityNode * startNode;
|
||||
// This represents the end of the song, after all of the notes
|
||||
StepParityNode *endNode;
|
||||
|
||||
~StepParityGraph()
|
||||
{
|
||||
states.clear();
|
||||
for(StepParityNode * node: nodes)
|
||||
{
|
||||
delete node;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Returns a pointer to a StepParityNode that represents the given state within the graph.
|
||||
/// If a node already exists, it is returned, otherwise a new one is created and added to the graph.
|
||||
/// @param state
|
||||
/// @return
|
||||
StepParityNode *addOrGetExistingNode(const State &state);
|
||||
|
||||
void addEdge(StepParityNode* from, StepParityNode* to, float cost)
|
||||
{
|
||||
from->neighbors[to] = cost;
|
||||
}
|
||||
|
||||
int nodeCount() const
|
||||
{
|
||||
return static_cast<int>(nodes.size());
|
||||
}
|
||||
|
||||
Json::Value ToJson();
|
||||
Json::Value NodeStateJson();
|
||||
StepParityNode *operator[](int index) const
|
||||
{
|
||||
return nodes[index];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+199
-76
@@ -28,8 +28,8 @@ void StepParityGenerator::analyzeGraph() {
|
||||
|
||||
for (unsigned long i = 0; i < rows.size(); i++)
|
||||
{
|
||||
StepParityNode *node = graph[nodes_for_rows[i]];
|
||||
rows[i].setFootPlacement(node->state.columns);
|
||||
StepParityNode *node = nodes[nodes_for_rows[i]];
|
||||
rows[i].setFootPlacement(node->state->combinedColumns);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,87 +37,93 @@ void StepParityGenerator::buildStateGraph()
|
||||
{
|
||||
// The first node of the graph is beginningState, which represents the time before
|
||||
// the first note (and so it's roIndex is considered -1)
|
||||
State beginningState(columnCount);
|
||||
beginningState.rowIndex = -1;
|
||||
beginningState.second = rows[0].second - 1;
|
||||
StepParityNode *startNode = graph.addOrGetExistingNode(beginningState);
|
||||
|
||||
graph.startNode = startNode;
|
||||
|
||||
std::queue<State> previousStates;
|
||||
previousStates.push(beginningState);
|
||||
beginningState = new State(columnCount);
|
||||
startNode = addNode(beginningState, rows[0].second - 1, -1);
|
||||
|
||||
std::queue<StepParityNode *> previousNodes;
|
||||
previousNodes.push(startNode);
|
||||
StepParityCost costCalculator(layout);
|
||||
|
||||
for (unsigned long i = 0; i < rows.size(); i++)
|
||||
{
|
||||
std::vector<State> uniqueStates;
|
||||
std::vector<StepParityNode *> resultNodes;
|
||||
Row &row = rows[i];
|
||||
std::vector<FootPlacement> *PermuteFootPlacements = getFootPlacementPermutations(row);
|
||||
while (!previousStates.empty())
|
||||
while (!previousNodes.empty())
|
||||
{
|
||||
State state = previousStates.front();
|
||||
StepParityNode *initialNode = graph.addOrGetExistingNode(state);
|
||||
|
||||
StepParityNode *initialNode = previousNodes.front();
|
||||
float elapsedTime = row.second - initialNode->second;
|
||||
for(auto it = PermuteFootPlacements->begin(); it != PermuteFootPlacements->end(); it++)
|
||||
{
|
||||
State resultState = initResultState(state, row, *it);
|
||||
float cost = costCalculator.getActionCost(&state, &resultState, rows, i);
|
||||
resultState.calculateHashes();
|
||||
StepParityNode *resultNode = graph.addOrGetExistingNode(resultState);
|
||||
graph.addEdge(initialNode, resultNode, cost);
|
||||
if(std::find(uniqueStates.begin(), uniqueStates.end(), resultState) == uniqueStates.end())
|
||||
{
|
||||
uniqueStates.push_back(resultState);
|
||||
}
|
||||
State * resultState = initResultState(initialNode->state, row, *it);
|
||||
|
||||
float cost = costCalculator.getActionCost(initialNode->state, resultState, rows, i, elapsedTime);
|
||||
addStateToGraph(resultState, initialNode, row, resultNodes, cost);
|
||||
|
||||
}
|
||||
previousStates.pop();
|
||||
previousNodes.pop();
|
||||
}
|
||||
|
||||
for (State s : uniqueStates)
|
||||
for (StepParityNode * n : resultNodes)
|
||||
{
|
||||
previousStates.push(s);
|
||||
previousNodes.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, previousStates holds all of the states for the very last row,
|
||||
// which just get connected to the endState
|
||||
State endState(columnCount);
|
||||
endState.rowIndex = rows.size();
|
||||
endState.second = rows[rows.size() - 1].second + 1;
|
||||
StepParityNode *endNode = graph.addOrGetExistingNode(endState);
|
||||
graph.endNode = endNode;
|
||||
while(!previousStates.empty())
|
||||
endingState = new State(columnCount);
|
||||
endNode = addNode(endingState, rows[rows.size() - 1].second + 1, rows.size());
|
||||
|
||||
while(!previousNodes.empty())
|
||||
{
|
||||
State state = previousStates.front();
|
||||
StepParityNode *node = graph.addOrGetExistingNode(state);
|
||||
graph.addEdge(node, endNode, 0);
|
||||
previousStates.pop();
|
||||
StepParityNode *node = previousNodes.front();
|
||||
addEdge(node, endNode, 0);
|
||||
previousNodes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
State StepParityGenerator::initResultState(State &initialState, Row &row, const FootPlacement &columns)
|
||||
void StepParityGenerator::addStateToGraph(State * resultState, StepParityNode * initialNode, Row & row, std::vector<StepParityNode *> &existingNodesForThisRow, float cost)
|
||||
{
|
||||
State resultState(row.columnCount);
|
||||
resultState.columns = columns;
|
||||
resultState.rowIndex = row.rowIndex;
|
||||
|
||||
for(StepParityNode * existingNode : existingNodesForThisRow)
|
||||
{
|
||||
if(existingNode->state == resultState)
|
||||
{
|
||||
addEdge(initialNode, existingNode, cost);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StepParityNode *resultNode = addNode(resultState, row.second, row.rowIndex);
|
||||
addEdge(initialNode, resultNode, cost);
|
||||
existingNodesForThisRow.push_back(resultNode);
|
||||
}
|
||||
|
||||
|
||||
State * StepParityGenerator::initResultState(State * initialState, Row &row, const FootPlacement &columns)
|
||||
{
|
||||
State * resultState = new State(row.columnCount);
|
||||
resultState->columns = columns;
|
||||
|
||||
|
||||
// I tried to condense this, but kept getting the logic messed up
|
||||
for (unsigned long i = 0; i < columns.size(); i++)
|
||||
{
|
||||
if(columns[i] == NONE) {
|
||||
continue;
|
||||
}
|
||||
resultState.whereTheFeetAre[columns[i]] = i;
|
||||
resultState->whatNoteTheFootIsHitting[columns[i]] = i;
|
||||
|
||||
if(row.holds[i].type == TapNoteType_Empty)
|
||||
{
|
||||
resultState.movedFeet[i] = columns[i];
|
||||
resultState.didTheFootMove[columns[i]] = true;
|
||||
resultState->movedFeet[i] = columns[i];
|
||||
resultState->didTheFootMove[columns[i]] = true;
|
||||
continue;
|
||||
}
|
||||
if(initialState.columns[i] != columns[i])
|
||||
if(initialState->combinedColumns[i] != columns[i])
|
||||
{
|
||||
resultState.movedFeet[i] = columns[i];
|
||||
resultState.didTheFootMove[columns[i]] = true;
|
||||
resultState->movedFeet[i] = columns[i];
|
||||
resultState->didTheFootMove[columns[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +135,87 @@ State StepParityGenerator::initResultState(State &initialState, Row &row, const
|
||||
|
||||
if(row.holds[i].type != TapNoteType_Empty)
|
||||
{
|
||||
resultState.holdFeet[i] = columns[i];
|
||||
resultState.isTheFootHolding[columns[i]] = true;
|
||||
resultState->holdFeet[i] = columns[i];
|
||||
resultState->isTheFootHolding[columns[i]] = true;
|
||||
}
|
||||
}
|
||||
resultState.second = row.second;
|
||||
|
||||
mergeInitialAndResultPosition(initialState, resultState, (int)columns.size());
|
||||
|
||||
std::uint64_t stateHash = getStateCacheKey(resultState);
|
||||
|
||||
auto maybeState = stateCache.find(stateHash);
|
||||
|
||||
if(maybeState != stateCache.end())
|
||||
{
|
||||
State* cachedState = maybeState->second;
|
||||
delete resultState;
|
||||
return maybeState->second;
|
||||
}
|
||||
|
||||
stateCache.insert({stateHash, resultState});
|
||||
|
||||
return resultState;
|
||||
}
|
||||
|
||||
// This merges the `columns` properties of initialState and resultState, which
|
||||
// fully represents the player's position on the dance stage.
|
||||
// For example:
|
||||
// initialState.combinedColumns = [L,0,0,R]
|
||||
// resultState.columns = [0,L,0,0]
|
||||
// combinedColumns = [0,L,0,R]
|
||||
// This eventually gets saved back to resultState
|
||||
void StepParityGenerator::mergeInitialAndResultPosition(State * initialState, State * resultState, int columnCount)
|
||||
{
|
||||
// Merge initial + result position
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
// copy in data from resultState over the top which overrides it, as long as it's not nothing
|
||||
if (resultState->columns[i] != NONE) {
|
||||
resultState->combinedColumns[i] = resultState->columns[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
// copy in data from initialState, if it wasn't moved
|
||||
if (
|
||||
initialState->combinedColumns[i] == LEFT_HEEL ||
|
||||
initialState->combinedColumns[i] == RIGHT_HEEL
|
||||
) {
|
||||
if (!resultState->didTheFootMove[initialState->combinedColumns[i]]) {
|
||||
resultState->combinedColumns[i] = initialState->combinedColumns[i];
|
||||
}
|
||||
} else if (initialState->combinedColumns[i] == LEFT_TOE) {
|
||||
if (
|
||||
!resultState->didTheFootMove[LEFT_TOE] &&
|
||||
!resultState->didTheFootMove[LEFT_HEEL]
|
||||
) {
|
||||
resultState->combinedColumns[i] = initialState->combinedColumns[i];
|
||||
}
|
||||
} else if (initialState->combinedColumns[i] == RIGHT_TOE) {
|
||||
if (
|
||||
!resultState->didTheFootMove[RIGHT_TOE] &&
|
||||
!resultState->didTheFootMove[RIGHT_HEEL]
|
||||
) {
|
||||
resultState->combinedColumns[i] = initialState->combinedColumns[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < columnCount; i++)
|
||||
{
|
||||
if(resultState->combinedColumns[i] != NONE)
|
||||
{
|
||||
resultState->whereTheFeetAre[resultState->combinedColumns[i]] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For the given row, generate all of the possible foot placements
|
||||
// (even if they're not physically possible)
|
||||
//
|
||||
// We cache this data and return a pointer for two reasons:
|
||||
// - It takes a long time to generate the permutations
|
||||
// - We end up generating a lot of redundant data, so caching it saves memory
|
||||
|
||||
std::vector<FootPlacement>* StepParityGenerator::getFootPlacementPermutations(const Row &row)
|
||||
{
|
||||
int cacheKey = getPermuteCacheKey(row);
|
||||
@@ -151,15 +230,18 @@ std::vector<FootPlacement>* StepParityGenerator::getFootPlacementPermutations(co
|
||||
return &permuteCache[cacheKey];
|
||||
}
|
||||
|
||||
|
||||
// Recursively generate each permutation for the given row.
|
||||
std::vector<FootPlacement> StepParityGenerator::PermuteFootPlacements(const Row &row, FootPlacement columns, unsigned long column)
|
||||
{
|
||||
// If column >= columns.size(), we've reached the end of the row.
|
||||
// Perform some final validation before returning the contents of columns
|
||||
if (column >= columns.size())
|
||||
{
|
||||
int leftHeelIndex = -1;
|
||||
int leftToeIndex = -1;
|
||||
int rightHeelIndex = -1;
|
||||
int rightToeIndex = -1;
|
||||
int leftHeelIndex = StepParity::INVALID_COLUMN;
|
||||
int leftToeIndex = StepParity::INVALID_COLUMN;
|
||||
int rightHeelIndex = StepParity::INVALID_COLUMN;
|
||||
int rightToeIndex = StepParity::INVALID_COLUMN;
|
||||
|
||||
for (unsigned long i = 0; i < columns.size(); i++)
|
||||
{
|
||||
if (columns[i] == NONE)
|
||||
@@ -183,20 +265,24 @@ std::vector<FootPlacement> StepParityGenerator::PermuteFootPlacements(const Row
|
||||
rightToeIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out actually invalid combinations:
|
||||
// - We don't want permutations where the toe is on an arrow, but not the heel
|
||||
// - We don't want impossible brackets (eg you can't bracket up and down)
|
||||
if (
|
||||
(leftHeelIndex == -1 && leftToeIndex != -1) ||
|
||||
(rightHeelIndex == -1 && rightToeIndex != -1))
|
||||
(leftHeelIndex == StepParity::INVALID_COLUMN && leftToeIndex != StepParity::INVALID_COLUMN) ||
|
||||
(rightHeelIndex == StepParity::INVALID_COLUMN && rightToeIndex != StepParity::INVALID_COLUMN))
|
||||
{
|
||||
return std::vector<FootPlacement>();
|
||||
}
|
||||
if (leftHeelIndex != -1 && leftToeIndex != -1)
|
||||
if (leftHeelIndex != StepParity::INVALID_COLUMN && leftToeIndex != StepParity::INVALID_COLUMN)
|
||||
{
|
||||
if (!layout.bracketCheck(leftHeelIndex, leftToeIndex))
|
||||
{
|
||||
return std::vector<FootPlacement>();
|
||||
}
|
||||
}
|
||||
if (rightHeelIndex != -1 && rightToeIndex != -1)
|
||||
if (rightHeelIndex != StepParity::INVALID_COLUMN && rightToeIndex != StepParity::INVALID_COLUMN)
|
||||
{
|
||||
if (!layout.bracketCheck(rightHeelIndex, rightToeIndex))
|
||||
{
|
||||
@@ -206,11 +292,18 @@ std::vector<FootPlacement> StepParityGenerator::PermuteFootPlacements(const Row
|
||||
return {columns};
|
||||
}
|
||||
|
||||
// If this column has a valid tap/hold head, or is actively holding a note,
|
||||
// iterate through values of StepParity::Foot. For each foot part, check that
|
||||
// it's not already present in columns, and if not, create a copy of columns,
|
||||
// and set the current foot part to the current column.
|
||||
// Then pass it to PermuteFootPlacements() and increment the column index.
|
||||
// Collect each permutationm, and then return all of them.
|
||||
|
||||
std::vector<FootPlacement> permutations;
|
||||
if (row.notes[column].type != TapNoteType_Empty ||
|
||||
row.holds[column].type != TapNoteType_Empty)
|
||||
{
|
||||
for (StepParity::Foot foot: FEET) {
|
||||
for (StepParity::Foot foot: FEET) {
|
||||
if(std::find(columns.begin(), columns.end(), foot) != columns.end())
|
||||
{
|
||||
continue;
|
||||
@@ -221,24 +314,27 @@ std::vector<FootPlacement> StepParityGenerator::PermuteFootPlacements(const Row
|
||||
newColumns[column] = foot;
|
||||
std::vector<FootPlacement> p = PermuteFootPlacements(row, newColumns, column + 1);
|
||||
permutations.insert(permutations.end(), p.begin(), p.end());
|
||||
}
|
||||
return permutations;
|
||||
}
|
||||
return permutations;
|
||||
}
|
||||
// If the current column doesn't have any taps or holds,
|
||||
// then we don't need to generate any permutations for it.
|
||||
// Return the contents of calling PermuteFootPlacements() for the next column.
|
||||
return PermuteFootPlacements(row, columns, column + 1);
|
||||
}
|
||||
|
||||
std::vector<int> StepParityGenerator::computeCheapestPath()
|
||||
{
|
||||
int start = graph.startNode->id;
|
||||
int end = graph.endNode->id;
|
||||
int start = startNode->id;
|
||||
int end = endNode->id;
|
||||
std::vector<int> shortest_path;
|
||||
std::vector<float> cost(graph.nodeCount(), FLT_MAX);
|
||||
std::vector<int> predecessor(graph.nodeCount(), -1);
|
||||
std::vector<float> cost(nodes.size(), FLT_MAX);
|
||||
std::vector<int> predecessor(nodes.size(), -1);
|
||||
|
||||
cost[start] = 0;
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
StepParityNode *node = graph[i];
|
||||
StepParityNode *node = nodes[i];
|
||||
for(auto neighbor: node->neighbors)
|
||||
{
|
||||
int neighbor_id = neighbor.first->id;
|
||||
@@ -472,15 +568,42 @@ int StepParityGenerator::getPermuteCacheKey(const Row &row)
|
||||
return key;
|
||||
}
|
||||
|
||||
Json::Value StepParityGenerator::SMEditorParityJson()
|
||||
std::uint64_t StepParityGenerator::getStateCacheKey(State * state)
|
||||
{
|
||||
Json::Value root;
|
||||
|
||||
for (unsigned long i = 0; i < nodes_for_rows.size(); i++)
|
||||
std::uint64_t value = 0;
|
||||
const std::uint64_t prime = 31;
|
||||
for(Foot f : state->columns)
|
||||
{
|
||||
StepParityNode *node = graph[nodes_for_rows[i]];
|
||||
root.append(node->state.ToJson(false));
|
||||
value *= prime;
|
||||
value += f;
|
||||
}
|
||||
|
||||
return root;
|
||||
for(Foot f : state->combinedColumns)
|
||||
{
|
||||
value *= prime;
|
||||
value += f;
|
||||
}
|
||||
for(Foot f : state->movedFeet)
|
||||
{
|
||||
value *= prime;
|
||||
value += f;
|
||||
}
|
||||
for(Foot f : state->holdFeet)
|
||||
{
|
||||
value *= prime;
|
||||
value += f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
StepParityNode * StepParityGenerator::addNode(State *state, float second, int rowIndex)
|
||||
{
|
||||
StepParityNode * newNode = new StepParityNode(state, second, rowIndex);
|
||||
newNode->id = int(nodes.size());
|
||||
nodes.push_back(newNode);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
void StepParityGenerator::addEdge(StepParityNode* from, StepParityNode* to, float cost)
|
||||
{
|
||||
from->neighbors[to] = cost;
|
||||
}
|
||||
|
||||
@@ -35,15 +35,42 @@ namespace StepParity {
|
||||
{
|
||||
private:
|
||||
StageLayout layout;
|
||||
std::map < int, std::vector<std::vector<StepParity::Foot>>> permuteCache;
|
||||
|
||||
std::unordered_map < int, std::vector<std::vector<StepParity::Foot>>> permuteCache;
|
||||
std::unordered_map <std::uint64_t, StepParity::State*> stateCache;
|
||||
std::vector<StepParity::StepParityNode*> nodes;
|
||||
StepParity::State * beginningState = nullptr;
|
||||
StepParity::StepParityNode * startNode = nullptr;
|
||||
StepParity::State * endingState = nullptr;
|
||||
StepParity::StepParityNode * endNode = nullptr;
|
||||
|
||||
public:
|
||||
StepParityGraph graph;
|
||||
std::vector<Row> rows;
|
||||
std::vector<int> nodes_for_rows;
|
||||
int columnCount;
|
||||
|
||||
StepParityGenerator(const StageLayout & l) : layout(l) {
|
||||
|
||||
}
|
||||
|
||||
~StepParityGenerator()
|
||||
{
|
||||
for(auto s : stateCache)
|
||||
{
|
||||
delete s.second;
|
||||
}
|
||||
for(auto n: nodes)
|
||||
{
|
||||
delete n;
|
||||
}
|
||||
if(beginningState != nullptr)
|
||||
{
|
||||
delete beginningState;
|
||||
}
|
||||
|
||||
if(endingState != nullptr)
|
||||
{
|
||||
delete endingState;
|
||||
}
|
||||
}
|
||||
/// @brief Analyzes the given NoteData to generate a vector of StepParity::Rows, with each step annotated with
|
||||
/// a foot placement.
|
||||
@@ -59,14 +86,17 @@ namespace StepParity {
|
||||
/// and one that represents the end of the song, after the final note.
|
||||
void buildStateGraph();
|
||||
|
||||
void addStateToGraph(State * resultState, StepParityNode * initialNode, Row & row, std::vector<StepParityNode *> &existingNodesForThisRow, float cost);
|
||||
/// @brief Creates a new State, which is the result of moving from the given initialState
|
||||
/// to the steps of the given row with the given foot placements in columns.
|
||||
/// @param initialState The state of the player prior to the next row
|
||||
/// @param row The next row for the resulting state
|
||||
/// @param columns The foot placement for the resulting state
|
||||
/// @return The resulting state
|
||||
State initResultState(State &initialState, Row &row, const FootPlacement &columns);
|
||||
State * initResultState(State * initialState, Row &row, const FootPlacement &columns);
|
||||
|
||||
void mergeInitialAndResultPosition(State * initialState, State * resultState, int columnCount);
|
||||
|
||||
/// @brief Returns a pointer to a vector of foot possible foot placements for the given row.
|
||||
/// Utilizes the permuteCache to re-use vectors. The returned pointer points to a vector within the permuteCache.
|
||||
/// @param row The row to calculate foot placement permutations for.
|
||||
@@ -97,9 +127,9 @@ namespace StepParity {
|
||||
void AddRow(RowCounter &counter);
|
||||
Row CreateRow(RowCounter &counter);
|
||||
int getPermuteCacheKey(const Row &row);
|
||||
bool bracketCheck(int column1, int column2);
|
||||
float getDistanceSq(StepParity::StagePoint p1, StepParity::StagePoint p2);
|
||||
Json::Value SMEditorParityJson();
|
||||
std::uint64_t getStateCacheKey(State * state);
|
||||
StepParityNode * addNode(State *state, float second, int rowIndex);
|
||||
void addEdge(StepParityNode* from, StepParityNode* to, float cost);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+9
-8
@@ -112,7 +112,7 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
{
|
||||
for (StepParity::Foot foot: StepParity::FEET)
|
||||
{
|
||||
if(currentRow.whereTheFeetAre[foot] == -1 || previousRow.whereTheFeetAre[foot] == -1)
|
||||
if(currentRow.whereTheFeetAre[foot] == StepParity::INVALID_COLUMN || previousRow.whereTheFeetAre[foot] == StepParity::INVALID_COLUMN)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -137,12 +137,12 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
// Check for brackets
|
||||
if(currentRow.noteCount >= 2)
|
||||
{
|
||||
if(currentRow.whereTheFeetAre[StepParity::LEFT_HEEL] != -1 && currentRow.whereTheFeetAre[StepParity::LEFT_TOE] != -1)
|
||||
if(currentRow.whereTheFeetAre[StepParity::LEFT_HEEL] != StepParity::INVALID_COLUMN && currentRow.whereTheFeetAre[StepParity::LEFT_TOE] != StepParity::INVALID_COLUMN)
|
||||
{
|
||||
out[TechCountsCategory_Brackets] += 1;
|
||||
}
|
||||
|
||||
if(currentRow.whereTheFeetAre[StepParity::RIGHT_HEEL] != -1 && currentRow.whereTheFeetAre[StepParity::RIGHT_TOE] != -1)
|
||||
if(currentRow.whereTheFeetAre[StepParity::RIGHT_HEEL] != StepParity::INVALID_COLUMN && currentRow.whereTheFeetAre[StepParity::RIGHT_TOE] != StepParity::INVALID_COLUMN)
|
||||
{
|
||||
out[TechCountsCategory_Brackets] += 1;
|
||||
}
|
||||
@@ -197,7 +197,7 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
// - Was the right foot farther right than the left?
|
||||
// - If so, then this was a full crossover (like RDL, starting on right foot)
|
||||
// - otherwise, then this was probably a half crossover (like UDL, starting on right foot)
|
||||
if(rightHeel != -1 && previousLeftHeel != -1 && previousRightHeel == -1)
|
||||
if(rightHeel != StepParity::INVALID_COLUMN && previousLeftHeel != StepParity::INVALID_COLUMN && previousRightHeel == StepParity::INVALID_COLUMN)
|
||||
{
|
||||
StepParity::StagePoint leftPos = layout.averagePoint(previousLeftHeel, previousLeftToe);
|
||||
StepParity::StagePoint rightPos = layout.averagePoint(rightHeel, rightToe);
|
||||
@@ -209,7 +209,7 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
const StepParity::Row & previousPreviousRow = rows[i - 2];
|
||||
int previousPreviousRightHeel = previousPreviousRow.whereTheFeetAre[StepParity::RIGHT_HEEL];
|
||||
|
||||
if(previousPreviousRightHeel != -1 && previousPreviousRightHeel != rightHeel)
|
||||
if(previousPreviousRightHeel != StepParity::INVALID_COLUMN && previousPreviousRightHeel != rightHeel)
|
||||
{
|
||||
StepParity::StagePoint previousPreviousRightPos = layout.columns[previousPreviousRightHeel];
|
||||
if(previousPreviousRightPos.x > leftPos.x)
|
||||
@@ -231,7 +231,7 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
}
|
||||
}
|
||||
// And check the same thing, starting with left foot
|
||||
else if(leftHeel != -1 && previousRightHeel != -1 && previousLeftHeel == -1)
|
||||
else if(leftHeel != StepParity::INVALID_COLUMN && previousRightHeel != StepParity::INVALID_COLUMN && previousLeftHeel == StepParity::INVALID_COLUMN)
|
||||
{
|
||||
StepParity::StagePoint leftPos = layout.averagePoint(leftHeel, leftToe);
|
||||
StepParity::StagePoint rightPos = layout.averagePoint(previousRightHeel, previousRightToe);
|
||||
@@ -241,9 +241,10 @@ void TechCounts::CalculateTechCountsFromRows(const std::vector<StepParity::Row>
|
||||
if(i > 1)
|
||||
{
|
||||
const StepParity::Row & previousPreviousRow = rows[i - 2];
|
||||
if(previousPreviousRow.whereTheFeetAre[StepParity::LEFT_HEEL] != leftHeel)
|
||||
int previousPreviousLeftHeel = previousPreviousRow.whereTheFeetAre[StepParity::LEFT_HEEL];
|
||||
if(previousPreviousLeftHeel != StepParity::INVALID_COLUMN && previousPreviousLeftHeel != leftHeel)
|
||||
{
|
||||
StepParity::StagePoint previousPreviousLeftPos = layout.columns[previousPreviousRow.whereTheFeetAre[StepParity::LEFT_HEEL]];
|
||||
StepParity::StagePoint previousPreviousLeftPos = layout.columns[previousPreviousLeftHeel];
|
||||
if(rightPos.x > previousPreviousLeftPos.x)
|
||||
{
|
||||
out[TechCountsCategory_FullCrossovers] += 1;
|
||||
|
||||
Reference in New Issue
Block a user