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 #[inline]
148 #[must_use]
149 pub const fn distance_param(&self, target: &Parameter) -> i64 {
150 let above = target.min - self.max;
151 let below = self.min - target.max;
152 if above > 0 {
153 above
154 } else if below > 0 {
155 below
156 } else {
157 0
158 }
159 }
160
161 #[must_use]
163 pub const fn span_with(&self, other: Option<&Parameter>) -> Self {
164 match other {
165 Some(o) => Self {
166 min: self.min.min(o.min),
167 max: self.max.max(o.max),
168 },
169 None => *self,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy)]
179pub struct ParameterPoint {
180 pub temperature: Parameter,
182 pub humidity: Parameter,
184 pub continentalness: Parameter,
186 pub erosion: Parameter,
188 pub depth: Parameter,
190 pub weirdness: Parameter,
192 pub offset: i64,
194}
195
196impl ParameterPoint {
197 #[must_use]
199 pub const fn new(
200 temperature: Parameter,
201 humidity: Parameter,
202 continentalness: Parameter,
203 erosion: Parameter,
204 depth: Parameter,
205 weirdness: Parameter,
206 offset: i64,
207 ) -> Self {
208 Self {
209 temperature,
210 humidity,
211 continentalness,
212 erosion,
213 depth,
214 weirdness,
215 offset,
216 }
217 }
218
219 #[must_use]
223 #[expect(
224 clippy::many_single_char_names,
225 reason = "single-letter abbreviations match vanilla's climate parameter names"
226 )]
227 pub const fn fitness(&self, target: &TargetPoint) -> i64 {
228 let t = self.temperature.distance(target.temperature);
229 let h = self.humidity.distance(target.humidity);
230 let c = self.continentalness.distance(target.continentalness);
231 let e = self.erosion.distance(target.erosion);
232 let d = self.depth.distance(target.depth);
233 let w = self.weirdness.distance(target.weirdness);
234
235 t * t + h * h + c * c + e * e + d * d + w * w + self.offset * self.offset
237 }
238
239 #[must_use]
241 pub const fn parameter_space(&self) -> [Parameter; PARAMETER_COUNT] {
242 [
243 self.temperature,
244 self.humidity,
245 self.continentalness,
246 self.erosion,
247 self.depth,
248 self.weirdness,
249 Parameter::new(self.offset, self.offset),
250 ]
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_target_point_from_floats() {
260 let target = TargetPoint::from_floats(0.5, -0.3, 0.0, 0.1, 0.0, 0.2);
261 assert_eq!(target.temperature, 5000);
262 assert_eq!(target.humidity, -3000);
263 assert_eq!(target.continentalness, 0);
264 assert_eq!(target.erosion, 1000);
265 assert_eq!(target.depth, 0);
266 assert_eq!(target.weirdness, 2000);
267 }
268
269 #[test]
270 fn test_parameter_distance() {
271 let param = Parameter::new(-5000, 5000);
272
273 assert_eq!(param.distance(0), 0);
275 assert_eq!(param.distance(5000), 0);
276 assert_eq!(param.distance(-5000), 0);
277
278 assert_eq!(param.distance(6000), 1000);
280 assert_eq!(param.distance(-6000), 1000);
281 assert_eq!(param.distance(10000), 5000);
282 }
283
284 #[test]
285 fn test_parameter_point_fitness() {
286 let params = ParameterPoint::new(
287 Parameter::new(0, 0),
288 Parameter::new(0, 0),
289 Parameter::new(0, 0),
290 Parameter::new(0, 0),
291 Parameter::new(0, 0),
292 Parameter::new(0, 0),
293 0,
294 );
295
296 let target = TargetPoint::new(0, 0, 0, 0, 0, 0);
298 assert_eq!(params.fitness(&target), 0);
299
300 let target = TargetPoint::new(100, 0, 0, 0, 0, 0);
302 assert_eq!(params.fitness(&target), 100 * 100);
303
304 let target = TargetPoint::new(100, 100, 0, 0, 0, 0);
306 assert_eq!(params.fitness(&target), 100 * 100 + 100 * 100);
307 }
308}