94 lines
2.5 KiB
Rust
94 lines
2.5 KiB
Rust
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<Card>,
|
|
deck: Vec<Card>,
|
|
|
|
players: Vec<Vec<Card>>,
|
|
}
|
|
impl Game {
|
|
pub fn new(decks: usize, players: usize) -> Result<Self, MakeGameError> {
|
|
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<I, F>(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<Card>)
|
|
} |