use clap::Args;
use derivative::Derivative;
use derive_builder::Builder;
use rand::distributions::Standard;
use rand::prelude::Distribution;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use tracing::trace;
use crate::utils::random::generator;
use super::engines::generate_engine::{Generate, GenerateEngine};
use super::engines::mutate_engine::{Mutate, MutateEngine};
use super::environment::State;
use super::registers::Registers;
use derive_more::Display;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Copy, Deserialize)]
pub enum Mode {
External,
Internal,
}
#[derive(Clone, Copy, Debug, Display, Serialize, PartialEq, Eq, Deserialize)]
pub enum Op {
#[display(fmt = "+")]
Add,
#[display(fmt = "*")]
Mult,
#[display(fmt = "/")]
Divide,
#[display(fmt = "-")]
Sub,
}
impl Op {
pub fn apply(&self, a: f64, b: f64) -> f64 {
match *self {
Op::Add => a + b,
Op::Mult => a * b,
Op::Divide => a / b, Op::Sub => a - b,
}
}
}
impl Distribution<Op> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Op {
match rng.gen_range(0..=3) {
0 => Op::Add,
1 => Op::Mult,
2 => Op::Divide,
_ => Op::Sub,
}
}
}
impl Distribution<Mode> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mode {
match rng.gen_bool(0.5) {
false => Mode::External,
true => Mode::Internal,
}
}
}
#[derive(Clone, Derivative, Debug, Serialize, Args, PartialEq, Deserialize, Builder)]
#[derivative(Copy)]
pub struct InstructionGeneratorParameters {
#[arg(long, default_value = "1")]
#[builder(default = "1")]
pub n_extras: usize,
#[arg(long, default_value = "10.")]
#[builder(default = "10.")]
pub external_factor: f64,
#[arg(skip)]
pub n_actions: usize,
#[arg(skip)]
pub n_inputs: usize,
}
impl InstructionGeneratorParameters {
pub fn n_registers(&self) -> usize {
self.n_actions + self.n_extras
}
}
#[derive(Serialize, PartialEq, Debug, Deserialize, Derivative)]
#[derivative(Copy, Clone)]
pub struct Instruction {
src_idx: usize,
tgt_idx: usize,
mode: Mode,
op: Op,
external_factor: f64,
}
impl Generate<InstructionGeneratorParameters, Instruction> for GenerateEngine {
fn generate(using: InstructionGeneratorParameters) -> Instruction {
let src_idx = generator().gen_range(0..using.n_registers());
let mode = generator().gen();
let upper_bound_target_index = if mode == Mode::External {
using.n_inputs
} else {
using.n_registers()
};
let target_index = generator().gen_range(0..upper_bound_target_index);
let executable = generator().gen();
Instruction {
src_idx,
tgt_idx: target_index,
mode,
op: executable,
external_factor: using.external_factor,
}
}
}
impl Mutate<InstructionGeneratorParameters, Instruction> for MutateEngine {
fn mutate(instruction: &mut Instruction, using: InstructionGeneratorParameters) {
let mutated = GenerateEngine::generate(using);
let swap_target = generator().gen();
let swap_source = generator().gen();
let swap_exec = generator().gen();
if swap_target {
instruction.mode = mutated.mode;
instruction.tgt_idx = mutated.tgt_idx;
}
if swap_source {
instruction.src_idx = mutated.src_idx;
}
if swap_exec {
instruction.op = mutated.op;
}
}
}
impl Instruction {
pub fn apply(&self, registers: &mut Registers, input: &impl State) {
let target_value = match self.mode {
Mode::External => self.external_factor * input.get_value(self.tgt_idx),
_ => *registers.get(self.tgt_idx),
};
let source_value = *registers.get(self.src_idx);
let new_source_value = self.op.apply(source_value, target_value);
trace!(
op = %self.op,
src_idx = self.src_idx,
tgt_idx = self.tgt_idx,
mode = ?self.mode,
source_value = source_value,
target_value = target_value,
result = new_source_value,
"Instruction applied"
);
registers.update(self.src_idx, new_source_value);
}
}