use crate::funky::types::board::BuffoonBoard;
use crate::funky::types::buffoon_card::BuffoonCard;
use crate::funky::types::score::Score;
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub enum ScoreOp {
Nothing,
AddChips(usize),
AddMult(usize),
Add(Score),
TimesMult(f32),
Seq(Vec<Self>),
}
impl ScoreOp {
#[must_use]
pub fn apply(&self, score: Score) -> Score {
match self {
Self::Nothing => score,
Self::AddChips(chips) => score + Score::new(*chips, 0),
Self::AddMult(mult) => score + Score::new(0, *mult),
Self::Add(delta) => score + *delta,
Self::TimesMult(factor) => score.multi_mult(*factor),
Self::Seq(ops) => ops.iter().fold(score, |acc, op| op.apply(acc)),
}
}
}
pub struct ScoringContext<'a> {
pub board: &'a BuffoonBoard,
pub source: BuffoonCard,
}
pub trait Effect {
fn score(&self, ctx: &ScoringContext<'_>) -> ScoreOp;
}
#[derive(Default)]
pub struct EffectRegistry {
handlers: HashMap<u32, Box<dyn Effect>>,
}
impl EffectRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, id: u32, effect: impl Effect + 'static) {
self.handlers.insert(id, Box::new(effect));
}
#[must_use]
pub fn get(&self, id: u32) -> Option<&dyn Effect> {
self.handlers.get(&id).map(|effect| &**effect)
}
#[must_use]
pub fn contains(&self, id: u32) -> bool {
self.handlers.contains_key(&id)
}
}
#[cfg(test)]
#[allow(non_snake_case)]
mod funky__types__effect_tests {
use super::*;
#[test]
fn score_op__apply() {
let base = Score::new(10, 4);
assert_eq!(ScoreOp::Nothing.apply(base), Score::new(10, 4));
assert_eq!(ScoreOp::AddChips(5).apply(base), Score::new(15, 4));
assert_eq!(ScoreOp::AddMult(3).apply(base), Score::new(10, 7));
assert_eq!(
ScoreOp::Add(Score::new(5, 3)).apply(base),
Score::new(15, 7)
);
assert_eq!(ScoreOp::TimesMult(2.0).apply(base), Score::new(10, 8));
}
#[test]
fn score_op__seq_applies_in_order() {
let op = ScoreOp::Seq(vec![ScoreOp::AddMult(2), ScoreOp::TimesMult(3.0)]);
assert_eq!(op.apply(Score::new(0, 4)), Score::new(0, 18));
}
struct FlatMult(usize);
impl Effect for FlatMult {
fn score(&self, _ctx: &ScoringContext<'_>) -> ScoreOp {
ScoreOp::AddMult(self.0)
}
}
#[test]
fn registry__register_and_get() {
let mut registry = EffectRegistry::new();
assert!(!registry.contains(1));
registry.register(1, FlatMult(7));
assert!(registry.contains(1));
assert!(registry.get(2).is_none());
assert!(registry.get(1).is_some());
}
}