Add random program generation and mutation.

This commit is contained in:
zjkree
2025-01-13 11:57:18 +01:00
parent 856779e0bc
commit c5538e35bb
10 changed files with 286 additions and 26 deletions
+79
View File
@@ -0,0 +1,79 @@
use rand::Rng;
use crate::compiler::ast::{UntypedAst, OpCode};
pub fn random_sublist_ast(rng: &mut impl Rng, max_depth: usize) -> UntypedAst {
// 1) Choose how many children the top-level `Sublist` will have.
let len = rng.gen_range(1..=3);
// 2) Build a vector of sub-ASTs by calling `random_ast` for each child
let mut children = Vec::with_capacity(len);
for _ in 0..len {
// We start at `depth=1` because the top-level sublist itself is `depth=0`.
children.push(random_ast(rng, 1, max_depth));
}
// 3) Return a `Sublist` as the root node
UntypedAst::Sublist(children)
}
/// Generate a random `UntypedAst` by recursively building sub-trees,
/// int literals, or instructions.
///
/// - `depth` tracks how deep we are in the tree.
/// - `max_depth` is the maximum allowed depth to prevent infinite recursion.
pub fn random_ast(rng: &mut impl Rng, depth: usize, max_depth: usize) -> UntypedAst {
if depth >= max_depth {
// Return something "terminal,"
// e.g. an IntLiteral or a single Instruction
random_terminal(rng)
} else {
// Weighted choice:
// 0 => IntLiteral,
// 1 => single Instruction,
// 2 => Sublist with children
let choice = rng.gen_range(0..3);
match choice {
0 => UntypedAst::IntLiteral(rng.gen_range(-10..10)),
1 => UntypedAst::Instruction(random_opcode(rng)),
2 => {
// Make a sublist with 1..=3 children
let len = rng.gen_range(1..=3);
let mut children = Vec::with_capacity(len);
for _ in 0..len {
children.push(random_ast(rng, depth + 1, max_depth));
}
UntypedAst::Sublist(children)
}
_ => unreachable!(),
}
}
}
/// Generate a random "terminal" node.
/// Typically either an int literal or a single instruction.
fn random_terminal(rng: &mut impl Rng) -> UntypedAst {
// Weighted choice:
// 0 => IntLiteral
// 1 => single Instruction
let choice = rng.gen_range(0..2);
match choice {
0 => UntypedAst::IntLiteral(rng.gen_range(-10..10)),
1 => UntypedAst::Instruction(random_opcode(rng)),
_ => unreachable!(),
}
}
/// Generate a random `OpCode` from your known set of instructions.
pub fn random_opcode(rng: &mut impl Rng) -> OpCode {
let choice = rng.gen_range(0..6);
// because we have 6 variants: Noop, Plus, Minus, Mult, Dup, Pop
match choice {
0 => OpCode::Noop,
1 => OpCode::Plus,
2 => OpCode::Minus,
3 => OpCode::Mult,
4 => OpCode::Dup,
5 => OpCode::Pop,
_ => unreachable!(),
}
}