use melvm::{opcode::OpCode, Covenant};
use once_cell::sync::Lazy;
use ordered_float::OrderedFloat;
use quanta::Clock;
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
type OF64 = OrderedFloat<f64>;
static CLOCK: Lazy<Clock> = Lazy::new(Clock::new);
fn eval_fitness(input: &[u8]) -> OF64 {
let val = if let Ok(cov) = Covenant::from_bytes(input) {
cov
} else {
return 0.0.into();
};
let mut runtime = f64::MAX;
for _ in 0..10 {
let start = CLOCK.start();
val.debug_execute(&[]);
runtime = runtime.min((CLOCK.end() - start) as f64);
}
if val.to_ops().iter().any(|f| matches!(f, OpCode::Noop)) {
return 0.0.into();
};
let _weight = val.weight() as f64;
let ilen = input.len() as f64;
if ilen == 0.0 {
return 0.0.into();
}
(runtime / ilen).into()
}
fn mutate(input: &mut Vec<u8>) {
while rand::random::<f64>() < 0.9 && !input.is_empty() {
let i = rand::random::<usize>() % input.len();
if rand::random() {
input.insert(i, rand::random());
} else {
input.remove(i);
}
}
}
fn main() {
let mut population: Vec<(Vec<u8>, OrderedFloat<f64>)> = (0..2048)
.map(|_| loop {
let r: u64 = rand::random();
let v = r.to_le_bytes().to_vec();
let fitness = eval_fitness(&v);
if fitness > 0.0.into() {
return (v, fitness);
}
})
.collect();
println!("generation,fitness");
for generation in 0.. {
population.sort_unstable_by_key(|f| f.1);
if generation % 100 == 0 {
eprintln!(
"GENERATION {}; MAX FITNESS {} LEN {}",
generation,
population.last().unwrap().1,
population.last().unwrap().0.len()
);
if population.last().unwrap().1 > 0.0.into() {
let ops = Covenant::from_bytes(&population.last().unwrap().0.clone())
.unwrap()
.to_ops();
eprintln!("{:?}", ops);
}
}
if generation % 10 == 0 {
println!("{},{}", generation, population.last().unwrap().1);
}
for i in 0..population.len() / 2 {
population[i] = population[population.len() / 2 + i].clone();
}
let halflen = population.len() / 2;
population[..halflen].par_iter_mut().for_each(|elem| {
mutate(&mut elem.0);
});
population
.iter_mut()
.for_each(|elem| elem.1 = eval_fitness(&elem.0));
}
}