use super::{ScoringResult, ScoringSystem};
const DEBUG: bool = false;
struct Note {
second: f32,
assigned_hit: Option<AssignedHit>,
}
struct AssignedHit {
hit: *mut Hit,
deviation: f32,
}
struct Hit {
second: f32,
assigned_note: Option<*const Note>,
}
impl Hit {
unsafe fn find_matching_note(&mut self, notes: *mut Vec<Note>, judge: &crate::Judge) {
if DEBUG {
println!(
"Ok so, we're searching for a matching note for {}",
self.second
)
}
let mut best_note: Option<&mut Note> = None;
let mut best_deviation = f32::INFINITY;
for note in &mut *notes {
let deviation = (note.second - self.second).abs();
if deviation > best_deviation {
continue;
}
if deviation > judge.bad_window {
continue;
}
if DEBUG {
println!(
"Found best note so far at {} (dev={})",
note.second, deviation
)
}
if let Some(assigned_hit) = ¬e.assigned_hit {
if assigned_hit.deviation - 0.000001 < deviation {
if DEBUG {
println!(
"Already assigned to something better (dev={}) unfortunately..",
assigned_hit.deviation
);
}
continue;
}
if DEBUG {
println!(
"Already assigned to hit {} but we could overwrite! :) ({} < {})",
(*assigned_hit.hit).second,
deviation,
assigned_hit.deviation
);
}
}
best_note = Some(note);
best_deviation = deviation;
}
let best_note: &mut Note = match best_note {
Some(a) => a,
None => {
self.assigned_note = None;
return;
}
};
if DEBUG {
println!(
"After iterating notes, the best note is at {}",
best_note.second
)
}
let prev_assigned_hit_to_be_relocated: Option<*mut Hit> = best_note
.assigned_hit
.as_ref()
.map(|assigned_hit| assigned_hit.hit);
best_note.assigned_hit = Some(AssignedHit {
hit: self,
deviation: best_deviation,
});
self.assigned_note = Some(best_note as *const Note);
if let Some(prev_assigned_hit_to_be_relocated) = prev_assigned_hit_to_be_relocated {
if DEBUG {
println!(">>> Ah yes, it was already assigned. Starting inner re-find...\n")
}
(*prev_assigned_hit_to_be_relocated).find_matching_note(notes, judge);
if DEBUG {
println!("\n<<< Inner re-find done")
}
}
}
}
unsafe fn column_rescore<W: crate::Wife>(
mut notes: Vec<Note>,
mut hits: Vec<Hit>,
judge: &crate::Judge,
) -> (f32, u64) {
let stray_tap_weight: f32 = W::MISS_WEIGHT;
for hit in &mut hits {
if DEBUG {
println!("Initial search for hit at {}", hit.second)
}
hit.find_matching_note(&mut notes, judge);
if DEBUG {
println!(
"Initial search for hit at {} completed -> {:?}",
hit.second,
hit.assigned_note.map(|n| (*n).second)
);
}
if DEBUG {
println!(".")
}
}
if DEBUG {
println!(".");
for hit in &hits {
println!(
"Hit {}\t-> Note {:?}\t(dev={:?})",
hit.second,
hit.assigned_note.map(|n| (*n).second),
hit.assigned_note
.map(|n| (*n).assigned_hit.as_ref().unwrap().deviation)
);
}
println!(".\nTHE NOTE PERSPECTIVE OF THINGS");
for note in ¬es {
println!(
"Note {}\t-> Hit {:?}\t(dev={:?})",
note.second,
note.assigned_hit.as_ref().map(|h| (*h.hit).second),
note.assigned_hit.as_ref().map(|h| h.deviation)
);
}
}
let num_stray_taps = hits
.iter()
.filter(|hit| hit.assigned_note.is_none())
.count();
let num_misses = notes
.iter()
.filter(|note| note.assigned_hit.is_none())
.count();
if DEBUG {
println!(
"Found {} misses and {} stray taps",
num_misses, num_stray_taps
);
}
let mut num_matched_hits = 0;
let mut wifescore_sum: f32 = notes
.iter()
.filter_map(|note| note.assigned_hit.as_ref()) .map(|assigned_hit| W::calc_deviation(assigned_hit.deviation, judge))
.inspect(|_| num_matched_hits += 1)
.sum();
wifescore_sum += W::MISS_WEIGHT * num_misses as f32;
wifescore_sum += stray_tap_weight * num_stray_taps as f32;
let num_judged_notes = num_matched_hits + num_misses + num_stray_taps;
(wifescore_sum, num_judged_notes as u64)
}
pub struct MatchingScorer;
impl ScoringSystem for MatchingScorer {
fn evaluate<W: crate::Wife>(
lane: &crate::NoteAndHitSeconds,
judge: &crate::Judge,
) -> ScoringResult {
let crate::NoteAndHitSeconds {
note_seconds,
hit_seconds,
} = lane;
assert!(crate::util::is_sorted(hit_seconds));
let notes: Vec<Note> = note_seconds
.iter()
.map(|&second| Note {
second,
assigned_hit: None,
})
.collect();
let hits: Vec<Hit> = hit_seconds
.iter()
.map(|&second| Hit {
second,
assigned_note: None,
})
.collect();
let (wifescore_sum, num_judged_notes) = unsafe { column_rescore::<W>(notes, hits, judge) };
ScoringResult {
wifescore_sum,
num_judged_notes,
}
}
}