games-rs 0.5.0

Pre-implemented games written in rust.
Documentation
use crate::errors::{BASE_ROCK_PAPER_SCISSORS_ERROR_CODE, ErrorCode};
use core::{
    cmp::{Ord, Ordering, PartialOrd},
    fmt,
};
use rand::RngExt;
use std::str::FromStr;

/// Weapons for Rock paper scissors
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Weapon {
    /// A rock, it beats scissors
    Rock,
    /// Paper, it beats a rock
    Paper,
    /// Scissors, it beats paper
    Scissors,
}

/// Error parsing weapon
#[repr(C)]
#[derive(
    Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Ord, PartialOrd,
)]

pub enum WeaponParseError {
    /// Weapon attempted to be parsed was not valid (4001)
    InvalidWeaponError,
}

impl fmt::Display for WeaponParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            WeaponParseError::InvalidWeaponError => f.write_str("Invalid Weapon"),
        }
    }
}

impl ErrorCode for WeaponParseError {
    fn error_code(&self) -> i32 {
        BASE_ROCK_PAPER_SCISSORS_ERROR_CODE
            + match *self {
                WeaponParseError::InvalidWeaponError => 1,
            }
    }
}

impl fmt::Display for Weapon {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Weapon::Rock => "Rock",
                Weapon::Paper => "Paper",
                Weapon::Scissors => "Scissors",
            }
        )
    }
}

impl FromStr for Weapon {
    type Err = WeaponParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.chars()
            .next()
            .map(|c| c.to_ascii_lowercase())
            .and_then(|c| match c {
                'r' => Some(Weapon::Rock),
                'p' => Some(Weapon::Paper),
                's' => Some(Weapon::Scissors),
                _ => None,
            })
            .ok_or(WeaponParseError::InvalidWeaponError)
    }
}

impl PartialOrd for Weapon {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Weapon {
    fn cmp(&self, other: &Self) -> Ordering {
        match self {
            // Rocks are
            Weapon::Rock => match other {
                // Equal to rocks
                Weapon::Rock => Ordering::Equal,
                // Weaker than papers
                Weapon::Paper => Ordering::Less,
                // Stronger than scissors
                Weapon::Scissors => Ordering::Greater,
            },
            // Papers are
            Weapon::Paper => match other {
                // Stronger than rocks
                Weapon::Rock => Ordering::Greater,
                // equal to papers
                Weapon::Paper => Ordering::Equal,
                // weaker than scissors
                Weapon::Scissors => Ordering::Less,
            },
            // Scissors are
            Weapon::Scissors => match other {
                // Weaker than rocks
                Weapon::Rock => Ordering::Less,
                // Stronger than paper
                Weapon::Paper => Ordering::Greater,
                // equal to scissors
                Weapon::Scissors => Ordering::Equal,
            },
        }
    }
}

impl Weapon {
    /// Randomly choose a weapon
    pub fn rand() -> Self {
        "rps"
            .chars()
            .nth(crate::get_rng().random_range(0..3))
            .and_then(|p| Weapon::from_str(&p.to_string()).ok())
            .expect("Rand should always return weapon")
    }
}