Skip to main content

steel_utils/value_providers/
float.rs

1use serde::{Deserialize, Deserializer, de::Error as _};
2
3use crate::random::Random;
4
5/// A `float`-valued provider.
6///
7/// Mirrors vanilla's `FloatProvider` hierarchy. `WeightedList` is omitted
8/// until a carver or feature needs it.
9#[derive(Debug, Clone, Copy)]
10pub enum FloatProvider {
11    /// Always returns the same value.
12    Constant(f32),
13    /// Uniform over `[min_inclusive, max_exclusive)`.
14    Uniform {
15        /// Inclusive lower bound.
16        min_inclusive: f32,
17        /// Exclusive upper bound.
18        max_exclusive: f32,
19    },
20    /// Sum of two uniform draws — symmetric triangle when `plateau == 0`,
21    /// trapezoid otherwise.
22    Trapezoid {
23        /// Lower bound.
24        min: f32,
25        /// Upper bound.
26        max: f32,
27        /// Flat-top width.
28        plateau: f32,
29    },
30    /// Gaussian with given mean/deviation, clamped to `[min, max]`.
31    ClampedNormal {
32        /// Distribution mean.
33        mean: f32,
34        /// Standard deviation.
35        deviation: f32,
36        /// Inclusive lower bound.
37        min: f32,
38        /// Inclusive upper bound.
39        max: f32,
40    },
41}
42
43impl FloatProvider {
44    /// Sample a value.
45    ///
46    /// Matches vanilla's `FloatProvider.sample` exactly. Order of
47    /// `random.next_*` calls is preserved for hash-level determinism.
48    pub fn sample<R: Random + ?Sized>(self, random: &mut R) -> f32 {
49        match self {
50            Self::Constant(v) => v,
51            Self::Uniform {
52                min_inclusive,
53                max_exclusive,
54            } => random.next_f32() * (max_exclusive - min_inclusive) + min_inclusive,
55            Self::Trapezoid { min, max, plateau } => {
56                let range = max - min;
57                let plateau_start = (range - plateau) / 2.0;
58                let plateau_end = range - plateau_start;
59                min + random.next_f32() * plateau_end + random.next_f32() * plateau_start
60            }
61            Self::ClampedNormal {
62                mean,
63                deviation,
64                min,
65                max,
66            } => {
67                // Mth.normal: mean + deviation * (float)nextGaussian()
68                let sample = mean + deviation * random.next_gaussian() as f32;
69                sample.clamp(min, max)
70            }
71        }
72    }
73
74    /// Static lower bound.
75    #[must_use]
76    pub const fn min(self) -> f32 {
77        match self {
78            Self::Constant(v) => v,
79            Self::Uniform { min_inclusive, .. } => min_inclusive,
80            Self::Trapezoid { min, .. } | Self::ClampedNormal { min, .. } => min,
81        }
82    }
83
84    /// Static upper bound.
85    #[must_use]
86    pub const fn max(self) -> f32 {
87        match self {
88            Self::Constant(v) => v,
89            Self::Uniform { max_exclusive, .. } => max_exclusive,
90            Self::Trapezoid { max, .. } | Self::ClampedNormal { max, .. } => max,
91        }
92    }
93}
94
95impl<'de> Deserialize<'de> for FloatProvider {
96    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
97        #[derive(Deserialize)]
98        #[serde(tag = "type", deny_unknown_fields)]
99        enum Tagged {
100            #[serde(rename = "minecraft:constant")]
101            Constant { value: f32 },
102            #[serde(rename = "minecraft:uniform")]
103            Uniform {
104                min_inclusive: f32,
105                max_exclusive: f32,
106            },
107            #[serde(rename = "minecraft:trapezoid")]
108            Trapezoid { min: f32, max: f32, plateau: f32 },
109            #[serde(rename = "minecraft:clamped_normal")]
110            ClampedNormal {
111                mean: f32,
112                deviation: f32,
113                min: f32,
114                max: f32,
115            },
116        }
117
118        let value = serde_json::Value::deserialize(d)?;
119        if value.is_number() {
120            return Ok(Self::Constant(
121                f32::deserialize(value).map_err(D::Error::custom)?,
122            ));
123        }
124
125        Ok(
126            match serde_json::from_value(value).map_err(D::Error::custom)? {
127                Tagged::Constant { value: v } => Self::Constant(v),
128                Tagged::Uniform {
129                    min_inclusive,
130                    max_exclusive,
131                } => Self::Uniform {
132                    min_inclusive,
133                    max_exclusive,
134                },
135                Tagged::Trapezoid { min, max, plateau } => Self::Trapezoid { min, max, plateau },
136                Tagged::ClampedNormal {
137                    mean,
138                    deviation,
139                    min,
140                    max,
141                } => Self::ClampedNormal {
142                    mean,
143                    deviation,
144                    min,
145                    max,
146                },
147            },
148        )
149    }
150}