use std::{fmt::Display, ops::Add};
use anyhow::bail;
use crate::trick_taking::TrickTakingGame;
pub const PLAYERS: usize = 4;
#[derive(Clone, Default, Debug)]
pub struct Player<G>
where
G: TrickTakingGame,
{
hand: Vec<G::CardType>,
id: PlayerId,
}
impl<G> Player<G>
where
G: TrickTakingGame,
{
pub fn give(&mut self, card: G::CardType) {
self.hand.push(card);
}
pub fn remove_card(&mut self, index: usize) -> Option<G::CardType> {
if index < self.hand.len() {
Some(self.hand.remove(index))
} else {
None
}
}
pub fn hand(&self) -> &[G::CardType] {
&self.hand
}
pub fn id(&self) -> PlayerId {
self.id
}
pub fn new(id: PlayerId) -> Self {
Self {
id,
hand: Vec::new(),
}
}
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct PlayerId(usize);
impl PlayerId {
pub const PLAYER_0: Self = PlayerId(0);
pub const PLAYER_1: Self = PlayerId(1);
pub const PLAYER_2: Self = PlayerId(2);
pub const PLAYER_3: Self = PlayerId(3);
pub fn inc(&mut self) {
if self.0 < PLAYERS - 1 {
self.0 += 1;
} else {
self.0 = 0;
}
}
pub fn next(mut self) -> Self {
self.inc();
self
}
pub fn as_usize(&self) -> usize {
self.0
}
}
impl Add<usize> for PlayerId {
type Output = Self;
fn add(self, rhs: usize) -> Self::Output {
let sum = self.0 + rhs;
PlayerId(sum % PLAYERS)
}
}
impl TryFrom<usize> for PlayerId {
type Error = anyhow::Error;
fn try_from(value: usize) -> Result<Self, Self::Error> {
if (0..PLAYERS).contains(&value) {
Ok(PlayerId(value))
} else {
bail!(
"Tried to convert {} into a PlayerId, but acceptable values are in range 0..{PLAYERS}",
value
)
}
}
}
impl Display for PlayerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod test_utils {
use proptest::prelude::Strategy;
use crate::trick_taking::{PLAYERS, PlayerId};
pub(crate) fn player_id_strategy() -> impl Strategy<Value = PlayerId> {
(0..PLAYERS).prop_map(|id| PlayerId::try_from(id).unwrap())
}
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use crate::trick_taking::{PLAYERS, player::test_utils::player_id_strategy};
proptest! {
#[test]
fn sum_always_gives_valid_playerid(p_id in player_id_strategy(), rhs: usize) {
let sum = p_id + rhs;
assert!(sum.0<PLAYERS);
}
}
}