pub(crate) mod happy_families;
pub(crate) mod quartet;
pub(crate) mod standard;
pub use happy_families::{HappyFamilies, HappyFamiliesRules};
pub use quartet::{Quartet, QuartetRules};
pub use standard::StandardRules;
use cardpack::prelude::{BasicPile, Pip};
pub trait GoFishRules: Send + Sync {
fn name(&self) -> &'static str;
fn deck(&self) -> BasicPile;
fn book_size(&self) -> usize;
fn initial_hand_size(&self, player_count: usize) -> usize;
fn min_players(&self) -> usize;
fn max_players(&self) -> usize;
fn is_valid_ask(&self, hand: &BasicPile, rank: &Pip) -> bool;
fn is_book(&self, cards: &BasicPile) -> bool;
}
pub enum GameVariant {
Standard,
HappyFamilies,
Quartet,
Custom(Box<dyn GoFishRules + Send + Sync>),
}
impl std::fmt::Debug for GameVariant {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Standard => write!(f, "GameVariant::Standard"),
Self::HappyFamilies => write!(f, "GameVariant::HappyFamilies"),
Self::Quartet => write!(f, "GameVariant::Quartet"),
Self::Custom(_) => write!(f, "GameVariant::Custom(<dyn GoFishRules>)"),
}
}
}
impl GameVariant {
#[must_use]
pub fn rules(&self) -> &dyn GoFishRules {
match self {
Self::Standard => &StandardRules,
Self::HappyFamilies => &HappyFamiliesRules,
Self::Quartet => &QuartetRules,
Self::Custom(rules) => rules.as_ref(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use cardpack::prelude::{BasicPile, FrenchBasicCard};
#[test]
fn test_game_variant_standard_rules() {
let variant = GameVariant::Standard;
let rules = variant.rules();
assert_eq!(rules.name(), "Standard Go Fish");
assert_eq!(rules.book_size(), 4);
assert_eq!(rules.min_players(), 2);
assert_eq!(rules.max_players(), 8);
}
#[test]
fn test_game_variant_happy_families_rules() {
let variant = GameVariant::HappyFamilies;
let rules = variant.rules();
assert_eq!(rules.name(), "Happy Families");
assert_eq!(rules.book_size(), 4);
assert_eq!(rules.min_players(), 2);
assert_eq!(rules.max_players(), 8);
assert_eq!(rules.initial_hand_size(4), 6);
assert_eq!(rules.initial_hand_size(5), 4);
}
#[test]
fn test_game_variant_quartet_rules() {
let variant = GameVariant::Quartet;
let rules = variant.rules();
assert_eq!(rules.name(), "Quartet");
assert_eq!(rules.book_size(), 4);
assert_eq!(rules.min_players(), 2);
assert_eq!(rules.max_players(), 8);
assert_eq!(rules.initial_hand_size(4), 8);
assert_eq!(rules.initial_hand_size(5), 6);
}
#[test]
fn test_game_variant_custom() {
struct MyRules;
impl GoFishRules for MyRules {
fn name(&self) -> &'static str {
"My Custom Rules"
}
fn deck(&self) -> BasicPile {
BasicPile::default()
}
fn book_size(&self) -> usize {
3
}
fn initial_hand_size(&self, _player_count: usize) -> usize {
5
}
fn min_players(&self) -> usize {
2
}
fn max_players(&self) -> usize {
6
}
fn is_valid_ask(&self, hand: &BasicPile, rank: &Pip) -> bool {
hand.iter().any(|c| &c.rank == rank)
}
fn is_book(&self, cards: &BasicPile) -> bool {
cards.len() == 3
}
}
let variant = GameVariant::Custom(Box::new(MyRules));
let rules = variant.rules();
assert_eq!(rules.name(), "My Custom Rules");
assert_eq!(rules.book_size(), 3);
}
#[test]
fn test_go_fish_rules_trait_object_is_valid() {
let rules: &dyn GoFishRules = &StandardRules;
assert_eq!(rules.name(), "Standard Go Fish");
}
#[test]
fn test_standard_rules_is_book_four_aces() {
let four_aces = BasicPile::from(vec![
FrenchBasicCard::ACE_SPADES,
FrenchBasicCard::ACE_HEARTS,
FrenchBasicCard::ACE_DIAMONDS,
FrenchBasicCard::ACE_CLUBS,
]);
assert!(StandardRules.is_book(&four_aces));
}
#[test]
fn test_game_variant_debug_standard() {
assert_eq!(
format!("{:?}", GameVariant::Standard),
"GameVariant::Standard"
);
}
#[test]
fn test_game_variant_debug_happy_families() {
assert_eq!(
format!("{:?}", GameVariant::HappyFamilies),
"GameVariant::HappyFamilies"
);
}
#[test]
fn test_game_variant_debug_quartet() {
assert_eq!(
format!("{:?}", GameVariant::Quartet),
"GameVariant::Quartet"
);
}
}