Skip to main content

nil_core/military/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4#[cfg(test)]
5mod tests;
6
7pub mod army;
8pub mod maneuver;
9pub mod squad;
10pub mod unit;
11
12use crate::continent::{ContinentIndex, ContinentKey, ContinentSize, Coord};
13use crate::error::{Error, Result};
14use crate::ranking::score::Score;
15use crate::resources::maintenance::Maintenance;
16use crate::ruler::Ruler;
17use army::personnel::ArmyPersonnel;
18use army::{Army, ArmyId, collapse_armies};
19use itertools::Itertools;
20use maneuver::{Maneuver, ManeuverId};
21use serde::{Deserialize, Serialize};
22use squad::Squad;
23use std::collections::HashMap;
24use tap::Pipe;
25
26#[derive(Clone, Debug, Deserialize, Serialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Military {
29  continent: HashMap<ContinentIndex, Vec<Army>>,
30  continent_size: ContinentSize,
31  maneuvers: HashMap<ManeuverId, Maneuver>,
32}
33
34impl Military {
35  pub(crate) fn new(size: ContinentSize) -> Self {
36    Self {
37      continent: HashMap::new(),
38      continent_size: size,
39      maneuvers: HashMap::new(),
40    }
41  }
42
43  pub(crate) fn spawn<K, R>(&mut self, key: K, owner: R, personnel: ArmyPersonnel)
44  where
45    K: ContinentKey,
46    R: Into<Ruler>,
47  {
48    let ruler: Ruler = owner.into();
49    let army = Army::builder()
50      .owner(ruler)
51      .personnel(personnel)
52      .build();
53
54    let index = key.into_index(self.continent_size);
55    self
56      .continent
57      .entry(index)
58      .or_default()
59      .push(army);
60
61    self.collapse_armies_in(index);
62  }
63
64  pub fn collapse_armies(&mut self) {
65    self
66      .continent
67      .values_mut()
68      .for_each(collapse_armies);
69
70    self
71      .continent
72      .retain(|_, armies| !armies.is_empty());
73  }
74
75  pub fn collapse_armies_in<K>(&mut self, key: K)
76  where
77    K: ContinentKey,
78  {
79    let index = key.into_index(self.continent_size);
80    if let Some(armies) = self.continent.get_mut(&index) {
81      collapse_armies(armies);
82    }
83  }
84
85  /// Creates a new instance containing only entries related to a given set of coords.
86  pub fn intersection<K, I>(&self, keys: I) -> Result<Self>
87  where
88    K: ContinentKey,
89    I: IntoIterator<Item = K>,
90  {
91    let mut military = Self::new(self.continent_size);
92    for key in keys {
93      let coord = key.into_coord(self.continent_size)?;
94      let index = coord.into_index(self.continent_size);
95      if let Some(armies) = self.continent.get(&index).cloned() {
96        military.continent.insert(index, armies);
97      }
98
99      let maneuvers = self
100        .maneuvers_at(coord)
101        .map(|maneuver| (maneuver.id(), maneuver.clone()));
102
103      military.maneuvers.extend(maneuvers);
104    }
105
106    Ok(military)
107  }
108
109  pub(crate) fn remove_army(&mut self, id: ArmyId) -> Result<Army> {
110    let (curr_vec, pos) = self
111      .continent
112      .values_mut()
113      .find_map(|armies| {
114        armies
115          .iter()
116          .position(|army| army.id() == id)
117          .map(|pos| (armies, pos))
118      })
119      .ok_or(Error::ArmyNotFound(id))?;
120
121    Ok(curr_vec.swap_remove(pos))
122  }
123
124  pub(crate) fn remove_armies<I>(&mut self, armies: I) -> Result<Vec<Army>>
125  where
126    I: IntoIterator<Item = ArmyId>,
127  {
128    armies
129      .into_iter()
130      .map(|id| self.remove_army(id))
131      .try_collect()
132  }
133
134  pub(crate) fn relocate_army<K>(&mut self, id: ArmyId, new_key: K) -> Result<()>
135  where
136    K: ContinentKey,
137  {
138    let army = self.remove_army(id)?;
139    let index = new_key.into_index(self.continent_size);
140    self
141      .continent
142      .entry(index)
143      .or_default()
144      .push(army);
145
146    self.collapse_armies_in(index);
147
148    Ok(())
149  }
150
151  pub fn army(&self, id: ArmyId) -> Result<&Army> {
152    self
153      .armies()
154      .find(|army| army.id() == id)
155      .ok_or(Error::ArmyNotFound(id))
156  }
157
158  pub(crate) fn army_mut(&mut self, id: ArmyId) -> Result<&mut Army> {
159    self
160      .armies_mut()
161      .find(|army| army.id() == id)
162      .ok_or(Error::ArmyNotFound(id))
163  }
164
165  pub fn armies(&self) -> impl Iterator<Item = &Army> {
166    self.continent.values().flatten()
167  }
168
169  pub(crate) fn armies_mut(&mut self) -> impl Iterator<Item = &mut Army> {
170    self.continent.values_mut().flatten()
171  }
172
173  #[inline]
174  pub fn count_armies(&self) -> usize {
175    self.armies().count()
176  }
177
178  pub fn armies_at<K>(&self, key: K) -> &[Army]
179  where
180    K: ContinentKey,
181  {
182    let index = key.into_index(self.continent_size);
183    self
184      .continent
185      .get(&index)
186      .map(Vec::as_slice)
187      .unwrap_or_default()
188  }
189
190  pub(crate) fn armies_mut_at<K>(&mut self, key: K) -> &mut [Army]
191  where
192    K: ContinentKey,
193  {
194    let index = key.into_index(self.continent_size);
195    self
196      .continent
197      .get_mut(&index)
198      .map(Vec::as_mut_slice)
199      .unwrap_or_default()
200  }
201
202  pub fn count_armies_at<K>(&self, key: K) -> usize
203  where
204    K: ContinentKey,
205  {
206    self.armies_at(key).iter().count()
207  }
208
209  pub fn idle_armies_at<K>(&self, key: K) -> impl Iterator<Item = &Army>
210  where
211    K: ContinentKey,
212  {
213    self
214      .armies_at(key)
215      .iter()
216      .filter(|army| army.is_idle())
217  }
218
219  pub(crate) fn idle_armies_mut_at<K>(&mut self, key: K) -> impl Iterator<Item = &mut Army>
220  where
221    K: ContinentKey,
222  {
223    self
224      .armies_mut_at(key)
225      .iter_mut()
226      .filter(|army| army.is_idle())
227  }
228
229  pub fn armies_of<R>(&self, owner: R) -> impl Iterator<Item = &Army>
230  where
231    R: Into<Ruler>,
232  {
233    let owner: Ruler = owner.into();
234    self
235      .continent
236      .values()
237      .flatten()
238      .filter(move |army| army.is_owned_by(&owner))
239  }
240
241  #[inline]
242  pub fn personnel(&self, id: ArmyId) -> Result<&ArmyPersonnel> {
243    self.army(id).map(Army::personnel)
244  }
245
246  #[inline]
247  pub fn squads(&self, id: ArmyId) -> Result<Vec<Squad>> {
248    self
249      .personnel(id)
250      .cloned()
251      .map(ArmyPersonnel::to_vec)
252  }
253
254  pub fn fold_idle_personnel_at<K>(&self, key: K) -> ArmyPersonnel
255  where
256    K: ContinentKey,
257  {
258    self
259      .idle_armies_at(key)
260      .map(Army::personnel)
261      .fold(ArmyPersonnel::default(), |mut acc, personnel| {
262        acc += personnel;
263        acc
264      })
265  }
266
267  pub fn idle_squads_at<K>(&self, key: K) -> Vec<Squad>
268  where
269    K: ContinentKey,
270  {
271    self.fold_idle_personnel_at(key).to_vec()
272  }
273
274  pub fn score_of<R>(&self, owner: R) -> Score
275  where
276    R: Into<Ruler>,
277  {
278    self
279      .armies_of(owner)
280      .fold(Score::default(), |mut score, army| {
281        score += army.score();
282        score
283      })
284  }
285
286  pub fn maintenance_of<R>(&self, owner: R) -> Maintenance
287  where
288    R: Into<Ruler>,
289  {
290    self
291      .armies_of(owner)
292      .fold(Maintenance::default(), |mut maintenance, army| {
293        maintenance += army.maintenance();
294        maintenance
295      })
296  }
297
298  #[inline]
299  pub fn maneuver(&self, id: ManeuverId) -> Result<&Maneuver> {
300    self
301      .maneuvers
302      .get(&id)
303      .ok_or(Error::ManeuverNotFound(id))
304  }
305
306  pub fn maneuvers(&self) -> impl Iterator<Item = &Maneuver> {
307    self.maneuvers.values()
308  }
309
310  /// Find all maneuvers whose origin or destination matches the specified coord.
311  pub fn maneuvers_at(&self, coord: Coord) -> impl Iterator<Item = &Maneuver> {
312    self
313      .maneuvers()
314      .filter(move |maneuver| maneuver.matches_coord(coord))
315  }
316
317  pub(crate) fn insert_maneuver(&mut self, maneuver: Maneuver) {
318    self
319      .maneuvers
320      .insert(maneuver.id(), maneuver);
321  }
322
323  pub(crate) fn advance_maneuvers(&mut self) -> Result<Vec<Maneuver>> {
324    let mut done = Vec::new();
325    for (id, maneuver) in &mut self.maneuvers {
326      maneuver.advance()?;
327
328      if maneuver.is_done() {
329        done.push(*id);
330      }
331    }
332
333    done
334      .into_iter()
335      .filter_map(|id| self.maneuvers.remove(&id))
336      .collect_vec()
337      .pipe(Ok)
338  }
339}