private ArrayList getNoteDifficulty() { ArrayList[] note_stack = { new ArrayList(), new ArrayList() }; Note[] prior_color_note = { null, null }; ArrayList result_note_data = new ArrayList(); // not used for anything just yet ArrayList prior_note_stack = new ArrayList(); for (int itr = 0; itr < _notes.Length; itr++) { Note note = _notes[itr]; // keep a history of previous notes if (note._type != 2) { prior_note_stack.Add(note); } if (note._type == 0 || note._type == 1) { float score = 0.0f; Note next_note = getNextNote(_notes, note._type, itr); if (next_note != null) { if (note._time == next_note._time) { //red_note_stack.push(note); note_stack[note._type].Add(note); continue; } } if (note_stack[note._type].Count > 0) { Note last_of_type = (Note)note_stack[note._type][note_stack[note._type].Count - 1]; if (note._time == last_of_type._time) { // don't handle stack yet, may be more notes note_stack[note._type].Add(note); //red_note_stack.push(note); if (getNextNote(_notes, note._type, itr) != null) { continue; } } // handle stack here if (note_stack[note._type].Count == 2) { // handle two note stack here // figure out which note is first, based on distance. Really need to know the last note Note note_a = (Note)note_stack[note._type][0], note_b = (Note)note_stack[note._type][1], first_note = null, last_note = null; if (GetPhysicalNoteDistance(note_a, note_b) < GetPhysicalNoteDistance(note_b, note_a)) { first_note = note_a; last_note = note_b; } else { first_note = note_b; last_note = note_a; } // add the average to each note to give a difficulty boost float score_a = NoteHitDifficulty(prior_note_stack, prior_color_note[note._type], first_note), score_b = NoteHitDifficulty(prior_note_stack, prior_color_note[note._type], last_note); float avg_score_a_b = ((score_a + score_b) / 2.0f); score_a += avg_score_a_b; score_b += avg_score_a_b; NoteDiff a = new NoteDiff() { _time = note_a._time, _diffScore = score_a, _type = note_a._type }; NoteDiff b = new NoteDiff() { _time = note_b._time, _diffScore = score_b, _type = note_b._type }; result_note_data.Add(a); result_note_data.Add(b); prior_color_note[note._type] = last_note; } else { ArrayList dist_data = new ArrayList(); float largest_dist = -1; int largest_index = 0; ArrayList diffs = new ArrayList(); for (int i = 0; i < note_stack[note._type].Count; i++) { for (int j = 0; j < note_stack[note._type].Count; j++) { Note note_a = (Note)note_stack[note._type][i]; Note note_b = (Note)note_stack[note._type][j]; if (i != j) { var dist = GetPhysicalNoteDistance(note_a, note_b); if (dist >= largest_dist) { largest_dist = dist; largest_index = i; } } } diffs.Add(NoteHitDifficulty(prior_note_stack, prior_color_note[note._type], note)); } for (int i = 0; i < note_stack[note._type].Count; i++) { for (int j = 0; j < note_stack[note._type].Count; j++) { Note note_a = (Note)note_stack[note._type][i]; Note note_b = (Note)note_stack[note._type][j]; if (i != j) { var dist = GetPhysicalNoteDistance(note_a, note_b); if (dist >= largest_dist) { largest_dist = dist; largest_index = i; } } } diffs.Add(NoteHitDifficulty(prior_note_stack, prior_color_note[note._type], note)); } Note last_note = (Note)note_stack[note._type][largest_index]; float sum = 0; foreach (float diff in diffs) { sum += diff; } float sum_avg = sum / (float)(diffs.Count); for (int i = 0; i < diffs.Count; i++) { if (i >= note_stack[note._type].Count) { continue; } Note note_a = (Note)note_stack[note._type][i]; float diff = (float)diffs[i]; NoteDiff a = new NoteDiff() { _time = note_a._time, _diffScore = diff + sum_avg, _type = note_a._type }; result_note_data.Add(a); } prior_color_note[note._type] = last_note; } note_stack[note._type].Clear(); } if (note_stack[note._type].Count == 0) { score = NoteHitDifficulty(prior_note_stack, prior_color_note[note._type], note); prior_color_note[note._type] = note; NoteDiff nd = new NoteDiff() { _time = note._time, _diffScore = score, _type = note._type }; result_note_data.Add(nd); } } else { // skip because this is a bomb or wall ... } // pop off old notes more than a beat away Note temp = (Note)prior_note_stack[0]; while (prior_note_stack.Count > 0 && temp._time < note._time - 1) { prior_note_stack.RemoveAt(0); } } //return { 'red_diffs': red_diffs, 'blue_diffs': blue_diffs }; writer.Write("Calculated Note Difficulty for " + _notes.Length + " notes." + Environment.NewLine); return(result_note_data); }
private float calcSongEnduranceScore(ArrayList note_data, int beats_per_section) { // first find peak strain value NoteDiff first_note = (NoteDiff)note_data[0]; NoteDiff last_note = (NoteDiff)note_data[note_data.Count - 1]; float first_beat = first_note._time; float last_beat = last_note._time; float peak_strain = -1f; //float curr_strain = 0; ArrayList strains = new ArrayList(); // init all possible beat sections to 0 for (int itr = (int)Math.Floor(first_beat / beats_per_section) * beats_per_section; itr <= (int)Math.Floor(last_beat / beats_per_section) * beats_per_section; itr += beats_per_section) { //strains[itr / beats_per_section] = 0; strains.Add(0.0f); } float curr_beat = first_beat; foreach (NoteDiff note in note_data) { int beat_pos = (int)Math.Floor(note._time / beats_per_section); if (beat_pos < strains.Count) { strains[beat_pos] = (float)(strains[beat_pos]) + note._diffScore; } else { strains.Add(note._diffScore); } } foreach (float strain in strains) { if (strain > peak_strain) { peak_strain = strain; } } // analyze the strain over time // first find average float strain_avg = 0f, strain_sum = 0f; foreach (float strain in strains) { strain_sum += strain; } strain_avg = strain_sum / (float)(strains.Count); // next std deviation float strain_dev = 0; strain_sum = 0; foreach (float strain in strains) { strain_sum += (float)(Math.Pow(strain - strain_avg, 2.0f)); } strain_dev = (float)Math.Sqrt(strain_sum / ((float)strains.Count)); writer.Write("Strain Peak: " + peak_strain + " Strain Avg: " + strain_avg + " Strain Dev " + strain_dev + Environment.NewLine); // do some bullshit math to get score float strain_score = 0f; float bonus = 0.0f; // the longer we are close to or above avg by std, then increase bonus because is tiring to work above avg all the time... something like that? float max_bonus = 2.0f; for (int i = 0; i < strains.Count; i++) { float strain = (float)strains[i]; float score = 0; if (strain > strain_avg) { if (strain > strain_avg + strain_dev * 3f) { bonus = Math.Min(bonus + 0.3f, max_bonus); } else if (strain > strain_avg + strain_dev * 2f) { bonus = Math.Min(bonus + 0.11f, max_bonus); } else if (strain > strain_avg + strain_dev * 1f) { bonus = Math.Min(bonus + 0.05f, max_bonus); } else if (strain > strain_avg + strain_dev * 0.5f) { bonus = Math.Min(bonus + 0.03f, max_bonus); // new } else if (strain > strain_avg + strain_dev * 0f) { bonus = Math.Min(bonus + 0.02f, max_bonus); // was 0.03 } } else { if (strain < strain_avg - strain_dev * 3f) { bonus = Math.Max(bonus - 0.20f, 0.0f); } else if (strain < strain_avg - strain_dev * 2f) { bonus = Math.Max(bonus - 0.10f, 0.0f); } else if (strain < strain_avg - strain_dev * 1f) { bonus = Math.Max(bonus - 0.05f, 0.0f); } else if (strain < strain_avg - strain_dev * 0.5f) { bonus = Math.Max(bonus - 0.01f, max_bonus); } else if (strain < strain_avg - strain_dev * 0f) { bonus = Math.Max(bonus + 0.002f, max_bonus); } // trying out having strain for a section not compared against peak strain // TODO tried it, need more balancing for sure, helped some songs, made many more worse score += strain * (1.0f + bonus); //score += (float)Math.Pow(strain / peak_strain, 2f) * (1f + bonus); } score *= 1f + ((float)i / (float)strains.Count) * 0.3f; // TODO Balance //writer.Write("Strain Pre Length: " + score + Environment.NewLine); //score *= 1.0f + ((float)i / (25.0f * (float)beats_per_section)); //writer.Write("Strain Post Length: " + score + Environment.NewLine); strain_score += score; } strain_score *= (peak_strain / 3f) * (strain_avg / 2f) * Mathf.Clamp(10f / strain_dev, 0.6f, 2.0f); // bonus scalar so easy songs that are uniform in diff don't get high strain scores writer.Write("Strain Score Pre Log: " + strain_score + Environment.NewLine); //strain_score = (float)(Math.Log(strain_score) / Math.Log(1.2)); strain_score = (float)Math.Sqrt(strain_score); // This should never happen anymore, but leaving just in case if (strain_score < 0f) { strain_score = 0f; } writer.Write("Strain Score For " + beats_per_section + " Beats: " + strain_score + Environment.NewLine); return(strain_score); }