use crate::battle::{Battle, BattleRules};
use crate::error::WeaselResult;
use crate::event::{Event, EventKind, EventProcessor, EventQueue, EventTrigger};
use num_traits::Num;
#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::cmp::Ordering;
use std::fmt::Debug;
pub struct Entropy<R: BattleRules> {
model: EntropyModel<R>,
rules: R::ER,
}
impl<R: BattleRules> Entropy<R> {
pub(crate) fn new(seed: Option<EntropySeed<R>>, rules: R::ER) -> Self {
Self {
model: rules.generate_model(&seed),
rules,
}
}
pub fn generate(&mut self, low: EntropyOutput<R>, high: EntropyOutput<R>) -> EntropyOutput<R> {
match low.partial_cmp(&high) {
Some(Ordering::Less) => self.rules.generate(&mut self.model, low, high),
Some(Ordering::Greater) => self.rules.generate(&mut self.model, high, low),
Some(Ordering::Equal) => low,
None => panic!("incomparable range! low: {:?}, high: {:?}", low, high),
}
}
pub fn model(&self) -> &EntropyModel<R> {
&self.model
}
pub fn model_mut(&mut self) -> &mut EntropyModel<R> {
&mut self.model
}
pub fn rules(&self) -> &R::ER {
&self.rules
}
pub fn rules_mut(&mut self) -> &mut R::ER {
&mut self.rules
}
pub(crate) fn regenerate_model(&mut self, seed: &Option<EntropySeed<R>>) {
self.model = self.rules.generate_model(seed)
}
}
pub trait EntropyRules {
#[cfg(not(feature = "serialization"))]
type EntropySeed: Clone + Debug + Send;
#[cfg(feature = "serialization")]
type EntropySeed: Clone + Debug + Send + Serialize + for<'a> Deserialize<'a>;
type EntropyModel;
type EntropyOutput: PartialOrd + Copy + Num + Debug;
fn generate_model(&self, seed: &Option<Self::EntropySeed>) -> Self::EntropyModel;
fn generate(
&self,
model: &mut Self::EntropyModel,
low: Self::EntropyOutput,
high: Self::EntropyOutput,
) -> Self::EntropyOutput;
}
pub type EntropySeed<R> = <<R as BattleRules>::ER as EntropyRules>::EntropySeed;
pub type EntropyModel<R> = <<R as BattleRules>::ER as EntropyRules>::EntropyModel;
pub type EntropyOutput<R> = <<R as BattleRules>::ER as EntropyRules>::EntropyOutput;
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct ResetEntropy<R: BattleRules> {
#[cfg_attr(
feature = "serialization",
serde(bound(
serialize = "Option<EntropySeed<R>>: Serialize",
deserialize = "Option<EntropySeed<R>>: Deserialize<'de>"
))
)]
seed: Option<EntropySeed<R>>,
}
impl<R: BattleRules> ResetEntropy<R> {
pub fn trigger<P: EventProcessor<R>>(processor: &mut P) -> ResetEntropyTrigger<R, P> {
ResetEntropyTrigger {
processor,
seed: None,
}
}
pub fn seed(&self) -> &Option<EntropySeed<R>> {
&self.seed
}
}
impl<R: BattleRules> std::fmt::Debug for ResetEntropy<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ResetEntropy {{ seed: {:?} }}", self.seed)
}
}
impl<R: BattleRules> Clone for ResetEntropy<R> {
fn clone(&self) -> Self {
Self {
seed: self.seed.clone(),
}
}
}
impl<R: BattleRules + 'static> Event<R> for ResetEntropy<R> {
fn verify(&self, _battle: &Battle<R>) -> WeaselResult<(), R> {
Ok(())
}
fn apply(&self, battle: &mut Battle<R>, _: &mut Option<EventQueue<R>>) {
battle.entropy.regenerate_model(&self.seed);
}
fn kind(&self) -> EventKind {
EventKind::ResetEntropy
}
fn box_clone(&self) -> Box<dyn Event<R> + Send> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct ResetEntropyTrigger<'a, R, P>
where
R: BattleRules,
P: EventProcessor<R>,
{
processor: &'a mut P,
seed: Option<EntropySeed<R>>,
}
impl<'a, R, P> ResetEntropyTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
pub fn seed(&'a mut self, seed: EntropySeed<R>) -> &'a mut Self {
self.seed = Some(seed);
self
}
}
impl<'a, R, P> EventTrigger<'a, R, P> for ResetEntropyTrigger<'a, R, P>
where
R: BattleRules + 'static,
P: EventProcessor<R>,
{
fn processor(&'a mut self) -> &'a mut P {
self.processor
}
fn event(&self) -> Box<dyn Event<R> + Send> {
Box::new(ResetEntropy {
seed: self.seed.clone(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::battle::Battle;
use crate::battle_rules_with_entropy;
use crate::server::Server;
use crate::util::tests::server;
use crate::{battle_rules, rules::empty::*};
const DEFAULT_SEED: i32 = 3;
#[derive(Debug, Default, Clone, Copy)]
pub struct CustomEntropyRules {}
impl EntropyRules for CustomEntropyRules {
type EntropySeed = i32;
type EntropyModel = i32;
type EntropyOutput = i32;
fn generate_model(&self, seed: &Option<Self::EntropySeed>) -> Self::EntropyModel {
seed.unwrap_or(DEFAULT_SEED)
}
fn generate(
&self,
model: &mut Self::EntropyModel,
_low: Self::EntropyOutput,
_high: Self::EntropyOutput,
) -> Self::EntropyOutput {
let model = *model;
let result: Self::EntropyOutput = model;
result
}
}
battle_rules_with_entropy! { CustomEntropyRules }
#[test]
fn reset_model() {
let battle = Battle::builder(CustomRules::new()).build();
let mut server = Server::builder(battle).build();
assert_eq!(server.battle.entropy.generate(1, 5), DEFAULT_SEED);
assert!(ResetEntropy::trigger(&mut server).seed(5).fire().is_ok());
assert_eq!(server.battle.entropy.generate(1, 5), 5);
}
#[test]
fn low_high_guarantee() {
battle_rules! {}
let mut server = server(CustomRules::new());
assert_eq!(server.battle.entropy.generate(5, 1), 3);
}
#[cfg(feature = "random")]
#[test]
fn empty_range() {
battle_rules_with_entropy! { crate::rules::entropy::UniformDistribution<i32> }
let mut server = server(CustomRules::new());
assert_eq!(server.battle.entropy.generate(1, 1), 1);
}
}