use crate::game::payout::payout;
use crate::game::symbol::Symbol;
use serde_derive::{Deserialize, Serialize};
use std::error::Error;
use std::fmt;
pub mod payout;
pub mod symbol;
pub const NUM_REELS: usize = 3;
#[derive(Debug, Clone)]
pub struct InvalidBet {
bet: u32,
bet_min: u32,
bet_max: u32,
}
impl Error for InvalidBet {}
impl fmt::Display for InvalidBet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message: &str;
if self.bet_min > self.bet_max {
message = "bet_min > bet_max";
} else if self.bet < self.bet_min {
message = "bet < bet_min";
} else if self.bet > self.bet_max {
message = "bet > bet_max";
} else {
panic!("Unprocessed condition!");
}
write!(f, "{}", message)
}
}
#[derive(Debug, Clone)]
pub struct LowBalance;
impl Error for LowBalance {}
impl fmt::Display for LowBalance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Insufficient credits on the balance!")
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Game {
credits: u32,
bet: u32,
bet_min: u32,
bet_max: u32,
win: u32,
}
impl Game {
pub fn new(credits: u32, bet: u32, bet_min: u32, bet_max: u32) -> Result<Self, InvalidBet> {
if !Game::validate_bet(bet, bet_min, bet_max) {
return Err(InvalidBet {
bet,
bet_min,
bet_max,
});
}
Ok(Game {
credits,
bet,
bet_min,
bet_max,
win: 0,
})
}
pub fn set_bet(&mut self, bet: u32) -> Result<(), InvalidBet> {
if !Game::validate_bet(bet, self.bet_min, self.bet_max) {
let bet_min = self.bet_min;
let bet_max = self.bet_max;
return Err(InvalidBet {
bet,
bet_min,
bet_max,
});
}
self.bet = bet;
Ok(())
}
pub fn bet(&self) -> u32 {
self.bet
}
pub fn min(&self) -> u32 {
self.bet_min
}
pub fn max(&self) -> u32 {
self.bet_max
}
pub fn credits(&self) -> u32 {
self.credits
}
pub fn win(&self) -> u32 {
self.win
}
pub fn spin(&mut self) -> Result<Vec<Symbol>, LowBalance> {
if self.credits() < self.bet() {
return Err(LowBalance);
}
let mut stops = Vec::with_capacity(NUM_REELS);
for _i in 0..NUM_REELS {
stops.push(Symbol::random());
}
self.credits -= self.bet();
self.win = payout(&stops) * self.bet();
self.credits += self.win;
Ok(stops)
}
pub fn to_json(&self) -> String {
serde_json::to_string(self).unwrap()
}
fn validate_bet(bet: u32, bet_min: u32, bet_max: u32) -> bool {
if bet_min > bet_max || bet < bet_min || bet > bet_max {
return false;
}
true
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn game_new() {
let credits = 1000;
let bet = 1;
let bet_min = 1;
let bet_max = 10;
assert_eq!(
Game {
credits,
bet,
bet_min,
bet_max,
win: 0
},
Game::new(credits, bet, bet_min, bet_max).unwrap()
)
}
#[test]
fn game_spin() {
let mut game = Game::new(1000, 1, 1, 10).unwrap();
assert!(game.spin().is_ok())
}
#[test]
fn game_validate_bet() {
assert!(Game::validate_bet(1, 1, 10))
}
#[test]
fn game_validate_bet_bet_less_bet_min() {
assert!(!Game::validate_bet(1, 10, 100))
}
#[test]
fn game_validate_bet_bet_bigger_bet_max() {
assert!(!Game::validate_bet(11, 1, 10))
}
#[test]
fn game_validate_bet_bet_min_bigger_bet_max() {
assert!(!Game::validate_bet(10, 20, 10))
}
}