genius_invokation/
deck.rs

1use crate::{
2    PlayingCard, CharacterCard, ActionCard, Card, Element,
3    EquipmentCard, TalentCard, 
4    EventCard, ElementalResonanceCard,
5};
6use super::cards::CardOrd;
7
8/// A deck for Genius Invokation TCG
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Deck {
11    characters: [CharacterCard; 3],
12    actions: [ActionCard; 30],
13}
14
15impl Deck {
16    pub fn has_character(&self, card: CharacterCard) -> bool {
17        let x = self.characters;
18
19        card == x[0] || card == x[1] || card == x[2]
20    }
21
22    pub fn contains(&self, card: ActionCard) -> bool {
23        for i in 0..30 {
24            if card == self.actions[i] { return true }
25        }
26
27        false
28    }
29
30    pub fn iter(&self) -> IterAction<'_> {
31        IterAction { array: &self.actions, index: 0, unique: false }
32    }
33
34    pub fn iter_unique(&self) -> IterAction<'_> {
35        IterAction { array: &self.actions, index: 0, unique: true }
36    }
37
38    pub fn from_iter(iter: impl IntoIterator<Item=Card>) -> Result<Self, DeckError> {
39        let mut cards = iter.into_iter();
40
41        let mut char_vec   = Vec::with_capacity(3);
42        let mut action_vec = Vec::with_capacity(30);
43
44        while let Some(card) = cards.next() {
45            match card {
46                Card::Character(character) => {
47                    if char_vec.len() == 3 {
48                        return Err(DeckError::TooManyCharacterCards)
49                    } else {
50                        char_vec.push(character)
51                    }
52                },
53                Card::Action(action) => {
54                    if action_vec.len() == 30 {
55                        return Err(DeckError::TooManyActionCards)
56                    } else {
57                        action_vec.push(action)
58                    }
59                }
60            }
61        }
62
63        if char_vec.len() != 3 { return   Err(DeckError::NotEnoughCharacterCards(char_vec.len() as u8)) }
64        if action_vec.len() != 30 { return Err(DeckError::NotEnoughActionCards(action_vec.len() as u8)) }
65
66        char_vec.sort_by(|a, b| a.cmp(b));
67        action_vec.sort_by(|a, b| a.cmp(b));
68
69        let characters = <[CharacterCard; 3]>::try_from(char_vec).unwrap();
70        let actions    = <[ActionCard; 30]>::try_from(action_vec).unwrap();
71
72        Deck::verify(&characters, &actions)?;
73
74        Ok(Self { characters, actions })
75    }
76
77    pub fn from_exact(mut characters: [CharacterCard; 3], mut actions: [ActionCard; 30]) -> Result<Self, DeckError> {
78        characters.sort_by(|a, b| a.cmp(b));
79        actions.sort_by(|a, b| a.cmp(b));
80
81        Deck::verify(&characters, &actions)?;
82
83        Ok(Self { characters, actions })
84    }
85
86    fn verify(characters: &[CharacterCard; 3], actions: &[ActionCard; 30]) -> Result<(), DeckError> {
87        if characters[0] == characters[1] || characters[1] == characters[2] {
88            // we don't have to verify if characters[0] == characters[2] because the array
89            // is assumed to be sorted already
90            return Err(DeckError::CharacterAppearsMoreThanOnce(characters[1]))
91        }
92
93        for i in 0..28 {
94            // similarly, since actions is also sorted, we can check for triplicates by just
95            // checking for the same card 3 times in a row (duplicates are allowed!)
96            if actions[i] == actions[i+1] && actions[i+1] == actions[i+2] {
97                return Err(DeckError::ActionCardAppearsMoreThanTwice(actions[i]))
98            }
99        }
100
101        // Verifies if there's elemental resonance within the characters
102        // That is, if there are two or three characters of same element
103        let resonance: Option<Element> = {
104            let el1 = characters[0].element();
105            let el2 = characters[1].element();
106            let el3 = characters[2].element();
107
108            if el1 == el2 {
109                Some(el1)
110            } else if el2 == el3 {
111                Some(el2)
112            } else if el3 == el1 {
113                Some(el3)
114            } else {
115                None
116            }
117        };
118
119        for i in 0..30 {
120            if let ActionCard::Equipment(EquipmentCard::Talent(talent)) = actions[i] {
121                let required = talent.character();
122
123                if !characters.contains(&required) {
124                    return Err(DeckError::TalentRequiresCharacter(talent))
125                }
126            }
127
128            if let ActionCard::Event(EventCard::Resonance(elemental)) = actions[i] {
129                let required = elemental.element();
130
131                if resonance != Some(required) {
132                    return Err(DeckError::ResonanceRequiresAtLeastTwo(elemental))
133                }
134            }
135        }
136
137        Ok(())
138    }
139}
140
141#[derive(PartialEq, Eq)]
142pub enum DeckError {
143    /// Character cards > 3
144    TooManyCharacterCards,
145    /// Action cards > 30
146    TooManyActionCards,
147    /// Character cards < 3 (u8 represents current amount)
148    NotEnoughCharacterCards(u8),
149    /// Action cards < 30 (u8 represents current amount)
150    NotEnoughActionCards(u8),
151    /// Talent card is present, but its CharacterCard requirement is not
152    TalentRequiresCharacter(TalentCard),
153    /// Resonance card is present, but less than 2 characters that match its element
154    ResonanceRequiresAtLeastTwo(ElementalResonanceCard),
155    /// Deck must include three different character cards
156    CharacterAppearsMoreThanOnce(CharacterCard),
157    /// Only one or two of the same action card allowed for a deck
158    ActionCardAppearsMoreThanTwice(ActionCard),
159}
160
161use std::fmt;
162
163impl fmt::Display for DeckError {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        let errstr = match self {
166            Self::TalentRequiresCharacter(card) => {
167                let talent_name = card.name();
168                let char_name = card.character().name();
169
170                format!("`{talent_name}` requires `{char_name}` to be present in the deck")
171            },
172            Self::ResonanceRequiresAtLeastTwo(card) => {
173                let res_name = card.name();
174                let element = card.element();
175
176                format!("`{res_name}` requires at least two {element:?} characters in the deck")
177            }
178            Self::CharacterAppearsMoreThanOnce(card) => {
179                let char_name = card.name();
180
181                format!("deck contains more than one `{char_name}`")
182            },
183            Self::ActionCardAppearsMoreThanTwice(card) => {
184                let card_name = card.name();
185
186                format!("deck contains more than two `{card_name}`")
187            },
188            Self::TooManyCharacterCards => "deck has more than three character cards".into(),
189            Self::TooManyActionCards => "deck has more than 30 action cards".into(),
190            Self::NotEnoughCharacterCards(x) => {
191                format!("decks require 3 character cards, only retrieved {x}")
192            },
193            Self::NotEnoughActionCards(x) => {
194                format!("decks require 30 action cards, only retrieved {x}")
195            },
196        };
197        
198        write!(f, "{errstr}")
199    }
200}
201
202impl fmt::Debug for DeckError {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        fmt::Display::fmt(self, f)
205    }
206}
207
208impl std::error::Error for DeckError {}
209
210/// Helper struct for iterating over action cards in a [`Deck`]
211/// 
212/// Can only be created with the [`iter`] and [`iter_unique`] methods
213/// 
214/// [`iter`]: Deck::iter
215/// [`iter_unique`]: Deck::iter_unique
216pub struct IterAction<'d> {
217    array: &'d [ActionCard; 30],
218    index: usize,
219    unique: bool,
220}
221
222impl Iterator for IterAction<'_> {
223    type Item = ActionCard;
224
225    fn next(&mut self) -> Option<Self::Item> {
226        if self.index < 30 {
227            let card = self.array[self.index];
228
229            if self.unique && self.index < 29 {
230                if card == self.array[self.index + 1] {
231                    self.index += 2; // Assumes you can only have at most 2 cards
232                } else {
233                    self.index += 1;
234                }
235            } else {
236                self.index += 1;
237            }
238
239            Some(card)
240        } else {
241            None
242        }
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    #![allow(dead_code)]
249
250    use rand::prelude::*;
251    use crate::*;
252
253    const CH: CharacterCard = CharacterCard::Chongyun;
254    const XI: CharacterCard = CharacterCard::Xingqiu;
255    const FI: CharacterCard = CharacterCard::Fischl;
256    const KE: CharacterCard = CharacterCard::Keqing;
257    const RA: CharacterCard = CharacterCard::Razor;
258    const LA: CharacterCard = CharacterCard::StonehideLawachurl;
259    const NO: CharacterCard = CharacterCard::Noelle;
260    const CO: CharacterCard = CharacterCard::Collei;
261    const AG: CharacterCard = CharacterCard::FatuiPyroAgent;
262    const YO: CharacterCard = CharacterCard::Yoimiya;
263
264    const GA: ActionCard = ActionCard::Equipment(EquipmentCard::Artifact(ArtifactCard::GamblersEarrings));
265    const LU: ActionCard = ActionCard::Equipment(EquipmentCard::Artifact(ArtifactCard::LuckyDogsSilverCirclet));
266    const DE: ActionCard = ActionCard::Equipment(EquipmentCard::Artifact(ArtifactCard::DeepwoodMemories));
267    const VI: ActionCard = ActionCard::Equipment(EquipmentCard::Artifact(ArtifactCard::ViridescentVenerer));
268    const TH: ActionCard = ActionCard::Equipment(EquipmentCard::Talent(TalentCard::TheScentRemained));
269    const AW: ActionCard = ActionCard::Equipment(EquipmentCard::Talent(TalentCard::Awakening));
270    const IG: ActionCard = ActionCard::Equipment(EquipmentCard::Talent(TalentCard::IGotYourBack));
271    const SH: ActionCard = ActionCard::Equipment(EquipmentCard::Talent(TalentCard::ShakenNotPurred));
272    const AQ: ActionCard = ActionCard::Equipment(EquipmentCard::Weapon(WeaponCard::AquilaFavonia));
273    const WO: ActionCard = ActionCard::Equipment(EquipmentCard::Weapon(WeaponCard::WolfsGravestone));
274    const WH: ActionCard = ActionCard::Equipment(EquipmentCard::Weapon(WeaponCard::WhiteTassel));
275    const LO: ActionCard = ActionCard::Event(EventCard::Food(FoodCard::LotusFlowerCrisp));
276    const MI: ActionCard = ActionCard::Event(EventCard::Food(FoodCard::MintyMeatRolls));
277    const AD: ActionCard = ActionCard::Event(EventCard::Food(FoodCard::AdeptusTemptation));
278    const JU: ActionCard = ActionCard::Event(EventCard::Food(FoodCard::JueyunGuoba));
279    const LE: ActionCard = ActionCard::Event(EventCard::Normal(NormalEventCard::LeaveItToMe));
280    const ST: ActionCard = ActionCard::Event(EventCard::Normal(NormalEventCard::Strategize));
281    const MA: ActionCard = ActionCard::Event(EventCard::Normal(NormalEventCard::MasterOfWeaponry));
282    const AB: ActionCard = ActionCard::Event(EventCard::Normal(NormalEventCard::AbyssalSummons));
283    const HI: ActionCard = ActionCard::Event(EventCard::Resonance(ElementalResonanceCard::HighVoltage));
284    const EN: ActionCard = ActionCard::Event(EventCard::Resonance(ElementalResonanceCard::EnduringRock));
285    const PA: ActionCard = ActionCard::Support(SupportCard::Companion(CompanionCard::Paimon));
286    const LI: ActionCard = ActionCard::Support(SupportCard::Companion(CompanionCard::Liben));
287    const TI: ActionCard = ActionCard::Support(SupportCard::Companion(CompanionCard::Timaeus));
288    const TU: ActionCard = ActionCard::Support(SupportCard::Companion(CompanionCard::Tubby));
289    const NR: ActionCard = ActionCard::Support(SupportCard::Item(ItemCard::NRE));
290    const JA: ActionCard = ActionCard::Support(SupportCard::Location(LocationCard::JadeChamber));
291    const FA: ActionCard = ActionCard::Support(SupportCard::Location(LocationCard::FavoniusCathedral));
292    const DA: ActionCard = ActionCard::Support(SupportCard::Location(LocationCard::DawnWinery));
293    const WA: ActionCard = ActionCard::Support(SupportCard::Location(LocationCard::WangshuInn));
294
295    fn iter(characters: &[CharacterCard], actions: &[ActionCard]) -> Vec<Card> {
296        characters.iter().map(|x| Card::from(*x))
297            .chain(actions.iter().map(|x| Card::from(*x)))
298            .collect()
299    }
300
301    #[test]
302    fn eq_works() {
303        let characters = [LA, CO, NO];
304        let actions = [ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, EN, DE];
305
306        let deck = iter(&characters, &actions);        
307        let original_deck = Deck::from_iter(deck.iter().copied());        
308        
309        let mut rng = thread_rng();
310
311        for _ in 0..5 {
312            let mut shuffled = deck.clone();
313            shuffled.shuffle(&mut rng);
314
315            let shuffled_deck = Deck::from_iter(shuffled.iter().copied());
316
317            // Two decks should be considered equal as long as they have the same cards, no
318            // matter what order they were declared in
319            if original_deck != shuffled_deck {
320                panic!("These vec iterators produce different decks:\n{original_deck:?}\n{shuffled_deck:?}")
321            }
322        }
323    }
324
325    #[test]
326    fn wrong_amounts() {
327        let not_enough_chars = iter(
328            &[LA, NO],
329            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, EN, DE]
330        );
331
332        let not_enough_actions = iter(
333            &[LA, CO, NO],
334            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, TI, AQ, AB, TU, AB, EN, DE]
335        );
336
337        let too_many_chars = iter(
338            &[LA, CO, NO, CH],
339            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, EN, DE]
340        );
341
342        let too_many_actions = iter(
343            &[LA, CO, NO],
344            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, LI, TU, IG, TI, AQ, AB, TU, AB, EN, DE]
345        );
346
347        assert_eq!(Deck::from_iter(not_enough_chars), Err(DeckError::NotEnoughCharacterCards(2)));
348        assert_eq!(Deck::from_iter(not_enough_actions), Err(DeckError::NotEnoughActionCards(29)));
349        assert_eq!(Deck::from_iter(too_many_chars), Err(DeckError::TooManyCharacterCards));
350        assert_eq!(Deck::from_iter(too_many_actions), Err(DeckError::TooManyActionCards));
351    }
352
353    #[test]
354    fn talent_error() {
355        let talent_missing_character = iter(
356            &[FI, RA, AG],
357            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, AQ, DE]
358        );
359
360        assert_eq!(
361            Deck::from_iter(talent_missing_character),
362            Err(DeckError::TalentRequiresCharacter(TalentCard::IGotYourBack))
363        );
364
365        // including Noelle in the deck (character for TalentCard::IGotYourBack) fixes it
366        let this_works = iter(
367            &[FI, NO, AG],
368            &[ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, AQ, DE]
369        );
370
371        assert!(Deck::from_iter(this_works).is_ok());
372    }
373
374    #[test]
375    fn resonance_error() {
376        let actions = [ST, PA, VI, HI, LU, WO, WH, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, HI, TU, MI, TI, AQ, AB, TU, AB, AQ, DE];
377
378        let resonance_missing_chars = iter(&[FI, CH, XI], &actions);
379
380        assert_eq!(
381            Deck::from_iter(resonance_missing_chars),
382            Err(DeckError::ResonanceRequiresAtLeastTwo(ElementalResonanceCard::HighVoltage))
383        );
384
385        // having 2 or 3 characters of the resonance's element fixes it
386        let resonance_with_two   = iter(&[RA, FI, XI], &actions);
387        let resonance_with_three = iter(&[KE, RA, FI], &actions);
388
389        assert!(Deck::from_iter(resonance_with_two).is_ok());
390        assert!(Deck::from_iter(resonance_with_three).is_ok());
391
392    }
393
394    #[test]
395    fn multiple_error() {
396        let duplicate_char = iter(
397            &[YO, AG, YO],
398            &[ST, PA, VI, DE, LU, WO, WH, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, PA, TU, MI, TI, AQ, AB, TU, AB, AQ, DE]
399        );
400
401        assert_eq!(
402            Deck::from_iter(duplicate_char),
403            Err(DeckError::CharacterAppearsMoreThanOnce(CharacterCard::Yoimiya)),
404        );
405
406        let triplicate_card = iter(
407            &[LA, CO, NO],
408            &[ST, PA, VI, TU, LU, WO, WH, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, HI, TU, MI, TI, AQ, AB, TU, AB, AQ, DE]
409        );
410
411        assert_eq!(
412            Deck::from_iter(triplicate_card),
413            Err(DeckError::ActionCardAppearsMoreThanTwice(TU)),
414        );
415    }
416
417    #[test]
418    fn from_exact_works() {
419        let characters = [LA, CO, NO];
420        let actions = [ST, PA, VI, DA, LU, WO, IG, NR, WA, JU, JA, WA, MI, WH, FA, LE, GA, LO, MA, AD, LI, TU, IG, TI, AQ, AB, TU, AB, EN, DE];
421
422        let card_vec = iter(&characters, &actions);
423
424        assert_eq!(
425            Deck::from_iter(card_vec),
426            Deck::from_exact(characters, actions),
427        );
428    }
429}