94 lines
2.6 KiB
Rust
94 lines
2.6 KiB
Rust
|
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<Card>,
|
||
|
pub suits: Vec<u8>,
|
||
|
pub cards: Vec<CardMetadata>,
|
||
|
}
|
||
|
|
||
|
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<Setup> {
|
||
|
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<u8> = 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 }
|
||
|
}
|
||
|
}
|