diff --git a/src/Etterna/MinaCalc/Agnostic/MetaRowInfo.h b/src/Etterna/MinaCalc/Agnostic/MetaRowInfo.h index 869dd4ae77..16c58ba76b 100644 --- a/src/Etterna/MinaCalc/Agnostic/MetaRowInfo.h +++ b/src/Etterna/MinaCalc/Agnostic/MetaRowInfo.h @@ -25,6 +25,14 @@ struct metaRowInfo bool gluts_maybe = false; // not really used/tested yet bool twas_jack = false; + Calc& _calc; + + explicit metaRowInfo(Calc& calc) + : _calc(calc) + { + + } + void reset() { time = s_init; @@ -79,7 +87,7 @@ struct metaRowInfo { twas_jack = false; - for (const auto& id : col_ids) { + for (const auto& id : _calc.col_masks) { if (is_jack_at_col(id, notes, last_notes)) { // not scaled to the number of jacks anymore ++mitvi.actual_jacks; diff --git a/src/Etterna/MinaCalc/CMakeLists.txt b/src/Etterna/MinaCalc/CMakeLists.txt index a78e4ca4a8..ecb4f1ea39 100644 --- a/src/Etterna/MinaCalc/CMakeLists.txt +++ b/src/Etterna/MinaCalc/CMakeLists.txt @@ -10,6 +10,8 @@ list(APPEND GENERAL_CALC_SRC "SequencedBaseDiffCalc.h" "SequencingHelpers.h" "Ulbu.h" - "UlbuAcolytes.h") + "UlbuAcolytes.h" + "UlbuBase.h" + "UlbuSevenKey.h") target_sources(Etterna PUBLIC ${GENERAL_CALC_SRC}) \ No newline at end of file diff --git a/src/Etterna/MinaCalc/Dependent/MetaHandInfo.h b/src/Etterna/MinaCalc/Dependent/MetaHandInfo.h index 01c8dd2809..7b116166f2 100644 --- a/src/Etterna/MinaCalc/Dependent/MetaHandInfo.h +++ b/src/Etterna/MinaCalc/Dependent/MetaHandInfo.h @@ -43,6 +43,14 @@ struct metaHandInfo int offhand_taps = 0; int offhand_ohjumps = 0; + Calc& _calc; + + explicit metaHandInfo(Calc& calc) + : _calc(calc) + { + + } + /// we need to reset everything between hands or the trailing values from the /// end of one will carry over into the start of the other, not a huge /// practical deal but it could theoretically be abused and it's good diff --git a/src/Etterna/MinaCalc/MinaCalc.cpp b/src/Etterna/MinaCalc/MinaCalc.cpp index 24d40d2c96..1e8310a661 100644 --- a/src/Etterna/MinaCalc/MinaCalc.cpp +++ b/src/Etterna/MinaCalc/MinaCalc.cpp @@ -1,5 +1,6 @@ #include "MinaCalc.h" #include "Ulbu.h" +#include "UlbuSevenKey.h" #include "MinaCalcHelpers.h" #include @@ -475,21 +476,48 @@ Calc::InitializeHands(const std::vector& NoteInfo, // ulbu calculates everything needed for the block below // (mostly patternmods) - thread_local TheGreatBazoinkazoinkInTheSky ulbu_that_which_consumes_all( - *this); + thread_local std::unordered_map> ulbu_collective{}; + + bool keycount_defined = false; + if (!ulbu_collective.contains(keycount)) { + switch (keycount) { + case 4u: + ulbu_collective.emplace( + keycount, + std::make_unique(*this)); + keycount_defined = true; + break; + case 7u: + ulbu_collective.emplace( + keycount, + std::make_unique(*this)); + keycount_defined = true; + break; + default: + if (!ulbu_collective.contains(0u)) { + ulbu_collective.emplace( + 0u, std::make_unique(*this)); + } + break; + } + } else { + keycount_defined = true; + } + auto t_keycount = keycount_defined ? keycount : 0u; + auto& all_consuming_ulbu = ulbu_collective.at(t_keycount); // if debug, force params to load if (debugmode || loadparams) - ulbu_that_which_consumes_all.load_calc_params_from_disk(true); + all_consuming_ulbu->load_calc_params_from_disk(true); // reset ulbu patternmod structs // run agnostic patternmod/sequence loop // run dependent patternmod/sequence loop - ulbu_that_which_consumes_all(); + (*all_consuming_ulbu)(); // loop over hands to set adjusted difficulties using the patternmods for (const auto& hand : both_hands) { - InitAdjDiff(*this, hand); + InitAdjDiff(*this, all_consuming_ulbu, hand); // post pattern mod smoothing for cj // (Chordjack related tuning done: this is disabled for now) @@ -720,107 +748,9 @@ Calc::Chisel(const float player_skill, * misclassing hard and polluting leaderboards, and good scores on overrated * files will simply produce high ratings in every category */ inline void -Calc::InitAdjDiff(Calc& calc, const int& hand) +Calc::InitAdjDiff(Calc& calc, std::unique_ptr& all_consuming_ulbu, const int& hand) { - static const std::array, NUM_Skillset> pmods_used = { { - // overall, nothing, don't handle here - {}, - - // stream - { - Stream, - OHTrill, - VOHTrill, - Roll, - Chaos, - WideRangeRoll, - WideRangeJumptrill, - WideRangeJJ, - FlamJam, - // OHJumpMod, - // Balance, - // RanMan, - // WideRangeBalance, - }, - - // js - { - JS, - // OHJumpMod, - // Chaos, - // Balance, - // TheThing, - // TheThing2, - WideRangeBalance, - WideRangeJumptrill, - WideRangeJJ, - // WideRangeRoll, - // OHTrill, - VOHTrill, - // Roll, - RollJS, - // RanMan, - FlamJam, - // WideRangeAnchor, - }, - - // hs - { - HS, - OHJumpMod, - TheThing, - // WideRangeAnchor, - WideRangeRoll, - WideRangeJumptrill, - WideRangeJJ, - OHTrill, - VOHTrill, - // Roll, - // RanMan, - FlamJam, - HSDensity, - }, - - // stam, nothing, don't handle here - {}, - - // jackspeed, doesn't use pmods (atm) - {}, - - // chordjack - { - CJ, - // CJDensity, - CJOHJump, - CJOHAnchor, - VOHTrill, - // WideRangeAnchor, - FlamJam, // you may say, why? why not? - // WideRangeJJ, - WideRangeJumptrill, - }, - - // tech, duNNO wat im DOIN - { - OHTrill, - VOHTrill, - Balance, - Roll, - // OHJumpMod, - Chaos, - WideRangeJumptrill, - WideRangeJJ, - WideRangeBalance, - WideRangeRoll, - FlamJam, - // RanMan, - Minijack, - // WideRangeAnchor, - TheThing, - TheThing2, - }, - } }; - + const auto& pmods_used = all_consuming_ulbu->get_pmods(); std::array pmod_product_cur_interval = {}; // ok this loop is pretty wack i know, for each interval @@ -1016,7 +946,7 @@ MinaSDCalcDebug( } } -int mina_calc_version = 505; +int mina_calc_version = 506; auto GetCalcVersion() -> int { diff --git a/src/Etterna/MinaCalc/MinaCalc.h b/src/Etterna/MinaCalc/MinaCalc.h index 94742d1272..ba86369ab1 100644 --- a/src/Etterna/MinaCalc/MinaCalc.h +++ b/src/Etterna/MinaCalc/MinaCalc.h @@ -3,6 +3,7 @@ #include #include #include +#include // For internal, must be preprocessor defined #if defined(MINADLL_COMPILE) && defined(_WIN32) @@ -18,6 +19,7 @@ using MinaSD = std::vector>; class Calc; +struct Bazoinkazoink; /** This defines the base size for each interval-based vector in MinaCalc. * Each interval is one half second. If any situation arises in which the @@ -97,6 +99,11 @@ class Calc /// Set true to force calc params to load outside debug mode. bool loadparams = false; + /// Assigns the keymode specific logic + unsigned keycount = 4; + std::array hand_col_masks = { 0U, 0U }; + std::vector col_masks{}; + private: /** Splits up the chart by each hand and processes them individually to * produce hand specific base difficulty values, which are then passed to @@ -127,7 +134,9 @@ class Calc * Iterates over each interval, and every skillset for each interval. * Skips iterations of Overall and Stamina (unaffected by patternmods). */ - static inline void InitAdjDiff(Calc& calc, const int& hand); + static inline void InitAdjDiff(Calc& calc, + std::unique_ptr& all_consuming_ulbu, + const int& hand); public: /** Each Calc instance created sets up the interval related vectors. diff --git a/src/Etterna/MinaCalc/SequencedBaseDiffCalc.h b/src/Etterna/MinaCalc/SequencedBaseDiffCalc.h index 5c21ecd1a7..afe2bef035 100644 --- a/src/Etterna/MinaCalc/SequencedBaseDiffCalc.h +++ b/src/Etterna/MinaCalc/SequencedBaseDiffCalc.h @@ -95,7 +95,7 @@ struct nps notes += cur.hand_counts.at(hand); const auto& crt = cur.row_time; - switch (determine_col_type(cur.row_notes, hand_col_ids[hand])) { + switch (determine_col_type(cur.row_notes, calc.hand_col_masks[hand])) { case col_left: if (last_left_row_time != s_init) { const auto left_ms = diff --git a/src/Etterna/MinaCalc/SequencingHelpers.h b/src/Etterna/MinaCalc/SequencingHelpers.h index 496c4c37de..4738a25cf4 100644 --- a/src/Etterna/MinaCalc/SequencingHelpers.h +++ b/src/Etterna/MinaCalc/SequencingHelpers.h @@ -4,7 +4,6 @@ /* generic sequencing functions and defs to help either agnostic or dependent * sequencers do their stuff */ -static const std::array col_ids = { 1U, 2U, 4U, 8U }; /// default for any field tracking seconds constexpr float s_init = -5.F; @@ -14,26 +13,37 @@ constexpr float ms_init = 5000.F; /// global multiplier to standardize baselines constexpr float finalscaler = 3.632F * 1.06F; +// outputs 0b1111 for 4, 0b111 for 3, etc +inline auto +keycount_to_bin(const unsigned& keycount) -> unsigned +{ + if (keycount < 2) + return 0b11; + return ~(~1 << (keycount - 1)); +} + +// outputs 0b1100 for 4, 0b110 for 3, etc +inline auto +right_mask(const unsigned& keycount) -> unsigned +{ + if (keycount <= 2) + return 0b10; + return keycount_to_bin(keycount) >> (keycount / 2) << (keycount / 2); +} + +// outputs 0b0011 for 4, 0b001 for 3, etc +inline auto +left_mask(const unsigned& keycount) -> unsigned +{ + const auto m = right_mask(keycount); + return ~m & static_cast(std::exp2(std::ceil(std::log2(m))) - 1); +} + +// count number of 1's in noterow binary inline auto column_count(const unsigned& notes) -> int { - // singles - if (notes == 1U || notes == 2U || notes == 4U || notes == 8U) { - return 1; - } - - // hands - if (notes == 7U || notes == 11U || notes == 13U || notes == 14U) { - return 3; - } - - // quad - if (notes == 15U) { - return 4; - } - - // everything else is a jump - return 2; + return std::popcount(notes); } /// milliseconds between two given timestamps in seconds diff --git a/src/Etterna/MinaCalc/Ulbu.h b/src/Etterna/MinaCalc/Ulbu.h index 32a29c6a89..2491f986dc 100644 --- a/src/Etterna/MinaCalc/Ulbu.h +++ b/src/Etterna/MinaCalc/Ulbu.h @@ -44,6 +44,7 @@ // they're useful sometimes #include "UlbuAcolytes.h" +#include "UlbuBase.h" // a new thing #include "SequencedBaseDiffCalc.h" @@ -52,11 +53,10 @@ /** I am ulbu, the great bazoinkazoink in the sky, and ulbu does everything, for * ulbu is all. Praise ulbu. */ -struct TheGreatBazoinkazoinkInTheSky +struct TheGreatBazoinkazoinkInTheSky : public Bazoinkazoink { bool dbg = false; - Calc& _calc; int hand = 0; // to generate these @@ -118,16 +118,122 @@ struct TheGreatBazoinkazoinkInTheSky diffz _diffz; explicit TheGreatBazoinkazoinkInTheSky(Calc& calc) - : _calc(calc) + : Bazoinkazoink(calc) { // setup our data pointers - _last_mri = std::make_unique(); - _mri = std::make_unique(); - _last_mhi = std::make_unique(); - _mhi = std::make_unique(); + _last_mri = std::make_unique(calc); + _mri = std::make_unique(calc); + _last_mhi = std::make_unique(calc); + _mhi = std::make_unique(calc); + } + + private: + const std::array, NUM_Skillset> pmods = { { + // overall, nothing, don't handle here + {}, + + // stream + { + Stream, + OHTrill, + VOHTrill, + Roll, + Chaos, + WideRangeRoll, + WideRangeJumptrill, + WideRangeJJ, + FlamJam, + // OHJumpMod, + // Balance, + // RanMan, + // WideRangeBalance, + }, + + // js + { + JS, + // OHJumpMod, + // Chaos, + // Balance, + // TheThing, + // TheThing2, + WideRangeBalance, + WideRangeJumptrill, + WideRangeJJ, + // WideRangeRoll, + // OHTrill, + VOHTrill, + // Roll, + RollJS, + // RanMan, + FlamJam, + // WideRangeAnchor, + }, + + // hs + { + HS, + OHJumpMod, + TheThing, + // WideRangeAnchor, + WideRangeRoll, + WideRangeJumptrill, + WideRangeJJ, + OHTrill, + VOHTrill, + // Roll, + // RanMan, + FlamJam, + HSDensity, + }, + + // stam, nothing, don't handle here + {}, + + // jackspeed, doesn't use pmods (atm) + {}, + + // chordjack + { + CJ, + // CJDensity, + CJOHJump, + CJOHAnchor, + VOHTrill, + // WideRangeAnchor, + FlamJam, // you may say, why? why not? + // WideRangeJJ, + WideRangeJumptrill, + }, + + // tech, duNNO wat im DOIN + { + OHTrill, + VOHTrill, + Balance, + Roll, + // OHJumpMod, + Chaos, + WideRangeJumptrill, + WideRangeJJ, + WideRangeBalance, + WideRangeRoll, + FlamJam, + // RanMan, + Minijack, + // WideRangeAnchor, + TheThing, + TheThing2, + }, + } }; + + public: + const std::array, NUM_Skillset> get_pmods() const override + { + return pmods; } - void operator()() + void operator()() override { hand = 0; @@ -429,7 +535,7 @@ struct TheGreatBazoinkazoinkInTheSky { setup_dependent_mods(); - for (const auto& ids : hand_col_ids) { + for (const auto& ids : _calc.hand_col_masks) { auto row_time = s_init; auto last_row_time = s_init; auto any_ms = ms_init; @@ -544,40 +650,7 @@ struct TheGreatBazoinkazoinkInTheSky } #pragma endregion - [[nodiscard]] static auto make_mod_param_node( - const std::vector>& param_map, - const std::string& name) -> XNode* - { - auto* pmod = new XNode(name); - for (const auto& p : param_map) { - pmod->AppendChild(p.first, std::to_string(*p.second)); - } - - return pmod; - } - - static void load_params_for_mod( - const XNode* node, - const std::vector>& param_map, - const std::string& name) - { - auto boat = 0.F; - const auto* pmod = node->GetChild(name); - if (pmod == nullptr) { - return; - } - for (const auto& p : param_map) { - const auto* ch = pmod->GetChild(p.first); - if (ch == nullptr) { - continue; - } - - ch->GetTextValue(boat); - *p.second = boat; - } - } - - void load_calc_params_from_disk(bool bForce = false) const + void load_calc_params_from_disk(bool bForce = false) const override { const auto fn = calc_params_xml; int iError; @@ -694,7 +767,7 @@ struct TheGreatBazoinkazoinkInTheSky } #pragma endregion - void write_params_to_disk() const + void write_params_to_disk() const override { const auto fn = calc_params_xml; const std::unique_ptr xml(make_param_node()); diff --git a/src/Etterna/MinaCalc/UlbuAcolytes.h b/src/Etterna/MinaCalc/UlbuAcolytes.h index 7a885351e5..f169dd255a 100644 --- a/src/Etterna/MinaCalc/UlbuAcolytes.h +++ b/src/Etterna/MinaCalc/UlbuAcolytes.h @@ -14,7 +14,6 @@ static const std::array basescalers = { }; static const std::string calc_params_xml = "Save/calc params.xml"; -static const std::array hand_col_ids = { 3, 12 }; constexpr float interval_span = 0.5F; /// smoothing function to reduce spikes and holes in a given vector @@ -173,6 +172,19 @@ fast_walk_and_check_for_skip(const std::vector& ni, } } + // set up extra keycount information + const auto max_keycount_notes = keycount_to_bin(calc.keycount); + const auto left_hand_mask = left_mask(calc.keycount); + const auto right_hand_mask = right_mask(calc.keycount); + // left, right + calc.hand_col_masks = { left_hand_mask, right_hand_mask }; + // all columns from left to rightmost + calc.col_masks.clear(); + calc.col_masks.reserve(calc.keycount); + for (int i = 0; i < calc.keycount; i++) { + calc.col_masks.push_back(1 << i); + } + /* now we can attempt to construct notinfo that includes column count and * rate adjusted row time, both of which are derived data that both pmod * loops require */ @@ -190,7 +202,7 @@ fast_walk_and_check_for_skip(const std::vector& ni, const auto& ri = i; // either not a 4k file or malformed - if (ri.notes < 0 || ri.notes > 0b1111) { + if (ri.notes < 0 || ri.notes > max_keycount_notes) { return true; } @@ -228,26 +240,14 @@ fast_walk_and_check_for_skip(const std::vector& ni, nri.row_count = column_count(ri.notes); nri.row_time = scaled_time; - auto left = 0; - auto right = 0; - - if ((ri.notes & 1U) != 0U) { - ++left; - } - if ((ri.notes & 2U) != 0U) { - ++left; - } - if ((ri.notes & 4U) != 0U) { - ++right; - } - if ((ri.notes & 8U) != 0U) { - ++right; - } - - assert(left + right == nri.row_count); + // how many columns have a note on them per hand + nri.hand_counts[left_hand] = std::popcount(ri.notes & left_hand_mask); + nri.hand_counts[right_hand] = std::popcount(ri.notes & right_hand_mask); - nri.hand_counts[left_hand] = left; - nri.hand_counts[right_hand] = right; + // make sure row_count adds up... + // this validates that the mask is correct + assert(nri.hand_counts[left_hand] + nri.hand_counts[right_hand] == + nri.row_count); ++row_counter; } diff --git a/src/Etterna/MinaCalc/UlbuBase.h b/src/Etterna/MinaCalc/UlbuBase.h new file mode 100644 index 0000000000..3e6b2aa021 --- /dev/null +++ b/src/Etterna/MinaCalc/UlbuBase.h @@ -0,0 +1,106 @@ +#pragma once + +/// base behavior for pmod stuff +/// defines fallback for undefined keymodes +struct Bazoinkazoink +{ + public: + Calc& _calc; + + explicit Bazoinkazoink(Calc& calc) + : _calc(calc) + { + + } + + private: + const std::array, NUM_Skillset> pmods = { { + // Overall + {}, + + // Stream + { + + }, + + // Jumpstream + { + + }, + + // Handstream + { + + }, + + // Stamina + {}, + + // Jackspeed + {}, + + // Chordjack + { + + }, + + // Technical + { + + }, + }}; + + public: + virtual const std::array, NUM_Skillset> get_pmods() const + { + return pmods; + } + + /// main driver for operations + virtual void operator()() { + + } + + /// load custom xml parameters + virtual void load_calc_params_from_disk(bool bForce = false) const { + + } + + /// save default xml parameters + virtual void write_params_to_disk() const { + + } + + static auto make_mod_param_node( + const std::vector>& param_map, + const std::string& name) -> XNode* + { + auto* pmod = new XNode(name); + for (const auto& p : param_map) { + pmod->AppendChild(p.first, std::to_string(*p.second)); + } + + return pmod; + } + + static void load_params_for_mod( + const XNode* node, + const std::vector>& param_map, + const std::string& name) + { + auto boat = 0.F; + const auto* pmod = node->GetChild(name); + if (pmod == nullptr) { + return; + } + for (const auto& p : param_map) { + const auto* ch = pmod->GetChild(p.first); + if (ch == nullptr) { + continue; + } + + ch->GetTextValue(boat); + *p.second = boat; + } + } +}; diff --git a/src/Etterna/MinaCalc/UlbuSevenKey.h b/src/Etterna/MinaCalc/UlbuSevenKey.h new file mode 100644 index 0000000000..38c8080738 --- /dev/null +++ b/src/Etterna/MinaCalc/UlbuSevenKey.h @@ -0,0 +1,68 @@ +#pragma once + +#include "UlbuBase.h" + +struct TheSevenFootedBazoinkazoink : public Bazoinkazoink +{ + explicit TheSevenFootedBazoinkazoink(Calc& calc) + : Bazoinkazoink(calc) + { + + } + + private: + const std::array, NUM_Skillset> pmods = { { + // Overall + {}, + + // Stream + { + + }, + + // Jumpstream + { + + }, + + // Handstream + { + + }, + + // Stamina + {}, + + // Jackspeed + {}, + + // Chordjack + { + + }, + + // Technical + { + + }, + } }; + + public: + const std::array, NUM_Skillset> get_pmods() const override + { + return pmods; + } + + void operator()() override { + + } + + void load_calc_params_from_disk(bool bForce = false) const override { + + } + + void write_params_to_disk() const override { + + } + +};