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