1#[cfg(test)]
5mod tests;
6
7pub mod army;
8pub mod maneuver;
9pub mod squad;
10pub mod unit;
11
12use crate::continent::coord::Coord;
13use crate::continent::index::{ContinentIndex, ContinentKey};
14use crate::continent::size::ContinentSize;
15use crate::error::{Error, Result};
16use crate::military::army::personnel::ArmyPersonnel;
17use crate::military::army::{Army, ArmyId, collapse_armies};
18use crate::military::maneuver::{Maneuver, ManeuverId};
19use crate::military::squad::Squad;
20use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
21use crate::ranking::score::Score;
22use crate::resources::maintenance::Maintenance;
23use crate::ruler::Ruler;
24use itertools::Itertools;
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27
28#[derive(Clone, Debug, Deserialize, Serialize)]
29#[serde(rename_all = "camelCase")]
30#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
31pub struct Military {
32 continent: HashMap<ContinentIndex, Vec<Army>>,
33 continent_size: ContinentSize,
34 maneuvers: HashMap<ManeuverId, Maneuver>,
35}
36
37impl Military {
38 pub(crate) fn new(size: ContinentSize) -> Self {
39 Self {
40 continent: HashMap::new(),
41 continent_size: size,
42 maneuvers: HashMap::new(),
43 }
44 }
45
46 pub(crate) fn spawn<K, R>(&mut self, key: K, owner: R, personnel: ArmyPersonnel)
47 where
48 K: ContinentKey,
49 R: Into<Ruler>,
50 {
51 let ruler: Ruler = owner.into();
52 let army = Army::builder()
53 .owner(ruler)
54 .personnel(personnel)
55 .build();
56
57 let index = key.into_index(self.continent_size);
58 self
59 .continent
60 .entry(index)
61 .or_default()
62 .push(army);
63
64 self.collapse_armies_in(index);
65 }
66
67 pub fn collapse_armies(&mut self) {
68 self
69 .continent
70 .values_mut()
71 .for_each(collapse_armies);
72
73 self
74 .continent
75 .retain(|_, armies| !armies.is_empty());
76 }
77
78 pub fn collapse_armies_in<K>(&mut self, key: K)
79 where
80 K: ContinentKey,
81 {
82 let index = key.into_index(self.continent_size);
83 if let Some(armies) = self.continent.get_mut(&index) {
84 collapse_armies(armies);
85 }
86 }
87
88 pub fn intersection<K, I>(&self, keys: I) -> Result<Self>
90 where
91 K: ContinentKey,
92 I: IntoIterator<Item = K>,
93 {
94 let mut military = Self::new(self.continent_size);
95 for key in keys {
96 let coord = key.into_coord(self.continent_size)?;
97 let index = coord.into_index(self.continent_size);
98 if let Some(armies) = self.continent.get(&index).cloned() {
99 military.continent.insert(index, armies);
100 }
101
102 let maneuvers = self
103 .maneuvers_at(coord)
104 .map(|maneuver| (maneuver.id(), maneuver.clone()));
105
106 military.maneuvers.extend(maneuvers);
107 }
108
109 Ok(military)
110 }
111
112 pub(crate) fn remove_army(&mut self, id: ArmyId) -> Result<Army> {
113 let (curr_vec, pos) = self
114 .continent
115 .values_mut()
116 .find_map(|armies| {
117 armies
118 .iter()
119 .position(|army| army.id() == id)
120 .map(|pos| (armies, pos))
121 })
122 .ok_or(Error::ArmyNotFound(id))?;
123
124 Ok(curr_vec.swap_remove(pos))
125 }
126
127 pub(crate) fn remove_armies<I>(&mut self, armies: I) -> Result<Vec<Army>>
128 where
129 I: IntoIterator<Item = ArmyId>,
130 {
131 armies
132 .into_iter()
133 .map(|id| self.remove_army(id))
134 .try_collect()
135 }
136
137 pub(crate) fn relocate_army<K>(&mut self, id: ArmyId, new_key: K) -> Result<()>
138 where
139 K: ContinentKey,
140 {
141 let army = self.remove_army(id)?;
142 let index = new_key.into_index(self.continent_size);
143 self
144 .continent
145 .entry(index)
146 .or_default()
147 .push(army);
148
149 self.collapse_armies_in(index);
150
151 Ok(())
152 }
153
154 pub fn continent(&self) -> impl Iterator<Item = (ContinentIndex, &[Army])> {
155 self
156 .continent
157 .iter()
158 .map(|(index, armies)| (*index, armies.as_slice()))
159 }
160
161 pub fn army(&self, id: ArmyId) -> Result<&Army> {
162 self
163 .armies()
164 .find(|army| army.id() == id)
165 .ok_or(Error::ArmyNotFound(id))
166 }
167
168 pub(crate) fn army_mut(&mut self, id: ArmyId) -> Result<&mut Army> {
169 self
170 .armies_mut()
171 .find(|army| army.id() == id)
172 .ok_or(Error::ArmyNotFound(id))
173 }
174
175 pub fn armies(&self) -> impl Iterator<Item = &Army> {
176 self.continent.values().flatten()
177 }
178
179 pub(crate) fn armies_mut(&mut self) -> impl Iterator<Item = &mut Army> {
180 self.continent.values_mut().flatten()
181 }
182
183 pub fn armies_at<K>(&self, key: K) -> &[Army]
184 where
185 K: ContinentKey,
186 {
187 let index = key.into_index(self.continent_size);
188 self
189 .continent
190 .get(&index)
191 .map(Vec::as_slice)
192 .unwrap_or_default()
193 }
194
195 pub(crate) fn armies_mut_at<K>(&mut self, key: K) -> &mut [Army]
196 where
197 K: ContinentKey,
198 {
199 let index = key.into_index(self.continent_size);
200 self
201 .continent
202 .get_mut(&index)
203 .map(Vec::as_mut_slice)
204 .unwrap_or_default()
205 }
206
207 pub fn armies_of<R>(&self, owner: R) -> impl Iterator<Item = &Army>
208 where
209 R: Into<Ruler>,
210 {
211 let owner: Ruler = owner.into();
212 self
213 .continent
214 .values()
215 .flatten()
216 .filter(move |army| army.is_owned_by(&owner))
217 }
218
219 pub fn indexed_armies_of<R>(&self, owner: R) -> Vec<(ContinentIndex, &Army)>
220 where
221 R: Into<Ruler>,
222 {
223 let owner: Ruler = owner.into();
224 let mut owner_armies = Vec::new();
225
226 for (index, armies) in &self.continent {
227 for army in armies {
228 if army.is_owned_by(&owner) {
229 owner_armies.push((*index, army));
230 }
231 }
232 }
233
234 owner_armies.sort_by_key(|(index, _)| *index);
235 owner_armies
236 }
237
238 #[inline]
239 pub fn count_armies(&self) -> usize {
240 self.armies().count()
241 }
242
243 pub fn count_armies_at<K>(&self, key: K) -> usize
244 where
245 K: ContinentKey,
246 {
247 self.armies_at(key).iter().count()
248 }
249
250 pub fn idle_armies_at<K>(&self, key: K) -> impl Iterator<Item = &Army>
251 where
252 K: ContinentKey,
253 {
254 self
255 .armies_at(key)
256 .iter()
257 .filter(|army| army.is_idle())
258 }
259
260 pub(crate) fn idle_armies_mut_at<K>(&mut self, key: K) -> impl Iterator<Item = &mut Army>
261 where
262 K: ContinentKey,
263 {
264 self
265 .armies_mut_at(key)
266 .iter_mut()
267 .filter(|army| army.is_idle())
268 }
269
270 #[inline]
271 pub fn personnel(&self, id: ArmyId) -> Result<&ArmyPersonnel> {
272 self.army(id).map(Army::personnel)
273 }
274
275 pub fn personnel_at<K>(&self, key: K) -> impl Iterator<Item = &ArmyPersonnel>
276 where
277 K: ContinentKey,
278 {
279 self
280 .armies_at(key)
281 .iter()
282 .map(Army::personnel)
283 }
284
285 pub fn fold_personnel_at<K>(&self, key: K) -> ArmyPersonnel
286 where
287 K: ContinentKey,
288 {
289 self.personnel_at(key).sum()
290 }
291
292 pub fn personnel_of<R>(&self, owner: R) -> impl Iterator<Item = &ArmyPersonnel>
293 where
294 R: Into<Ruler>,
295 {
296 self.armies_of(owner).map(Army::personnel)
297 }
298
299 pub fn fold_personnel_of<R>(&self, owner: R) -> ArmyPersonnel
300 where
301 R: Into<Ruler>,
302 {
303 self.personnel_of(owner).sum()
304 }
305
306 pub fn idle_personnel_at<K>(&self, key: K) -> impl Iterator<Item = &ArmyPersonnel>
307 where
308 K: ContinentKey,
309 {
310 self.idle_armies_at(key).map(Army::personnel)
311 }
312
313 pub fn fold_idle_personnel_at<K>(&self, key: K) -> ArmyPersonnel
314 where
315 K: ContinentKey,
316 {
317 self.idle_personnel_at(key).sum()
318 }
319
320 #[inline]
321 pub fn squads(&self, id: ArmyId) -> Result<Vec<Squad>> {
322 self
323 .personnel(id)
324 .cloned()
325 .map(ArmyPersonnel::to_vec)
326 }
327
328 pub fn idle_squads_at<K>(&self, key: K) -> Vec<Squad>
329 where
330 K: ContinentKey,
331 {
332 self.fold_idle_personnel_at(key).to_vec()
333 }
334
335 #[inline]
336 pub fn maneuver(&self, id: ManeuverId) -> Result<&Maneuver> {
337 self
338 .maneuvers
339 .get(&id)
340 .ok_or(Error::ManeuverNotFound(id))
341 }
342
343 fn maneuver_mut(&mut self, id: ManeuverId) -> Result<&mut Maneuver> {
344 self
345 .maneuvers
346 .get_mut(&id)
347 .ok_or(Error::ManeuverNotFound(id))
348 }
349
350 pub fn maneuvers(&self) -> impl Iterator<Item = &Maneuver> {
351 self.maneuvers.values()
352 }
353
354 pub fn maneuvers_at(&self, coord: Coord) -> impl Iterator<Item = &Maneuver> {
356 self
357 .maneuvers()
358 .filter(move |maneuver| maneuver.matches_coord(coord))
359 }
360
361 #[inline]
362 pub fn has_maneuver(&self, id: ManeuverId) -> bool {
363 self.maneuvers.contains_key(&id)
364 }
365
366 pub(crate) fn insert_maneuver(&mut self, maneuver: Maneuver) {
367 self
368 .maneuvers
369 .insert(maneuver.id(), maneuver);
370 }
371
372 pub(crate) fn advance_maneuvers(&mut self) -> Result<Vec<Maneuver>> {
373 let mut done = Vec::new();
374 for (id, maneuver) in &mut self.maneuvers {
375 maneuver.advance()?;
376
377 if maneuver.is_done() {
378 done.push(*id);
379 }
380 }
381
382 let done = done
383 .into_iter()
384 .filter_map(|id| self.maneuvers.remove(&id))
385 .collect_vec();
386
387 self.maneuvers.shrink_to_fit();
388
389 Ok(done)
390 }
391
392 pub(crate) fn cancel_maneuver(&mut self, id: ManeuverId) -> Result<()> {
393 self.maneuver_mut(id)?.cancel()
394 }
395
396 pub(crate) fn retain_maneuvers<F>(&mut self, f: F)
398 where
399 F: Fn(&Maneuver) -> bool,
400 {
401 self
402 .maneuvers
403 .retain(|_, maneuver| f(maneuver));
404 }
405
406 pub fn score_of<R>(&self, owner: R) -> Score
407 where
408 R: Into<Ruler>,
409 {
410 self.armies_of(owner).sum()
411 }
412
413 pub fn maintenance_of<R>(&self, owner: R) -> Maintenance
414 where
415 R: Into<Ruler>,
416 {
417 self.armies_of(owner).sum()
418 }
419
420 pub fn power_of<R>(&self, owner: R) -> Power
421 where
422 R: Into<Ruler>,
423 {
424 self.armies_of(owner).sum()
425 }
426
427 pub fn attack_of<R>(&self, owner: R) -> AttackPower
428 where
429 R: Into<Ruler>,
430 {
431 self.armies_of(owner).sum()
432 }
433
434 pub fn defense_of<R>(&self, owner: R) -> DefensePower
435 where
436 R: Into<Ruler>,
437 {
438 self.armies_of(owner).sum()
439 }
440}