Skip to content

Commit

Permalink
begin new jack stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
MinaciousGrace committed May 11, 2020
1 parent 0c567b2 commit 37edae9
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 59 deletions.
220 changes: 162 additions & 58 deletions src/Etterna/Globals/MinaCalc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,52 +271,181 @@ Hand::InitPoints(const Finger& f1, const Finger& f2)
f2[ki_is_rising].size());
}
#pragma endregion utils

// DON'T WANT TO RECOMPILE HALF THE GAME IF I EDIT THE HEADER FILE
static const float finalscaler =
2.564f * 1.05f * 1.1f * 1.10f * 1.115f; // multiplier to standardize baselines

// ***note*** if we want max control over stamina we need to have one model for
// affecting the other skillsets to a certain degree, enough to push up longer
// stream ratings into contention with shorter ones, and another for both a more
// granular and influential modifier to calculate the end stamina rating with
// so todo on that

// Stamina Model params
static const float stam_ceil = 1.065234f; // stamina multiplier max
static const float stam_mag = 273.f; // multiplier generation scaler
static const float stam_fscale = 500.f; // how fast the floor rises (it's lava)
static const float stam_prop =
0.69424f; // proportion of player difficulty at which stamina tax begins

// since we are no longer using the normalizer system we need to lower
// the base difficulty for each skillset and then detect pattern types
// to push down OR up, rather than just down and normalizing to a differential
// since chorded patterns have lower enps than streams, streams default to 1
// and chordstreams start lower
// stam is a special case and may use normalizers again
static const float basescalers[NUM_Skillset] = { 0.f, 0.97f, 0.89f, 0.88f,
0.94f, 0.9f, 0.84f, 0.88f };

#pragma region CalcBodyFunctions
float
Calc::JackLoss(const vector<float>& j, float x)
{
float o = 0.f;
for (size_t i = 0; i < j.size(); i++)
if (x < j[i])
o += 7.f - (7.f * fastpow(x / (j[i] * 0.88f), 1.7f));
o += 7.f - (17.f * fastpow(x / (j[i] * 1.88f), 1.7f));
CalcClamp(o, 0.f, 10000.f);
return o;
}

inline float
ms_to_bpm(float& x)
{
return 15000.f / x;
}

JackSeq
Calc::SequenceJack(const vector<NoteInfo>& NoteInfo,
unsigned int t,
float music_rate)
float music_rate,
bool debugmode)
{
vector<float> output;
float last = -5.f;
float interval1 = 250.f;
float interval2 = 250.f;
float interval3 = 250.f;
float interval4 = 250.f;
bool dbg = true && debugmode;
// the 4 -> 5 note jack difficulty spike is well known, we aim to reflect
// this phenomena as best as possible. 500, 50, 50, 50, 50 should end up
// significantly more difficult than 50, 50, 50, 50, 50

// NOTE: in comments here "jack" will refer to exclusively the time between
// two taps on the same column, a "sequence" of jacks is n number of jacks
// which contain n + 1 number of total taps, a "component" of a sequence can
// be any n consecutive jacks within a sequence. we are operating under the
// assumption that the difficulty of any jack in any sequence is entirely
// dependent on the previous components of the sequence, and what comes
// afterwards is not relevant (usually true in actual gameplay, outside of
// stuff like mines immediately after shortjacks)
vector<float> jack_diff;
static const int window_size = 5;
vector<float> window_taps = { -1.f, -2.f, -3.f, -4.f, -5.f };
vector<float> comp_diff(window_size - 1);
vector<float> eff_scalers(window_size - 1);
float last_time = -1.f;
unsigned int track = 1u << t;
for (auto r : NoteInfo) {
float last_diff = 0.f;
if (r.notes & track) {
// convert to ms from s, it's much more intuitive
float cur_time = r.rowTime / music_rate * 1000.f;

if (dbg)
std::cout << "time now: " << cur_time / 1000.f << std::endl;
// update most recent values
for (size_t i = 1; i < window_taps.size(); ++i) {
if (false) {
std::cout << "window tap at: " << i - 1
<< " time was: " << window_taps[i - 1]
<< std::endl;
std::cout << "window tap at: " << i
<< " time is: " << window_taps[i] << std::endl;
std::cout << "difference is: "
<< window_taps[i] - window_taps[i - 1]
<< std::endl;
}
window_taps[i - 1] = window_taps[i];
}

// new plan, store cur time so we can easily grab the time the last
// 5 hits take place over, if we need specific ms values, we can
// derive them
window_taps[window_size - 1] = cur_time;
float window_time = window_taps.back() - window_taps.front();
if (dbg)
std::cout << "cur window time: " << window_time / 1000.f << std::endl;

// yes this is many loops, but we don't want to sacrifice
// legitimately difficult minijacks in the name of proper evaluation
// of shortjacks and longjacks, so try scanning through the last 5
// in a minisequence to pick out something something?
for (size_t i = 1; i < window_taps.size(); ++i) {
// first jack is element 1 - 0, 2nd is 2 - 1, 3rd is 3 - 2, and
// 4th is 4 - 3, each with their own total time at n - 0, this
// leaves us with 4 components for jack difficulty estimation
// that will hopefully not underrate minijacks

float base_ms = window_taps[i] - window_taps[i - 1];
float comp_time = window_taps[i] - window_taps[0];

// we sequence inside the jack this way so that we don't ignore
// minijacks, but we don't want them to overpower things like
// they did in 263. We know the general rules that govern how
// hard minijacks, shortjacks, and longjacks are to hit - an
// isolated minijack has an effective window if 180ms + jack ms
// + 180, an isolated triplet has an effective window of 180 +
// jack ms + jack ms + 180, the latter is more difficult but not
// by much, even if the jacks are extremely fast, since jacks
// are effectively down translated from x bpm jacks to y bpm as
// a function of the miss window

// try adding 180 for now, and see how well it works
float eff_ms = comp_time + 90.f;

// compute a simple scaler by taking the effective ms window
// (converted to bpm for the moment for familiarity / clarity)
// remember to multiply effective ms by number of jacks in the
// component

// we do want the base bpm to be the most recent value, not an
// average, since we will aggregate the value in (some) way at
// the end of this
float base_bpm = ms_to_bpm(base_ms);
float eff_bpm = ms_to_bpm(eff_ms) * static_cast<float>(i);
float eff_scaler = eff_bpm / base_bpm;

// ok this is pretty tricky, but basically if we catch a
// minijack in otherwise lenient spacing, we still want to
// evaluate it to at least a jack of some significance, we can
// divide by the effective scaler to get an idea of how the
// difficulty of the sequence at each stage is represented, then
// multiply by the final effective scaler for the sequence as a
// whole after aggregating the component estimates
comp_diff[i - 1] = base_bpm / eff_scaler;
eff_scalers[i - 1] = eff_scaler;
if (dbg) {
std::cout << "\nseq component: " << i << std::endl;
std::cout << "base bpm: " << base_bpm << std::endl;
std::cout << "eff bpm: " << eff_bpm << std::endl;
std::cout << "eff scaler: " << eff_scaler << std::endl;
}
}

for (auto i : NoteInfo) {
if (i.notes & track) {
float current_time = i.rowTime / music_rate;
interval1 = interval2;
interval2 = interval3;
interval3 = interval4;
interval4 = 1000.f * (current_time - last);
last = current_time;
output.emplace_back(min(
2750.f /
min((interval2 + interval3 + interval4) / 3.f,
0.8f * interval4 *
CalcClamp(
1.f + cv(vector<float>{
interval1, interval2, interval3, interval4 }),
1.f,
1.8f)),
45.f));
float comp_mean = mean(comp_diff);
// convert bpm to nps so we can apply finalscaler and get roughly
// comparable values to other skillsets and use the basescaler lever
// to fine tune
float fdiff = comp_mean / 15.f * finalscaler * mean(eff_scalers) * basescalers[Skill_JackSpeed];
last_diff = fdiff;
jack_diff.push_back(fdiff);
last_time = cur_time;
if (dbg) {
std::cout << "comp mean: " << comp_mean << std::endl;
std::cout << "fdiff: " << fdiff << std::endl;
std::cout << "finished this sequence \n" << std::endl;
}
}
}
return output;

return jack_diff;
}

Finger
Expand Down Expand Up @@ -388,32 +517,6 @@ Calc::ProcessFinger(const vector<NoteInfo>& NoteInfo,
return AllIntervals;
}

// DON'T WANT TO RECOMPILE HALF THE GAME IF I EDIT THE HEADER FILE
static const float finalscaler =
2.564f * 1.05f * 1.1f * 1.10f * 1.115f; // multiplier to standardize baselines

// ***note*** if we want max control over stamina we need to have one model for
// affecting the other skillsets to a certain degree, enough to push up longer
// stream ratings into contention with shorter ones, and another for both a more
// granular and influential modifier to calculate the end stamina rating with
// so todo on that

// Stamina Model params
static const float stam_ceil = 1.065234f; // stamina multiplier max
static const float stam_mag = 273.f; // multiplier generation scaler
static const float stam_fscale = 500.f; // how fast the floor rises (it's lava)
static const float stam_prop =
0.69424f; // proportion of player difficulty at which stamina tax begins

// since we are no longer using the normalizer system we need to lower
// the base difficulty for each skillset and then detect pattern types
// to push down OR up, rather than just down and normalizing to a differential
// since chorded patterns have lower enps than streams, streams default to 1
// and chordstreams start lower
// stam is a special case and may use normalizers again
static const float basescalers[NUM_Skillset] = { 0.f, 0.97f, 0.89f, 0.88f,
0.94f, 0.7f, 0.84f, 0.88f };

vector<float>
Calc::CalcMain(const vector<NoteInfo>& NoteInfo,
float music_rate,
Expand Down Expand Up @@ -676,10 +779,10 @@ Calc::InitializeHands(const vector<NoteInfo>& NoteInfo,
}
}

j0 = SequenceJack(NoteInfo, 0, music_rate);
j1 = SequenceJack(NoteInfo, 1, music_rate);
j2 = SequenceJack(NoteInfo, 2, music_rate);
j3 = SequenceJack(NoteInfo, 3, music_rate);
j0 = SequenceJack(NoteInfo, 0, music_rate, debugmode);
j1 = SequenceJack(NoteInfo, 1, music_rate, false);
j2 = SequenceJack(NoteInfo, 2, music_rate, false);
j3 = SequenceJack(NoteInfo, 3, music_rate, false);
return true;
}

Expand Down Expand Up @@ -715,7 +818,8 @@ Hand::InitBaseDiff(Finger& f1, Finger& f2)
difficulty = (5.f * right_difficulty + 2.f * left_difficulty) / 7.f;
soap[BaseNPS][i] = finalscaler * nps;
soap[BaseMS][i] = finalscaler * difficulty;
soap[BaseMSD][i] = finalscaler * (2.33333f * difficulty + 6.66666f * nps) / 9.f;
soap[BaseMSD][i] =
finalscaler * (2.33333f * difficulty + 6.66666f * nps) / 9.f;
}
Smooth(soap[BaseNPS], 0.f);
if (SmoothDifficulty)
Expand Down
2 changes: 1 addition & 1 deletion src/Etterna/Globals/MinaCalc.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Calc
static float JackLoss(const std::vector<float>& j, float x);
static JackSeq SequenceJack(const std::vector<NoteInfo>& NoteInfo,
unsigned int t,
float music_rate);
float music_rate, bool debugmode);

bool debugmode = false;
bool capssr = true; // set to true for scores, false for cache
Expand Down

0 comments on commit 37edae9

Please sign in to comment.