use anyhow::bail;
use rand::seq::{IndexedMutRandom, IndexedRandom, SliceRandom};
use rand::{RngExt, rng};
#[derive(Debug, Clone, Copy)]
pub enum TypeProblem {
Addition,
Subtraction,
Multiplication,
Division,
}
#[derive(Debug)]
pub struct Domain {
pub from: f32,
pub to: f32,
pub digits_after_num: Option<u32>,
}
impl Default for Domain {
fn default() -> Self {
Self {
from: 0.0,
to: 100.0,
digits_after_num: None,
}
}
}
#[derive(Debug)]
pub struct GameParams {
pub amount: u32,
pub from: f32,
pub to: f32,
pub digits_after_num: u32,
pub type_problems: Vec<TypeProblem>,
}
impl Default for GameParams {
fn default() -> Self {
Self {
amount: 10,
from: 0.0,
to: 50.0,
digits_after_num: 2,
type_problems: vec![TypeProblem::Multiplication, TypeProblem::Addition],
}
}
}
#[derive(Default, Debug)]
pub struct Example {
pub problem: String,
pub answer: f32,
}
pub fn generate_problem(types: &Vec<TypeProblem>, domain: &GameParams) -> Example {
let mut rng = rand::rng();
let digits = domain.digits_after_num;
let multiplier = 10u32.pow(digits) as f32;
let (operator, num1, num2, answer) = loop {
let type_ = types
.choose(&mut rng)
.copied()
.unwrap_or(TypeProblem::Addition);
let n1 = rng.random_range(domain.from as u32..domain.to as u32) as f32;
let n2 = rng.random_range(domain.from as u32..domain.to as u32) as f32;
match type_ {
TypeProblem::Addition => break ('+', n1, n2, n1 + n2),
TypeProblem::Subtraction => break ('-', n1, n2, n1 - n2),
TypeProblem::Multiplication => break ('*', n1, n2, n1 * n2),
TypeProblem::Division => {
if n2 == 0.0 && n1 == 0.0 {
continue;
}
if (n1 * multiplier) % n2 == 0.0 {
break ('/', n1, n2, n1 / n2);
}
}
}
};
Example {
problem: format!("{} {} {} = ", num1, operator, num2),
answer,
}
}