1use crate::town::Sim;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
14pub struct Tracker {
15 pub this_year: Vec<(usize, i32)>,
17 pub last_year: Vec<(usize, i32)>,
19 pub year: u32,
21}
22
23impl Tracker {
24 pub fn add(&mut self, year: u32, material: usize, amount: i32) {
29 if year != self.year {
30 self.last_year = std::mem::take(&mut self.this_year);
31 self.year = year;
32 }
33 if let Some(e) = self.this_year.iter_mut().find(|e| e.0 == material) {
34 e.1 += amount;
35 return;
36 }
37 self.this_year.push((material, amount));
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct StockCounters {
45 pub used: [i32; 21],
47 pub made: [i32; 21],
49}
50
51impl Default for StockCounters {
52 fn default() -> Self {
53 StockCounters {
54 used: [0; 21],
55 made: [0; 21],
56 }
57 }
58}
59
60impl Sim<'_, '_> {
61 pub(crate) fn count_made(
64 &mut self,
65 owner: Option<usize>,
66 update: bool,
67 material: usize,
68 n: i32,
69 ) {
70 if let (Some(o), true) = (owner, update) {
71 let year = self.year;
72 if let Some(t) = self.trackers.get_mut(&o) {
73 t.add(year, material, n);
74 }
75 }
76 if let Some(slot) = crate::work::produce::production_group(self.materials[material].flags) {
77 self.counters.made[slot] += n;
78 }
79 }
80
81 pub(crate) fn count_used(&mut self, material: usize, n: i32) {
84 if let Some(slot) = crate::work::produce::production_group(self.materials[material].flags) {
85 self.counters.used[slot] += n;
86 }
87 }
88}
89
90pub const SERIES: usize = 8 + 3 * 21;
94pub const SAMPLE_SECONDS: f32 = 75.0;
98
99#[derive(Debug, Clone, Copy, Default, PartialEq)]
103pub struct CitizenSample {
104 pub adult: bool,
106 pub school: bool,
108 pub health: f32,
110 pub happiness: f32,
112 pub educated: bool,
114 pub coat: bool,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct Statistics {
122 pub series: Vec<Vec<i32>>,
124 pub timer: i64,
126}
127
128impl Default for Statistics {
129 fn default() -> Self {
130 Statistics {
131 series: vec![Vec::new(); SERIES],
132 timer: 0,
133 }
134 }
135}
136
137fn truncate(v: f32) -> i32 {
139 if v.is_nan() || v >= 2_147_483_648.0 || v < -2_147_483_648.0 {
140 i32::MIN
141 } else {
142 v as i32
143 }
144}
145
146impl Statistics {
147 pub fn update(
151 &mut self,
152 dt: i64,
153 citizens: &[CitizenSample],
154 health_max: f32,
155 happiness_max: f32,
156 stock: &[i32; 21],
157 counters: &mut StockCounters,
158 ) -> bool {
159 self.timer -= dt;
160 if 0 < self.timer {
161 return false;
162 }
163 self.sample(citizens, health_max, happiness_max, stock, counters);
164 self.timer = crate::scheduler::ticks(SAMPLE_SECONDS);
165 true
166 }
167
168 pub fn sample(
174 &mut self,
175 citizens: &[CitizenSample],
176 health_max: f32,
177 happiness_max: f32,
178 stock: &[i32; 21],
179 counters: &mut StockCounters,
180 ) {
181 let (mut adults, mut students, mut children) = (0, 0, 0);
182 let (mut health, mut happiness) = (0.0f32, 0.0f32);
183 let (mut educated, mut coats) = (0, 0);
184 for c in citizens {
185 if !c.adult || c.school {
186 if c.school {
187 students += 1;
188 } else {
189 children += 1;
190 }
191 } else {
192 adults += 1;
193 }
194 health += c.health;
195 happiness += c.happiness;
196 if c.educated && c.adult && !c.school {
197 educated += 1;
198 }
199 if c.coat {
200 coats += 1;
201 }
202 }
203 let (mut h, mut j, mut e, mut k) = (0, 0, 0, 0);
204 if !citizens.is_empty() {
205 let n = citizens.len() as f32;
206 h = truncate(((health / n) / health_max) * 100.0);
207 j = truncate(((happiness / n) / happiness_max) * 100.0);
208 e = truncate((educated as f32 / adults as f32) * 100.0);
209 k = truncate((coats as f32 / n) * 100.0);
210 }
211 let people = [
212 citizens.len() as i32,
213 adults,
214 students,
215 children,
216 h,
217 e,
218 j,
219 k,
220 ];
221 for (s, v) in self.series.iter_mut().zip(people) {
222 s.push(v);
223 }
224 for slot in 0..21 {
225 self.series[8 + 3 * slot].push(stock[slot]);
226 self.series[9 + 3 * slot].push(counters.used[slot]);
227 self.series[10 + 3 * slot].push(counters.made[slot]);
228 }
229 *counters = StockCounters::default();
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn a_tracker_keeps_this_year_and_last() {
239 let mut t = Tracker::default();
240 t.add(0, 3, 5);
241 t.add(0, 3, 2);
242 t.add(0, 4, 1);
243 assert_eq!(t.this_year, vec![(3, 7), (4, 1)]);
244 t.add(1, 4, 9);
245 assert_eq!(t.last_year, vec![(3, 7), (4, 1)]);
246 assert_eq!(t.this_year, vec![(4, 9)]);
247 }
248
249 #[test]
250 fn a_sample_counts_the_town() {
251 let adult = CitizenSample {
252 adult: true,
253 health: 2.0,
254 happiness: 1.0,
255 educated: true,
256 coat: true,
257 ..Default::default()
258 };
259 let child = CitizenSample {
260 health: 2.0,
261 happiness: 1.0,
262 ..Default::default()
263 };
264 let student = CitizenSample {
265 school: true,
266 ..child
267 };
268 let mut stats = Statistics::default();
269 let mut counters = StockCounters::default();
270 counters.made[2] = 7;
271 counters.used[2] = 3;
272 let stock = [1; 21];
273 assert!(stats.update(
274 1,
275 &[adult, child, student, adult],
276 4.0,
277 2.0,
278 &stock,
279 &mut counters
280 ));
281 let last: Vec<i32> = stats.series.iter().map(|s| *s.last().unwrap()).collect();
282 assert_eq!(&last[..8], &[4, 2, 1, 1, 50, 100, 50, 50]);
283 assert_eq!(&last[8 + 6..8 + 9], &[1, 3, 7]);
284 assert_eq!(counters, StockCounters::default());
285 assert!(!stats.update(
287 crate::scheduler::ticks(74.0),
288 &[],
289 4.0,
290 2.0,
291 &stock,
292 &mut counters
293 ));
294 assert!(stats.update(
295 crate::scheduler::ticks(1.0),
296 &[],
297 4.0,
298 2.0,
299 &stock,
300 &mut counters
301 ));
302 assert_eq!(stats.series[4], vec![50, 0]);
303 }
304}