use anyhow::bail; pub struct Ruleset { pub n_slots: u8, pub n_suits: u8, pub n_cards_per_suit: u8, pub n_arcana: u8, } pub struct Deck { pub aces: Vec, pub suits: Vec, pub cards: Vec, } pub struct Setup { pub ruleset: Ruleset, pub deck: Deck } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Card(pub u8); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct CardMetadata { pub suit: u8, pub rank: u8 } impl Ruleset { fn usable_slots(&self) -> u8 { self.n_slots - 1 } fn total_n_cards(&self) -> u8 { self.n_arcana + self.n_suits * self.n_cards_per_suit } fn usable_n_cards(&self) -> u8 { self.total_n_cards() - self.n_suits } pub fn compile(self) -> anyhow::Result { self.validate()?; let deck = self.create_deck(); Ok(Setup { ruleset: self, deck }) } fn validate(&self) -> anyhow::Result<()> { if self.n_slots > 11 { bail!("max 11 slots") } if self.n_suits > 5 { bail!("max 5 suits") } if self.n_cards_per_suit > 13 { bail!("max 13 cards per suit") } if self.n_arcana > 99 { bail!("max 99 arcana") } if self.usable_slots() % 2 != 0 { bail!("must have even number of usable slots") } if self.usable_n_cards() % self.usable_slots() != 0 { bail!( "needs {} more cards for an even deal", self.usable_slots() - (self.usable_n_cards() % self.usable_slots()) ) } let instantly_placed_cards = self.n_suits + if self.n_arcana == 0 { 0 } else if self.n_arcana == 1 { 1 } else { 2 }; if self.total_n_cards() - instantly_placed_cards < self.usable_slots() { bail!("can't place all cards without automoves"); } Ok(()) } fn create_deck(&self) -> Deck { let possible_suits = b"pscwb"; let mut aces = vec![]; let mut suits: Vec = vec![]; suits.extend(possible_suits[..self.n_suits as usize].into_iter()); let mut cards = vec![]; // suited cards for &suit in &suits { for rank in 1..=self.n_cards_per_suit { if rank == 1 { aces.push(Card(cards.len() as u8)) } cards.push(CardMetadata { suit, rank: rank as u8 }) } } // arcana for rank in 0..self.n_arcana { cards.push(CardMetadata {suit: b'a', rank}); } Deck { aces, suits, cards } } }