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::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 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 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}