fortunes_foundation/simulator/src/ruleset.rs

108 lines
3.1 KiB
Rust

use anyhow::bail;
#[derive(Clone)]
pub struct Ruleset {
pub n_slots: u8,
pub n_suits: u8,
pub n_cards_per_suit: u8,
pub n_arcana: u8,
}
#[derive(Clone)]
pub struct Deck {
pub aces: Vec<Card>,
pub suits: Vec<u8>,
pub cards: Vec<CardMetadata>,
pub instantly_accepted: Vec<Card>,
}
#[derive(Clone)]
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
}
pub 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});
}
// instantly accepted
let first_arcana = self.n_suits * self.n_cards_per_suit;
let mut instantly_accepted = vec![];
for a in aces.iter() {
instantly_accepted.push(Card(a.0 + 1)) // twos
}
instantly_accepted.push(Card(first_arcana));
instantly_accepted.push(Card(first_arcana + self.n_arcana - 1));
Deck { aces, suits, cards, instantly_accepted }
}
}