use rand::{Rng, rngs::ThreadRng};
use crate::{
game::Game,
interface::{UserInput, UserOutput},
};
pub enum Command<I, O, F = fn(usize) -> f64, R = ThreadRng>
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
FinishGame,
Control(Box<dyn ControlCommand<I, O, F, R>>),
}
impl<I, O, F, R> Command<I, O, F, R>
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
pub fn control<C>(control: C) -> Self
where
C: ControlCommand<I, O, F, R> + 'static,
{
Self::Control(Box::new(control))
}
}
pub trait ControlCommand<I, O, F = fn(usize) -> f64, R = ThreadRng>
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>);
}
pub struct LaunchBall;
impl<I, O, F, R> ControlCommand<I, O, F, R> for LaunchBall
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>) {
let _ = game.launch_ball();
}
}
pub struct CauseLottery;
impl<I, O, F, R> ControlCommand<I, O, F, R> for CauseLottery
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>) {
game.cause_lottery();
}
}
pub struct StartGame;
impl<I, O, F, R> ControlCommand<I, O, F, R> for StartGame
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>) {
let _ = game.start();
}
}
pub struct FinishGame;
impl<I, O, F, R> ControlCommand<I, O, F, R> for FinishGame
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>) {
let _ = game.finish();
}
}
pub struct LaunchBallFlowProducer {
start_hole_probability: f64,
rng: ThreadRng,
}
impl LaunchBallFlowProducer {
pub fn new(start_hole_probability: f64) -> Self {
Self {
start_hole_probability,
rng: rand::rng(),
}
}
pub fn produce(&mut self) -> LaunchBallFlow {
LaunchBallFlow::new(self.rng.random_bool(self.start_hole_probability))
}
}
pub struct LaunchBallFlow {
is_lottery: bool,
}
impl LaunchBallFlow {
pub fn new(is_lottery: bool) -> Self {
Self { is_lottery }
}
}
impl<I, O, F, R> ControlCommand<I, O, F, R> for LaunchBallFlow
where
I: UserInput<O, F, R>,
O: UserOutput,
F: FnMut(usize) -> f64,
R: Rng,
{
fn execute(&mut self, game: &mut Game<I, O, F, R>) {
let _ = game.launch_ball();
if self.is_lottery {
game.cause_lottery();
}
}
}