Etterna 0.74.4
Loading...
Searching...
No Matches
RMSequencing.h
1#pragma once
2#include "../HD_MetaSequencing.h"
3#include "../../UlbuAcolytes.h"
4
5// this is 1.06 * (4k tech base scaler)
6static const float rma_diff_scaler = 1.06F * 1.06F;
7
8enum rm_behavior
9{
10 rmb_off_tap_oh,
11 rmb_off_tap_sh,
12 rmb_anchor,
13 rmb_jack, // only for the anchor col, not any col
14 rmb_init,
15};
16
17enum rm_status
18{
19 rm_inactive,
20 rm_running,
21};
22
27{
29 int ran_taps = 0;
30
32 int _len = 0;
33
35 int off_taps = 0;
36 int off_len = 0;
37
39 int off_taps_sh = 0;
40
42 int oht_taps = 0;
44 int oht_len = 0;
45
47 int ot_sh_len = 0;
48
50 int jack_taps = 0;
51 int jack_len = 0;
52
54 int anch_len = 0;
55
56 void full_reset()
57 {
58 // don't touch anchor col
59 _len = 0;
60
61 off_taps_sh = 0;
62
63 off_taps = 0;
64 off_len = 0;
65
66 oht_taps = 0;
67 oht_len = 0;
68
69 jack_taps = 0;
70 jack_len = 0;
71
72 anch_len = 0;
73 }
74
75 void add_off_tap_sh()
76 {
78 ++ot_sh_len;
79 add_off_tap();
80 }
81
82 void add_off_tap()
83 {
84 ++off_len;
85 ++off_taps;
86 ++ran_taps;
87 }
88
89 void add_oht_tap()
90 {
91 ++oht_len;
92 ++oht_taps;
93 }
94
95 void add_anchor_tap()
96 {
97 ++_len;
98 ++anch_len;
99 ++ran_taps;
100 }
101
102 void add_jack_tap()
103 {
104 ++jack_len;
105 ++jack_taps;
106 ++ran_taps;
107 }
108
109 void end_jack_and_anch_runs()
110 {
111 end_anch_run();
112 end_jack_run();
113 }
114
115 void end_anch_run() { anch_len = 0; }
116 void end_jack_run() { jack_len = 0; }
117
118 void end_off_tap_run()
119 {
120 off_len = 0;
121 ot_sh_len = 0;
122 }
123
124 void restart()
125 {
126 /* we will probably be resetting much more than we are restarting, so to
127 * reduce computational expense, only set any values back to 0 while
128 * sequencing after a restart, and remove the old restart function and
129 * replace it with an inactive status. this is the old reset block,
130 * minus _len, ran_taps, and time, those are set above because they are
131 * already known */
132
133 off_taps_sh = 0;
134 off_taps = 0;
135 off_len = 0;
136
137 oht_taps = 0;
138 oht_len = 0;
139
140 jack_taps = 0;
141 jack_len = 0;
142
143 anch_len = 0;
144 }
145
147 [[nodiscard]] auto get_off_tap_prop() const -> float
148 {
149 if (off_taps == 0)
150 return 0.F;
151
152 return (static_cast<float>(_len) / (static_cast<float>(off_taps)));
153 }
154
156 [[nodiscard]] auto get_offhand_tap_prop() const -> float
157 {
158 if (off_taps - off_taps_sh <= 0)
159 return 0.F;
160
161 return ((static_cast<float>(off_taps - off_taps_sh)) /
162 static_cast<float>(_len));
163 }
164
166 [[nodiscard]] auto get_off_tap_same_prop() const -> float
167 {
168 if (off_taps_sh == 0)
169 return 0.F;
170
171 return ((static_cast<float>(off_taps_sh)) / static_cast<float>(_len));
172 }
173};
174
178{
179 // params.. loaded by runningman and then set from there
180 int max_oht_len = 0;
181 int max_off_len = 0;
182 int max_ot_sh_len = 0;
183 int max_burst_len = 0;
184 int max_jack_len = 0;
185
186 // end with same hand off anchor taps, this is so 2h trills don't get
187 // flagged as runningmen
188 int max_anchor_len = 0;
189
190 void set_params(const float& moht,
191 const float& moff,
192 const float& motsh,
193 const float& mburst,
194 const float& mjack,
195 const float& manch)
196 {
197 max_oht_len = static_cast<int>(moht);
198 max_off_len = static_cast<int>(moff);
199 max_ot_sh_len = static_cast<int>(motsh);
200 max_burst_len = static_cast<int>(mburst);
201 max_jack_len = static_cast<int>(mjack);
202 max_anchor_len = static_cast<int>(manch);
203 }
204
205 col_type _ct = col_init;
206 rm_status _status = rm_inactive;
207 rm_behavior _rmb = rmb_init;
208 rm_behavior _last_rmb = rmb_init;
209
210 RunningMan _rm;
211
212 // try to allow 1 burst?
213 bool is_bursting = false;
214 bool had_burst = false;
215
216 float last_anchor_time = s_init;
217 float _start = s_init;
218
219#pragma region functions
220
221 void full_reset()
222 {
223 // don't touch anchor col
224
225 _status = rm_inactive;
226 _rmb = rmb_init;
227 _last_rmb = rmb_init;
228
229 _start = s_init;
230 last_anchor_time = s_init;
231
232 is_bursting = false;
233 had_burst = false;
234
235 _rm.full_reset();
236 }
237
238 /* restart only if we have just reset and there is a valid last rm_behavior
239 * to start from. this is so we don't restart a runningman sequence
240 * preceeded by pure jacks, (though there is some question about allowing
241 * for only a single jack to start a new sequence, and how to handle doing
242 * so). since restarting means we already have an anchor length of 2 (see
243 * anchor sequencer) we can check for offtaps same hand, or offhand taps as
244 * the last behavior to allow restarting. for the moment only
245 * offtaps_samehand will be allowed to restart */
246 void restart(const Anchor_Sequencing& as)
247 {
248 assert(_last_rmb == rmb_off_tap_sh);
249
250 /* we are restarting the runningman sequence because the anchor sequence
251 * has reset and our last update was a same hand off tap, or because we
252 * were inactive, had a same hand off tap last update, and are now on
253 * the anchor col. as._last is the last seen col of the anchor, since we
254 * can only restart when we're on an anchor col, the anchor col should
255 * have already been updated and _last would be now, so calculate the
256 * start time from as._sc_ms */
257 _start = as._last - (as._sc_ms / 1000.F);
258
259 // should always be equivalent to NOW,
260 last_anchor_time = as._last;
261
262 assert(_start < last_anchor_time);
263 _rm._len = 2;
264
265 // technically if we are restarting we know we have 3 taps, but let
266 // last_rmb handling take care of the third for clarity
267 _rm.ran_taps = 2;
268
269 /* we will probably be resetting much more than we are restarting, so to
270 * reduce computational expense, only set any values back to 0 while
271 * sequencing after a restart, and remove the old restart function and
272 * replace it with an inactive status. this is the old reset block,
273 * minus _len, ran_taps, and time, those are set above because they are
274 * already known */
275
276 {
277 is_bursting = false;
278 had_burst = false;
279 _rm.restart();
280 }
281
282 // retroactively handle whatever behavior allowed the restart
283 handle_last_rmb();
284 }
285
286 auto should_restart() const -> bool { return _last_rmb == rmb_off_tap_sh; }
287
288 void end_off_tap_run()
289 {
290 // allow only 1 burst
291 if (is_bursting) {
292 is_bursting = false;
293 had_burst = true;
294 }
295 // reset off_len counter
296 _rm.end_off_tap_run();
297 }
298
299 // optimization for restarting that skips max len checks
300 void handle_last_rmb()
301 {
302 // only viable start/restart mechanisms for now
303 switch (_last_rmb) {
304 // ok this is jank af but if rmb_anchor is _last_rmb when restarting
305 // it means we've started up again from the initial start logic, and
306 // so we are _on_ rmb_off_tap_sh, so let this fall through
307 case rmb_off_tap_sh:
308 _rm.add_off_tap_sh();
309 break;
310 default:
311 assert(0);
312 break;
313 }
314 }
315
316 auto off_len_exceeds_max() -> bool
317 {
318 // haven't exceeded anything
319 if (_rm.off_len <= max_off_len) {
320 return false;
321 }
322
323 // already had a burst and exceeding normal limit or exceeded the burst
324 // limit
325 if (had_burst || _rm.off_len > max_burst_len) {
326 return true;
327 }
328
329 // have exceeded the normal limit but not had a burst yet, set
330 // bursting to true and return false
331 is_bursting = true;
332 return false;
333 }
334
335 auto ot_sh_len_exceeds_max() const -> bool
336 {
337 return _rm.ot_sh_len > max_ot_sh_len;
338 }
339
340 auto jack_len_exceeds_max() const -> bool
341 {
342 return _rm.jack_len > max_jack_len;
343 }
344
345 auto anch_len_exceeds_max() const -> bool
346 {
347 return _rm.anch_len > max_anchor_len;
348 }
349
350 auto oht_len_exceeds_max() const -> bool
351 {
352 return _rm.oht_len > max_oht_len;
353 }
354
355 // executed if incoming ct == _ct
356 void handle_anchor_behavior(const Anchor_Sequencing& as)
357 {
358 // handle anchor logic here
359
360 // too long since we saw an off anchor same hand tap.. it's probably a
361 // trill or something
362 if (anch_len_exceeds_max()) {
363 _status = rm_inactive;
364 return;
365 }
366
367 switch (as._status) {
368 case reset_too_slow:
369 case reset_too_fast:
370
371 // anchor has changed speeds to a significant degree,
372 // restart if we are able to, otherwise flag the runningman
373 // as inactive
374 if (should_restart()) {
375 restart(as);
376 } else {
377 _status = rm_inactive;
378 }
379 break;
380 case anchoring:
381 _rm.add_anchor_tap();
382 _rm.end_off_tap_run();
383 case anch_init:
384 // do nothing
385 break;
386 }
387 }
388
389 void handle_off_tap_sh_behavior()
390 {
391 // add before running length checks
392 _rm.add_off_tap_sh();
393 if (off_len_exceeds_max() || ot_sh_len_exceeds_max()) {
394 // don't reset anything, just flag as inactive
395 _status = rm_inactive;
396 } else {
397 // we have an offanchor tap on the same hand, end any jack or
398 // consecutive anchor sequences
399 _rm.end_jack_and_anch_runs();
400 }
401 }
402
403 void handle_off_tap_oh_behavior()
404 {
405 _rm.add_off_tap();
406 if (off_len_exceeds_max()) {
407 _status = rm_inactive;
408 } else {
409 // we have an offanchor tap on the other hand, end any jack run, but
410 // not an anchor run, those should only be broken by same hand taps
411 _rm.end_jack_run();
412 }
413 }
414
415 void handle_jack_behavior()
416 {
417 _rm.add_jack_tap();
418 if (jack_len_exceeds_max()) {
419 _status = rm_inactive;
420 } else {
421 end_off_tap_run();
422 }
423 }
424
425 /* oht's are a subtype of off_tap_sh, and the behavior will just fall
426 * through to the latter's, so don't do anything outside of oht values
427 * or reset anything, it'll be redundant at best and bug prone at worst
428 */
429 void handle_oht_behavior(const col_type& ct)
430 {
431 /* to be explicit about the goal here, given 111212111 any reasonable
432 * player would conclude there was a 4 note oht in the middle of a
433 * runningman, and while rare (because it's hard as shit) it's
434 * acceptable, the same thing applies to a 6 note oht inside a
435 * runningman, though those are even rarer, however what we care about
436 * is the threshold at which this can be jumpjacked, which is about 8
437 * oht taps total, dependent on speed (this is already pretty generous
438 * and i don't want to handle diffrential speeds here). now since a 7
439 * note ohtrill in the context of a runningman is meaningless (think
440 * about it) we only really care about the number of consecutive
441 * off_taps_sh */
442
443 if (ct != _ct) {
444 /* metatype won't be set until it finds 1212, but we want to
445 * explicitly track the number of off_anchor taps in the oht, so
446 * boost by 1 when we see meta_oht and oht_len == 0 */
447 if (_rm.oht_len == 0) {
448 _rm.add_oht_tap();
449 }
450
451 _rm.add_oht_tap();
452 if (oht_len_exceeds_max()) {
453 _status = rm_inactive;
454 }
455 }
456 }
457
458 void handle_rmb(const Anchor_Sequencing& as)
459 {
460 assert(_status == rm_running);
461 switch (_rmb) {
462 case rmb_off_tap_oh:
463 // should only ever be called from advance_off_hand_sequencing
464 assert(0);
465 break;
466 case rmb_off_tap_sh:
467 handle_off_tap_sh_behavior();
468 break;
469 case rmb_anchor:
470 handle_anchor_behavior(as);
471 break;
472 case rmb_jack:
473 handle_jack_behavior();
474 break;
475 default:
476 break;
477 }
478 }
479
480 /* rm_sequencing is the only hand dependent mod atm that actually cares
481 * about basic off_hand information, so this should be called to update
482 * using that information before the ct == col_empty continue block in ulbu.
483 */
484 void advance_off_hand_sequencing()
485 {
486 handle_off_tap_oh_behavior();
487 _last_rmb = rmb_off_tap_oh;
488 }
489
490 void operator()(const col_type& ct,
491 const base_type& bt,
492 const meta_type& mt,
493 const Anchor_Sequencing& as)
494 {
495 /* cosmic brain handling of ohts, this won't interfere with the
496 * determinations in the behavior block, but it can set rm_inactive (as
497 * it should be able to) */
498 if (mt == meta_cccccc) {
499 handle_oht_behavior(ct);
500 }
501
502 /* update our last anchor time , we don't handle this in the anchoring
503 * block because technically jacks can either be on the anchor column or
504 * not, and i don't want to have to split logic again between anchor
505 * column jacks and off anchor jacks on the same hand */
506 last_anchor_time = as._last;
507
508 // if anchor_sequencing passes forward s_init, reset everything
509 // this means the rm for this finger should definitely be dead
510 // (nuclear bandaid, probably just want to set status to inactive)
511 if (last_anchor_time == s_init) {
512 full_reset();
513 return;
514 }
515
516 // determine what we should do
517 switch (bt) {
518 case base_left_right:
519 case base_right_left:
520 case base_single_single:
521 if (_ct == ct) {
522 // this is an anchor
523 _rmb = rmb_anchor;
524 } else {
525 // this is a same hand off anchor tap
526 _rmb = rmb_off_tap_sh;
527 }
528 break;
529 case base_jump_single:
530 if (_last_rmb == rmb_off_tap_oh) {
531 // if we have a jump -> single, and the last note was an
532 // offhand tap, and the single is the anchor col, then
533 // we have an anchor
534 if (_ct == ct) {
535 _rmb = rmb_anchor;
536 } else {
537 // this is a same hand off anchor tap
538 _rmb = rmb_off_tap_sh;
539 }
540 } else {
541 // if we are jump -> single and the last note was _not_
542 // an offhand hand tap, we have a jack
543 _rmb = rmb_jack;
544 }
545 break;
546 case base_single_jump:
547 case base_jump_jump:
548 // if last note was an offhand tap, this is by
549 // definition part of the anchor
550 if (_last_rmb == rmb_off_tap_oh) {
551 _rmb = rmb_anchor;
552 } else {
553 // if not, a jack
554 _rmb = rmb_jack;
555 }
556 break;
557 case base_type_init:
558 // bail and don't set anything
559 return;
560 default:
561 assert(0);
562 break;
563 }
564
565 /* only allow same hand off taps after an anchor to begin a runningman
566 * sequence for now, this means we are rm_inactive, we are currently
567 * using behavior _rmb_off_tap_sh, and _last_rmb was rmb_anchor */
568 if (_status == rm_inactive) {
569 if (_rmb == rmb_anchor && _last_rmb == rmb_off_tap_sh) {
570
571 // ok we can start
572 _status = rm_running;
573 restart(as);
574 }
575 } else {
576 // if we're not inactive, just do normal behavior
577 handle_rmb(as);
578 }
579
580 // remember what we did last
581 _last_rmb = _rmb;
582 }
583
584 [[nodiscard]] auto get_difficulty() const -> float
585 {
586 if (_status == rm_inactive || _rm._len < 3) {
587 return 1.F;
588 }
589
590 const auto flool = ms_from(last_anchor_time, _start);
591
592 const auto len = static_cast<float>(_rm._len);
593 const auto len_1 = static_cast<float>(_rm._len - 1);
594
595 const auto pule = (flool / len_1) * (len / len_1);
596 const auto drool = ms_to_scaled_nps(pule) * rma_diff_scaler;
597 return drool;
598 }
599};
Definition GenericSequencing.h:224
float _last
row_time of last note on this col
Definition GenericSequencing.h:76
float _sc_ms
same-column ms: time between now and previous tap
Definition GenericSequencing.h:65
Definition RMSequencing.h:178
Definition RMSequencing.h:27
auto get_off_tap_same_prop() const -> float
same hand taps to anchor len
Definition RMSequencing.h:166
int off_taps
any off anchor taps
Definition RMSequencing.h:35
int oht_len
current oht sequence length
Definition RMSequencing.h:44
int jack_taps
jack taps (like, actual jacks in the runningman)
Definition RMSequencing.h:50
int anch_len
consecutive anchors sequence length, track this to throw out 2h trills
Definition RMSequencing.h:54
int oht_taps
one hand trill taps
Definition RMSequencing.h:42
int ran_taps
all taps contained in this runningman sequence
Definition RMSequencing.h:29
int _len
total length of the anchor
Definition RMSequencing.h:32
int ot_sh_len
it's not really a runningman if the anchor is on the off column is it
Definition RMSequencing.h:47
auto get_offhand_tap_prop() const -> float
off hand taps to anchor len
Definition RMSequencing.h:156
int off_taps_sh
off anchor taps on the same hand, i.e. the 2s in 1211121
Definition RMSequencing.h:39
auto get_off_tap_prop() const -> float
any off taps to anchor len
Definition RMSequencing.h:147