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