1use super::{PARAMETER_COUNT, QUANTIZATION_FACTOR, quantize_coord};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct TargetPoint {
11 pub temperature: i64,
13 pub humidity: i64,
15 pub continentalness: i64,
17 pub erosion: i64,
19 pub depth: i64,
21 pub weirdness: i64,
23}
24
25impl TargetPoint {
26 #[must_use]
28 pub const fn new(
29 temperature: i64,
30 humidity: i64,
31 continentalness: i64,
32 erosion: i64,
33 depth: i64,
34 weirdness: i64,
35 ) -> Self {
36 Self {
37 temperature,
38 humidity,
39 continentalness,
40 erosion,
41 depth,
42 weirdness,
43 }
44 }
45
46 #[must_use]
48 pub fn from_floats(
49 temperature: f64,
50 humidity: f64,
51 continentalness: f64,
52 erosion: f64,
53 depth: f64,
54 weirdness: f64,
55 ) -> Self {
56 Self {
57 temperature: quantize_coord(temperature),
58 humidity: quantize_coord(humidity),
59 continentalness: quantize_coord(continentalness),
60 erosion: quantize_coord(erosion),
61 depth: quantize_coord(depth),
62 weirdness: quantize_coord(weirdness),
63 }
64 }
65
66 #[must_use]
69 pub const fn to_parameter_array(self) -> [i64; PARAMETER_COUNT] {
70 [
71 self.temperature,
72 self.humidity,
73 self.continentalness,
74 self.erosion,
75 self.depth,
76 self.weirdness,
77 0, ]
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub struct Parameter {
89 pub min: i64,
91 pub max: i64,
93}
94
95impl Parameter {
96 #[must_use]
98 pub const fn new(min: i64, max: i64) -> Self {
99 Self { min, max }
100 }
101
102 #[must_use]
104 pub fn point(value: f32) -> Self {
105 Self::span(value, value)
106 }
107
108 #[must_use]
110 pub fn span(min: f32, max: f32) -> Self {
111 debug_assert!(min <= max, "min > max: {min} > {max}");
112 Self {
113 min: (min * QUANTIZATION_FACTOR) as i64,
114 max: (max * QUANTIZATION_FACTOR) as i64,
115 }
116 }
117
118 #[must_use]
120 pub const fn span_params(min: &Parameter, max: &Parameter) -> Self {
121 debug_assert!(min.min <= max.max, "span_params: min > max");
122 Self {
123 min: min.min,
124 max: max.max,
125 }
126 }
127
128 #[inline]
133 #[must_use]
134 pub const fn distance(&self, target: i64) -> i64 {
135 let above = target - self.max;
136 let below = self.min - target;
137 if above > 0 {
138 above
139 } else if below > 0 {
140 below
141 } else {
142 0
143 }
144 }
145
146 #[must_use]
148 pub const fn span_with(&self, other: Option<&Parameter>) -> Self {
149 match other {
150 Some(o) => Self {
151 min: self.min.min(o.min),
152 max: self.max.max(o.max),
153 },
154 None => *self,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Copy)]
164pub struct ParameterPoint {
165 pub temperature: Parameter,
167 pub humidity: Parameter,
169 pub continentalness: Parameter,
171 pub erosion: Parameter,
173 pub depth: Parameter,
175 pub weirdness: Parameter,
177 pub offset: i64,
179}
180
181impl ParameterPoint {
182 #[must_use]
184 pub const fn new(
185 temperature: Parameter,
186 humidity: Parameter,
187 continentalness: Parameter,
188 erosion: Parameter,
189 depth: Parameter,
190 weirdness: Parameter,
191 offset: i64,
192 ) -> Self {
193 Self {
194 temperature,
195 humidity,
196 continentalness,
197 erosion,
198 depth,
199 weirdness,
200 offset,
201 }
202 }
203
204 #[must_use]
208 #[expect(
209 clippy::many_single_char_names,
210 reason = "single-letter abbreviations match vanilla's climate parameter names"
211 )]
212 pub const fn fitness(&self, target: &TargetPoint) -> i64 {
213 let t = self.temperature.distance(target.temperature);
214 let h = self.humidity.distance(target.humidity);
215 let c = self.continentalness.distance(target.continentalness);
216 let e = self.erosion.distance(target.erosion);
217 let d = self.depth.distance(target.depth);
218 let w = self.weirdness.distance(target.weirdness);
219
220 t * t + h * h + c * c + e * e + d * d + w * w + self.offset * self.offset
222 }
223
224 #[must_use]
226 pub const fn parameter_space(&self) -> [Parameter; PARAMETER_COUNT] {
227 [
228 self.temperature,
229 self.humidity,
230 self.continentalness,
231 self.erosion,
232 self.depth,
233 self.weirdness,
234 Parameter::new(self.offset, self.offset),
235 ]
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn test_target_point_from_floats() {
245 let target = TargetPoint::from_floats(0.5, -0.3, 0.0, 0.1, 0.0, 0.2);
246 assert_eq!(target.temperature, 5000);
247 assert_eq!(target.humidity, -3000);
248 assert_eq!(target.continentalness, 0);
249 assert_eq!(target.erosion, 1000);
250 assert_eq!(target.depth, 0);
251 assert_eq!(target.weirdness, 2000);
252 }
253
254 #[test]
255 fn test_parameter_distance() {
256 let param = Parameter::new(-5000, 5000);
257
258 assert_eq!(param.distance(0), 0);
260 assert_eq!(param.distance(5000), 0);
261 assert_eq!(param.distance(-5000), 0);
262
263 assert_eq!(param.distance(6000), 1000);
265 assert_eq!(param.distance(-6000), 1000);
266 assert_eq!(param.distance(10000), 5000);
267 }
268
269 #[test]
270 fn test_parameter_point_fitness() {
271 let params = ParameterPoint::new(
272 Parameter::new(0, 0),
273 Parameter::new(0, 0),
274 Parameter::new(0, 0),
275 Parameter::new(0, 0),
276 Parameter::new(0, 0),
277 Parameter::new(0, 0),
278 0,
279 );
280
281 let target = TargetPoint::new(0, 0, 0, 0, 0, 0);
283 assert_eq!(params.fitness(&target), 0);
284
285 let target = TargetPoint::new(100, 0, 0, 0, 0, 0);
287 assert_eq!(params.fitness(&target), 100 * 100);
288
289 let target = TargetPoint::new(100, 100, 0, 0, 0, 0);
291 assert_eq!(params.fitness(&target), 100 * 100 + 100 * 100);
292 }
293}