pub mod errors; use rand::prelude::*; use std::fmt::Display; use crate::card::Card; use crate::card::consts::{DECK, CARDS_PER_PLAYER}; use errors::{MakeGameError, PlayError}; #[derive(Debug, Clone)] pub struct Game { played: Vec, deck: Vec, players: Vec>, } impl Game { pub fn new(decks: usize, players: usize) -> Result { const TOP_CARD: usize = 1; if players * CARDS_PER_PLAYER + TOP_CARD >= DECK.len() * decks { return Err(MakeGameError::InsufficientCards); } let mut deck = DECK.to_vec().repeat(decks); let mut rng = thread_rng(); deck.shuffle(&mut rng); let mut players = vec![ Vec::with_capacity(7); players ]; for p in &mut players { let range = deck.len()-CARDS_PER_PLAYER .. deck.len(); let iter = deck.drain(range); p.extend(iter); } // safety: this will always be safe because we checked ahead of time // if there were enough cards let top_card = unsafe { deck.pop().unwrap_unchecked() }; let played = vec![top_card]; Ok(Self { played, deck, players, }) } pub fn play(&mut self, player: usize, cards: &[Card]) -> Result<(), PlayError> { let player = self.players.get_mut(player).ok_or(PlayError::NoSuchPlayer)?; let o_len = player.len(); player.retain(|card| !cards.contains(card)); if o_len - player.len() == cards.len() { Ok(()) } else { Err(PlayError::PlayerDoesNotHaveCard) } } } impl Display for Game { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let join_cards_by_spaces = |cards: &[Card]| join_formatted(cards, ToString::to_string, " "); let played = join_cards_by_spaces(&self.played); let deck = join_cards_by_spaces(&self.deck); let players = join_formatted( (1..).zip(&self.players), |(i, v)| format!("{i}: {}", join_cards_by_spaces(v)), "\n" ); write!(f, "played: {played}\ndeck: {deck}\nplayers:\n{players}") } } fn join_formatted(iter: I, f: F, sep: &str) -> String where I: IntoIterator, F: FnMut(I::Item) -> String, { iter.into_iter() .map(f) .reduce(|acc, s| acc + sep + &s) .unwrap_or_default() } pub enum Message { PlayCards(usize, Vec) }