use choice_function::ChoiceFunction;
use creature::Creature;
use rand::{rng, seq::SliceRandom};

mod choice_function;
mod creature;
mod genetics;

fn evolve() {
    let cf = ChoiceFunction::Random;
    const N_ITERATIONS: usize = 200;
    const N_CREATURES: usize = 100;
    const N_RANDOMS: usize = 50;
    const N_SURVIVORS: usize = 40;

    let mut pool = vec![];
    for _ in 0..N_CREATURES {
        pool.push(Creature::generate(cf));
    }

    for iteration in 0..N_ITERATIONS {
        report(iteration, &pool);

        let mut survivors = pool;
        survivors.sort_by(|x, y| fitness(x).partial_cmp(&fitness(y)).unwrap());
        survivors.reverse();
        survivors.drain(N_SURVIVORS..N_CREATURES);

        for _ in 0..N_RANDOMS {
            survivors.push(Creature::generate(cf));
        }

        let mut survivors_queue = vec![];
        while survivors_queue.len() < N_CREATURES * 2 {
            survivors_queue.extend(survivors.clone());
        }
        survivors_queue.shuffle(&mut rng());

        let mut offspring = vec![];
        for _ in 0..N_CREATURES {
            let lhs = survivors_queue.pop().unwrap();
            let rhs = survivors_queue.pop().unwrap();
            offspring.push(lhs.breed(cf, &rhs));
        }
        pool = offspring;
    }
}

fn report(iteration: usize, pool: &[Creature]) {
    println!("== Iteration {} ==", iteration + 1);
    println!("{}", pool[0].profile());
    println!("");
}

fn fitness(creature: &Creature) -> f64 {
    let phenotype = creature.phenotype();
    let mut value = 0.0;
    value += (phenotype.stats.iq.value() as f64) / 10.0;
    value += (phenotype.stats.eq.value() as f64) / 10.0;

    return value;
}

fn main() {
    evolve();
}
