1use crate::military::army::Army;
5use crate::military::squad::Squad;
6use crate::military::squad::size::SquadSize;
7use crate::military::unit::stats::haul::Haul;
8use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
9use crate::military::unit::stats::speed::Speed;
10use crate::military::unit::{UnitId, UnitIdIter};
11use crate::ranking::score::Score;
12use crate::resources::maintenance::Maintenance;
13use crate::world::config::WorldConfig;
14use serde::{Deserialize, Serialize};
15use std::iter::Sum;
16use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
17use strum::IntoEnumIterator;
18use tap::Pipe;
19
20#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
21#[serde(rename_all = "camelCase")]
22#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
23pub struct ArmyPersonnel {
24 archer: Squad,
25 axeman: Squad,
26 heavy_cavalry: Squad,
27 light_cavalry: Squad,
28 pikeman: Squad,
29 ram: Squad,
30 swordsman: Squad,
31}
32
33#[bon::bon]
34impl ArmyPersonnel {
35 #[builder]
36 pub fn new(
37 #[builder(default, into)] archer: SquadSize,
38 #[builder(default, into)] axeman: SquadSize,
39 #[builder(default, into)] heavy_cavalry: SquadSize,
40 #[builder(default, into)] light_cavalry: SquadSize,
41 #[builder(default, into)] pikeman: SquadSize,
42 #[builder(default, into)] ram: SquadSize,
43 #[builder(default, into)] swordsman: SquadSize,
44 ) -> Self {
45 use UnitId::*;
46 Self {
47 archer: Squad::new(Archer, archer),
48 axeman: Squad::new(Axeman, axeman),
49 heavy_cavalry: Squad::new(HeavyCavalry, heavy_cavalry),
50 light_cavalry: Squad::new(LightCavalry, light_cavalry),
51 pikeman: Squad::new(Pikeman, pikeman),
52 ram: Squad::new(Ram, ram),
53 swordsman: Squad::new(Swordsman, swordsman),
54 }
55 }
56
57 pub fn to_vec(self) -> Vec<Squad> {
58 Vec::<Squad>::from(self)
59 }
60
61 #[inline]
62 pub fn iter(&self) -> ArmyPersonnelIter<'_> {
63 ArmyPersonnelIter::new(self)
64 }
65
66 pub fn slowest_squad(&self, config: &WorldConfig) -> Option<&Squad> {
67 self
68 .iter()
69 .filter(|squad| squad.size() > 0u32)
70 .min_by(|a, b| a.speed(config).total_cmp(&b.speed(config)))
71 }
72
73 pub fn speed(&self, config: &WorldConfig) -> Speed {
74 self
75 .slowest_squad(config)
76 .map(|squad| squad.speed(config))
77 .unwrap_or_default()
78 }
79
80 #[inline]
81 pub fn haul(&self) -> Haul {
82 self.iter().sum()
83 }
84
85 #[inline]
86 pub fn score(&self) -> Score {
87 self.iter().sum()
88 }
89
90 #[inline]
91 pub fn maintenance(&self) -> Maintenance {
92 self.iter().sum()
93 }
94
95 #[inline]
96 pub fn power(&self) -> Power {
97 self.iter().sum()
98 }
99
100 #[inline]
101 pub fn attack(&self) -> AttackPower {
102 self.iter().sum()
103 }
104
105 #[inline]
106 pub fn defense(&self) -> DefensePower {
107 self.iter().sum()
108 }
109
110 #[inline]
111 pub fn is_empty(&self) -> bool {
112 self.iter().all(Squad::is_empty)
113 }
114}
115
116macro_rules! impl_army_personnel {
117 ($($unit:ident),+) => {
118 paste::paste! {
119 impl ArmyPersonnel {
120 pub fn splat(size: impl Into<SquadSize>) -> Self {
121 let size: SquadSize = size.into();
122 Self::builder()
123 $(.[<$unit:snake>](size))+
124 .build()
125 }
126
127 pub fn random() -> Self {
128 Self::builder()
129 $(.[<$unit:snake>](SquadSize::random()))+
130 .build()
131 }
132
133 pub fn squad(&self, id: UnitId) -> &Squad {
134 match id {
135 $(UnitId::$unit => &self.[<$unit:snake>],)+
136 }
137 }
138
139 pub(super) fn squad_mut(&mut self, id: UnitId) -> &mut Squad {
140 match id {
141 $(UnitId::$unit => &mut self.[<$unit:snake>],)+
142 }
143 }
144
145 $(
146 #[inline]
147 pub fn [<$unit:snake>](&self) -> &Squad {
148 &self.[<$unit:snake>]
149 }
150 )+
151
152 pub fn has_enough_personnel(&self, required: &ArmyPersonnel) -> bool {
153 $(self.[<$unit:snake>].size() >= required.[<$unit:snake>].size() && )+ true
154 }
155
156 pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
157 Self::builder()
158 $(
159 .[<$unit:snake>](
160 self
161 .[<$unit:snake>]
162 .size()
163 .checked_sub(rhs.[<$unit:snake>].size())?
164 )
165 )+
166 .build()
167 .pipe(Some)
168 }
169 }
170
171 impl From<ArmyPersonnel> for Vec<Squad> {
172 fn from(personnel: ArmyPersonnel) -> Self {
173 vec![$(personnel.[<$unit:snake>],)+]
174 }
175 }
176 }
177 };
178}
179
180impl_army_personnel!(
181 Archer,
182 Axeman,
183 HeavyCavalry,
184 LightCavalry,
185 Pikeman,
186 Ram,
187 Swordsman
188);
189
190impl Default for ArmyPersonnel {
191 fn default() -> Self {
192 Self::builder().build()
193 }
194}
195
196impl From<Army> for ArmyPersonnel {
197 fn from(army: Army) -> Self {
198 army.personnel
199 }
200}
201
202impl From<SquadSize> for ArmyPersonnel {
203 fn from(size: SquadSize) -> Self {
204 ArmyPersonnel::splat(size)
205 }
206}
207
208impl FromIterator<Squad> for ArmyPersonnel {
209 fn from_iter<T>(iter: T) -> Self
210 where
211 T: IntoIterator<Item = Squad>,
212 {
213 iter
214 .into_iter()
215 .fold(Self::default(), |mut personnel, squad| {
216 personnel += squad;
217 personnel
218 })
219 }
220}
221
222impl<'a> IntoIterator for &'a ArmyPersonnel {
223 type Item = &'a Squad;
224 type IntoIter = ArmyPersonnelIter<'a>;
225
226 fn into_iter(self) -> Self::IntoIter {
227 self.iter()
228 }
229}
230
231impl<'a> Sum<&'a ArmyPersonnel> for ArmyPersonnel {
232 fn sum<I>(iter: I) -> Self
233 where
234 I: Iterator<Item = &'a ArmyPersonnel>,
235 {
236 iter.fold(ArmyPersonnel::default(), |mut acc, personnel| {
237 acc += personnel;
238 acc
239 })
240 }
241}
242
243impl<'a> Sum<&'a Army> for ArmyPersonnel {
244 fn sum<I>(iter: I) -> Self
245 where
246 I: Iterator<Item = &'a Army>,
247 {
248 iter.map(Army::personnel).sum()
249 }
250}
251
252impl Add for ArmyPersonnel {
253 type Output = ArmyPersonnel;
254
255 fn add(mut self, rhs: Self) -> Self::Output {
256 self += &rhs;
257 self
258 }
259}
260
261impl Add<Squad> for ArmyPersonnel {
262 type Output = ArmyPersonnel;
263
264 fn add(mut self, rhs: Squad) -> Self::Output {
265 self += rhs;
266 self
267 }
268}
269
270impl AddAssign for ArmyPersonnel {
271 fn add_assign(&mut self, rhs: Self) {
272 *self += &rhs;
273 }
274}
275
276impl AddAssign<&ArmyPersonnel> for ArmyPersonnel {
277 fn add_assign(&mut self, rhs: &ArmyPersonnel) {
278 for squad in rhs {
279 *self.squad_mut(squad.id()) += squad.size();
280 }
281 }
282}
283
284impl AddAssign<Squad> for ArmyPersonnel {
285 fn add_assign(&mut self, rhs: Squad) {
286 *self.squad_mut(rhs.id()) += rhs;
287 }
288}
289
290impl Sub for ArmyPersonnel {
291 type Output = ArmyPersonnel;
292
293 fn sub(mut self, rhs: Self) -> Self::Output {
294 self -= rhs;
295 self
296 }
297}
298
299impl SubAssign for ArmyPersonnel {
300 fn sub_assign(&mut self, rhs: Self) {
301 for squad in &rhs {
302 *self.squad_mut(squad.id()) -= squad.size();
303 }
304 }
305}
306
307impl SubAssign<Squad> for ArmyPersonnel {
308 fn sub_assign(&mut self, rhs: Squad) {
309 *self.squad_mut(rhs.id()) -= rhs;
310 }
311}
312
313impl Mul<f64> for ArmyPersonnel {
314 type Output = ArmyPersonnel;
315
316 fn mul(mut self, rhs: f64) -> Self::Output {
317 self *= rhs;
318 self
319 }
320}
321
322impl MulAssign<f64> for ArmyPersonnel {
323 fn mul_assign(&mut self, rhs: f64) {
324 for id in UnitId::iter() {
325 *self.squad_mut(id) *= rhs;
326 }
327 }
328}
329
330pub struct ArmyPersonnelIter<'a> {
331 personnel: &'a ArmyPersonnel,
332 units: UnitIdIter,
333}
334
335impl<'a> ArmyPersonnelIter<'a> {
336 pub fn new(personnel: &'a ArmyPersonnel) -> Self {
337 Self { personnel, units: UnitId::iter() }
338 }
339}
340
341impl<'a> Iterator for ArmyPersonnelIter<'a> {
342 type Item = &'a Squad;
343
344 fn next(&mut self) -> Option<Self::Item> {
345 let id = self.units.next()?;
346 Some(self.personnel.squad(id))
347 }
348}