use crate::population::{TernaryAgent, SimpleRng};
#[derive(Clone, Debug)]
pub enum CrossoverMethod {
SinglePoint,
Uniform,
}
#[derive(Clone, Debug)]
pub struct Mutation {
pub point_rate: f64,
pub crossover: CrossoverMethod,
pub crossover_rate: f64,
}
impl Mutation {
pub fn default_rates() -> Self {
Self {
point_rate: 0.01,
crossover: CrossoverMethod::SinglePoint,
crossover_rate: 0.7,
}
}
pub fn point_mutate(&self, agent: &mut TernaryAgent, rng: &mut SimpleRng) {
for v in &mut agent.strategy {
if rng.next_f64() < self.point_rate {
let current = *v;
let new_val = (rng.next_u32() % 2) as u8;
*v = if new_val < current { new_val } else { new_val + 1 };
if *v > 2 { *v = 0; }
debug_assert!(*v <= 2);
}
}
}
pub fn crossover(&self, parent_a: &TernaryAgent, parent_b: &TernaryAgent, rng: &mut SimpleRng) -> (TernaryAgent, TernaryAgent) {
if rng.next_f64() > self.crossover_rate {
return (parent_a.clone(), parent_b.clone());
}
match &self.crossover {
CrossoverMethod::SinglePoint => {
let point = if parent_a.strategy.is_empty() {
0
} else {
((rng.next_u64() as usize) % parent_a.strategy.len()).max(1)
};
let mut child_a = parent_a.clone();
let mut child_b = parent_b.clone();
for i in point..parent_a.strategy.len() {
child_a.strategy[i] = parent_b.strategy[i];
child_b.strategy[i] = parent_a.strategy[i];
}
(child_a, child_b)
}
CrossoverMethod::Uniform => {
let mut child_a = parent_a.clone();
let mut child_b = parent_b.clone();
for i in 0..parent_a.strategy.len() {
if rng.next_f64() < 0.5 {
child_a.strategy[i] = parent_b.strategy[i];
child_b.strategy[i] = parent_a.strategy[i];
}
}
(child_a, child_b)
}
}
}
pub fn mutate(&self, agent: &mut TernaryAgent, rng: &mut SimpleRng) {
self.point_mutate(agent, rng);
}
}
pub fn autofill_mutate(agent: &mut TernaryAgent, start: usize, rng: &mut SimpleRng) {
if agent.strategy.len() < 2 || start >= agent.strategy.len() {
return;
}
let len = agent.strategy.len();
if start >= 2 {
let diff = (agent.strategy[start - 1] as i32 - agent.strategy[start - 2] as i32).rem_euclid(3);
for i in start..len {
agent.strategy[i] = (agent.strategy[i - 1] as i32 + diff) as u8 % 3;
}
} else {
for i in start..len {
agent.strategy[i] = (rng.next_u32() % 3) as u8;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_autofill_constant_pattern() {
let mut agent = TernaryAgent::new_unchecked(vec![1, 1, 1, 0, 0, 0]);
let mut rng = SimpleRng::from_seed(99);
autofill_mutate(&mut agent, 3, &mut rng);
assert_eq!(agent.strategy[3], 1);
assert_eq!(agent.strategy[4], 1);
assert_eq!(agent.strategy[5], 1);
}
#[test]
fn test_autofill_ascending_pattern() {
let mut agent = TernaryAgent::new_unchecked(vec![0, 1, 0, 0, 0, 0]);
let mut rng = SimpleRng::from_seed(99);
autofill_mutate(&mut agent, 2, &mut rng);
assert_eq!(agent.strategy[2], 2); assert_eq!(agent.strategy[3], 0); assert_eq!(agent.strategy[4], 1);
assert_eq!(agent.strategy[5], 2);
}
}