finished mania + restructured a little

This commit is contained in:
MaxOhn
2022-10-19 11:48:11 +02:00
parent 044fe986c9
commit 6ff46367ca
24 changed files with 737 additions and 350 deletions
+1 -1
View File
@@ -109,7 +109,7 @@ pub struct EffectPoint {
impl EffectPoint {
/// The default slider velocity for a [`DifficultyPoint`]
pub const DEFAULT_KIAI: bool = true;
pub const DEFAULT_KIAI: bool = false;
/// Create a new [`EffectPoint`].
#[inline]
-34
View File
@@ -1,34 +0,0 @@
use std::hash::{BuildHasher, Hasher};
#[derive(Copy, Clone, Default)]
pub(crate) struct BuildByteHasher;
impl BuildHasher for BuildByteHasher {
type Hasher = ByteHasher;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
ByteHasher { byte: 0 }
}
}
pub(crate) struct ByteHasher {
byte: u8,
}
impl Hasher for ByteHasher {
#[inline]
fn finish(&self) -> u64 {
self.byte as u64
}
#[inline]
fn write(&mut self, _: &[u8]) {
unreachable!()
}
#[inline]
fn write_u8(&mut self, byte: u8) {
self.byte = byte;
}
}
+9 -10
View File
@@ -2,8 +2,8 @@ use std::cmp::Ordering;
use crate::{
curve::{Curve, CurveBuffers},
limited_queue::LimitedQueue,
parse::{legacy_sort, HitObjectKind, Pos2},
util::{FloatExt, LimitedQueue},
Beatmap, GameMode,
};
@@ -17,7 +17,6 @@ use self::{
pattern_type::PatternType,
};
mod byte_hasher;
mod legacy_random;
mod pattern;
mod pattern_generator;
@@ -32,12 +31,14 @@ impl Beatmap {
let mut n_circles = 0;
let mut n_sliders = 0;
let seed =
(map.hp + map.cs).round() as i32 * 20 + (map.od * 41.2) as i32 + map.ar.round() as i32;
let seed = (map.hp + map.cs).round_even() as i32 * 20
+ (map.od * 41.2) as i32
+ map.ar.round_even() as i32;
let mut random = Random::new(seed);
let rounded_cs = map.cs.round();
let rounded_od = map.od.round();
let rounded_cs = map.cs.round_even();
let rounded_od = map.od.round_even();
let slider_or_spinner_count = self
.hit_objects
@@ -78,9 +79,7 @@ impl Beatmap {
};
let total_columns = map.cs as i32;
let mut last_values = PrevValues::default();
let mut curve_bufs = CurveBuffers::default();
for (obj, sound) in self.hit_objects.iter().zip(self.sounds.iter()) {
@@ -167,8 +166,8 @@ impl Beatmap {
&last_values.pattern,
);
last_values.time = obj.start_time;
last_values.pos = obj.pos;
last_values.time = end_time;
last_values.pos = Pos2 { x: 256.0, y: 192.0 };
compute_density(end_time, &mut density);
+10 -10
View File
@@ -1,31 +1,31 @@
use std::collections::HashSet;
use crate::parse::{HitObject, HitObjectKind, Pos2};
use crate::{
parse::{HitObject, HitObjectKind, Pos2},
util::ByteHasher,
};
use super::{
byte_hasher::BuildByteHasher,
pattern_generator::{
distance_object::DistanceObjectPatternGenerator,
end_time_object::EndTimeObjectPatternGenerator, hit_object::HitObjectPatternGenerator,
},
use super::pattern_generator::{
distance_object::DistanceObjectPatternGenerator,
end_time_object::EndTimeObjectPatternGenerator, hit_object::HitObjectPatternGenerator,
};
#[derive(Default)]
pub(crate) struct Pattern {
pub(crate) hit_objects: Vec<HitObject>,
contained_columns: HashSet<u8, BuildByteHasher>,
contained_columns: HashSet<u8, ByteHasher>,
}
impl Pattern {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
hit_objects: Vec::with_capacity(capacity),
contained_columns: HashSet::with_hasher(BuildByteHasher),
contained_columns: HashSet::with_hasher(ByteHasher),
}
}
fn new_single(hit_object: HitObject, column: u8) -> Self {
let mut contained_columns = HashSet::with_capacity_and_hasher(1, BuildByteHasher);
let mut contained_columns = HashSet::with_capacity_and_hasher(1, ByteHasher);
contained_columns.insert(column);
let hit_objects = vec![hit_object];
@@ -6,6 +6,7 @@ use crate::{
curve::Curve,
mania::ManiaObject,
parse::{HitObject, HitSound},
util::FloatExt,
Beatmap,
};
@@ -58,7 +59,7 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
let beat_len = timing_point.beat_len * difficulty_point.bpm_mult;
let span_count = (repeats + 1) as i32;
let start_time = hit_object.start_time.round() as i32;
let start_time = hit_object.start_time.round_even() as i32;
// * This matches stable's calculation.
let end_time = (start_time as f64
@@ -97,12 +98,12 @@ impl<'h> DistanceObjectPatternGenerator<'h> {
let mut end_time_pattern = Pattern::default();
for obj in orig_pattern.hit_objects {
let column = ManiaObject::new(&obj).column(self.total_columns as f32) as u8;
let col = ManiaObject::column(obj.pos.x, self.total_columns as f32) as u8;
if self.end_time != obj.end_time().round() as i32 {
intermediate_pattern.add_object(obj, column);
if self.end_time != obj.end_time().round_even() as i32 {
intermediate_pattern.add_object(obj, col);
} else {
end_time_pattern.add_object(obj, column);
end_time_pattern.add_object(obj, col);
}
}
@@ -98,15 +98,14 @@ impl<'h> HitObjectPatternGenerator<'h> {
pub(crate) fn generate(&mut self) -> Pattern {
let pattern = self.generate_core();
for obj in pattern.hit_objects.iter().map(ManiaObject::new) {
if self.convert_type.contains(PatternType::STAIR)
&& obj.column(self.total_columns as f32) as i32 == self.total_columns - 1
{
for obj in pattern.hit_objects.iter() {
let col = ManiaObject::column(obj.pos.x, self.total_columns as f32) as i32;
if self.convert_type.contains(PatternType::STAIR) && col == self.total_columns - 1 {
self.stair_type = PatternType::REVERSE_STAIR;
}
if self.convert_type.contains(PatternType::REVERSE_STAIR)
&& obj.column(self.total_columns as f32) as i32 == self.random_start()
if self.convert_type.contains(PatternType::REVERSE_STAIR) && col == self.random_start()
{
self.stair_type = PatternType::STAIR;
}
@@ -120,12 +119,9 @@ impl<'h> HitObjectPatternGenerator<'h> {
return Pattern::new_note(self, 0);
}
let last_column = self
.prev_pattern
.hit_objects
.last()
.map(ManiaObject::new)
.map_or(0, |h| h.column(self.total_columns as f32) as u8);
let last_column = self.prev_pattern.hit_objects.last().map_or(0, |h| {
ManiaObject::column(h.pos.x, self.total_columns as f32) as u8
});
let random_start = self.random_start() as u8;
@@ -21,15 +21,13 @@ trait PatternGenerator {
fn get_column(&self, allow_special: Option<bool>) -> u8 {
let allow_special = allow_special.unwrap_or(false);
let res = if allow_special && self.total_columns() == 8 {
if allow_special && self.total_columns() == 8 {
const LOCAL_X_DIVISOR: f32 = 512.0 / 7.0;
((self.hit_object().pos.x / LOCAL_X_DIVISOR).floor() as u8).clamp(0, 6) + 1
} else {
ManiaObject::new(self.hit_object()).column(self.total_columns() as f32) as u8
};
res
ManiaObject::column(self.hit_object().pos.x, self.total_columns() as f32) as u8
}
}
fn get_random_note_count(
+1 -1
View File
@@ -32,7 +32,7 @@ impl fmt::Display for PatternType {
$(
if $self.contains(Self::$pat) {
if $written {
$f.write_str(" | ")?;
$f.write_str(", ")?;
} else {
$written = true;
}
+1 -3
View File
@@ -1,13 +1,12 @@
use std::{borrow::Cow, cmp::Ordering};
use crate::parse::HitObject;
use crate::{parse::HitObject, util::SortedVec};
pub use self::{
attributes::{BeatmapAttributes, BeatmapAttributesBuilder, BeatmapHitWindows},
breaks::Break,
control_points::{DifficultyPoint, EffectPoint, TimingPoint},
mode::GameMode,
sorted_vec::SortedVec,
};
mod attributes;
@@ -15,7 +14,6 @@ mod breaks;
mod control_points;
mod converts;
mod mode;
mod sorted_vec;
/// The main beatmap struct containing all data relevant
/// for difficulty and performance calculation
-100
View File
@@ -1,100 +0,0 @@
use std::{
cmp::Ordering,
convert::identity,
fmt::{Debug, Formatter, Result as FmtResult},
ops::Deref,
};
use super::{control_points::EffectPoint, DifficultyPoint, TimingPoint};
/// A [`Vec`] whose elements are guaranteed to be in order based on the given comparator.
#[derive(Clone)]
pub struct SortedVec<T> {
inner: Vec<T>,
cmp: fn(&T, &T) -> Ordering,
}
impl<T> SortedVec<T> {
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
/// one of the matches could be returned.
/// If the value is not found then [`Result::Err`] is returned, containing
/// the index where a matching element could be inserted while maintaining
/// sorted order.
pub fn find(&self, value: &T) -> Result<usize, usize> {
self.inner
.binary_search_by(|probe| (self.cmp)(probe, value))
}
pub(crate) fn push(&mut self, value: T) {
let idx = self.find(&value).map_or_else(identity, identity);
self.inner.insert(idx, value);
}
pub(crate) fn dedup_by_key<F, K>(&mut self, mut key: F)
where
F: FnMut(&mut T) -> K,
K: PartialEq,
{
self.inner.dedup_by(|a, b| key(a) == key(b))
}
}
impl<T> Deref for SortedVec<T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Debug> Debug for SortedVec<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Debug::fmt(&self.inner, f)
}
}
impl Default for SortedVec<TimingPoint> {
#[inline]
fn default() -> Self {
Self {
inner: Vec::new(),
cmp: |a, b| a.time.partial_cmp(&b.time).unwrap_or(Ordering::Equal),
}
}
}
impl Default for SortedVec<DifficultyPoint> {
#[inline]
fn default() -> Self {
Self {
inner: Vec::new(),
cmp: |a, b| a.time.partial_cmp(&b.time).unwrap_or(Ordering::Equal),
}
}
}
impl Default for SortedVec<EffectPoint> {
#[inline]
fn default() -> Self {
Self {
inner: Vec::new(),
cmp: |a, b| a.time.partial_cmp(&b.time).unwrap_or(Ordering::Equal),
}
}
}
impl SortedVec<DifficultyPoint> {
pub(crate) fn push_if_not_redundant(&mut self, value: DifficultyPoint) {
let is_redundant = match self.find(&value).map_err(|idx| idx.checked_sub(1)) {
Ok(idx) | Err(Some(idx)) => value.is_redundant(&self[idx]),
Err(None) => value.is_redundant(&DifficultyPoint::default()),
};
if !is_redundant {
self.push(value);
}
}
}