From 7f22b4e425da32c3b299be1ce7b5ff1e540ecebe Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Sat, 12 Apr 2025 00:06:07 -0500 Subject: [PATCH] done with pysh code functions --- src/instructions/code.rs | 225 ++++++++++++++++++++++++++++++++++++++- src/push/state.rs | 167 +++++++++++++++++++++++++++++ 2 files changed, 387 insertions(+), 5 deletions(-) diff --git a/src/instructions/code.rs b/src/instructions/code.rs index 8a09807..b4a6e9a 100644 --- a/src/instructions/code.rs +++ b/src/instructions/code.rs @@ -426,18 +426,77 @@ make_instruction_clone!(exec, int, _size, Gene, 1); /// Returns a nested element inside a block based on an int. pub fn _extract(vals: Vec, auxs: Vec) -> Option { - Some(match vals[0].clone() { - Gene::Block(val) => { - if *val.len() == 0 { + match vals[0].clone() { + block @ Gene::Block(_) => { + let block_len = block.rec_len(); + if block_len == 0 { return None; } else { - let ndx = (auxs[0] % *val.len()).abs(); - // @TODO: Finish this later! + let ndx = (auxs[0] % block_len as i128).abs() as usize; + Some(vals[0].clone().code_at_point(ndx)?) } } + val => Some(val), + } +} +make_instruction_aux!(code, code, _extract, Gene, 1, int, 1, i128); + +/// Inserts a gene at a given position in into the top block based off an +/// int from the top of the int stack. The top code item is coerced into a block +/// if needed. +pub fn _insert(vals: Vec, auxs: Vec) -> Option { + let mut block = match vals[0].clone() { + iblock @ Gene::Block(_) => iblock, + val => Gene::Block(Box::new(vec![val])), + }; + if block.rec_len() == 0 { + return _combine(vec![block, vals[1].clone()]); + } + let ndx = auxs[0].abs() as usize % block.rec_len(); + block.with_code_inserted_at_point(vals[1].clone(), ndx); + Some(block) +} +make_instruction_aux!(code, code, _insert, Gene, 2, int, 1, i128); + +/// Pushes the first position of the 2nd code item within the top code item. +/// If not found, pushes -1. If top code item isn't a block, returns 0 if top +/// two code items equal, -1 otherwise. +pub fn _first_position(vals: Vec) -> Option { + let bad_cond: bool = match &vals[0] { + Gene::Block(val) => val.len() == 0, + _ => true, + }; + if bad_cond { + if vals[0] == vals[1] { + return Some(0); + } + } else { + match &vals[0] { + Gene::Block(val) => { + for (idx, el) in val.iter().enumerate() { + if el == &vals[1] { + return Some(idx as i128); + } + } + } + _ => panic!("Error: Invariant of only a block failed in _first_position!"), + } + } + Some(-1) +} +make_instruction_clone!(code, int, _first_position, Gene, 2); + +/// Reverses the top block. Does nothing if not a block. +pub fn _reverse(vals: Vec) -> Option { + Some(match vals[0].clone() { + Gene::Block(mut val) => { + val.reverse(); + Gene::Block(val) + } val => val, }) } +make_instruction_clone!(code, code, _reverse, Gene, 1); #[cfg(test)] mod tests { @@ -1027,4 +1086,160 @@ mod tests { code_size(&mut test_state); assert_eq!(vec![2], test_state.int); } + + #[test] + fn extract_test() { + let mut test_state = EMPTY_STATE; + + let test_code = vec![Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ]))]; + + test_state.code = test_code.clone(); + test_state.int = vec![0]; + code_member(&mut test_state); + assert_eq!(test_code.clone(), test_state.code); + + test_state.code = test_code.clone(); + test_state.int = vec![2]; + code_extract(&mut test_state); + assert_eq!(vec![Gene::GeneInt(1)], test_state.code); + + test_state.code = test_code.clone(); + test_state.int = vec![4]; + code_extract(&mut test_state); + assert_eq!(vec![Gene::GeneInt(4)], test_state.code); + + test_state.code = test_code.clone(); + test_state.int = vec![9]; + code_extract(&mut test_state); + assert_eq!( + vec![Gene::Block(Box::new(vec![Gene::GeneBoolean(false)]))], + test_state.code + ); + } + + #[test] + fn insert_test() { + let mut test_state = EMPTY_STATE; + + test_state.code = vec![ + Gene::GeneInt(20), + Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])), + ])), + ]; + test_state.int = vec![1]; + let inserted_block = vec![Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::GeneInt(20), + Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])), + ]))]; + code_insert(&mut test_state); + assert_eq!(inserted_block, test_state.code); + + test_state.code = vec![ + Gene::GeneInt(20), + Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ])), + ]; + test_state.int = vec![4]; + let inserted_block = vec![Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneInt(20), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ]))]; + code_insert(&mut test_state); + assert_eq!(inserted_block, test_state.code); + } + + #[test] + fn first_position_test() { + let mut test_state = EMPTY_STATE; + + test_state.code = vec![ + Gene::GeneInt(20), + Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::GeneInt(20), + Gene::GeneFloat(dec!(6.0)), + ])), + ]; + code_first_position(&mut test_state); + assert_eq!(vec![2], test_state.int); + test_state.int.clear(); + + test_state.code = vec![Gene::GeneBoolean(false), Gene::GeneBoolean(false)]; + code_first_position(&mut test_state); + assert_eq!(vec![0], test_state.int); + test_state.int.clear(); + + test_state.code = vec![Gene::GeneBoolean(false), Gene::GeneBoolean(true)]; + code_first_position(&mut test_state); + assert_eq!(vec![-1], test_state.int); + test_state.int.clear(); + + test_state.code = vec![ + Gene::GeneInt(-6), + Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::GeneInt(20), + Gene::GeneFloat(dec!(6.0)), + ])), + ]; + code_first_position(&mut test_state); + assert_eq!(vec![-1], test_state.int); + test_state.int.clear(); + } + + #[test] + fn code_reverse_test() { + let mut test_state = EMPTY_STATE; + + test_state.code = vec![Gene::GeneInt(1)]; + code_reverse(&mut test_state); + assert_eq!(vec![Gene::GeneInt(1)], test_state.code); + + test_state.code = vec![Gene::Block(Box::new(vec![ + Gene::GeneBoolean(false), + Gene::GeneInt(1), + Gene::GeneInt(20), + ]))]; + code_reverse(&mut test_state); + assert_eq!( + vec![Gene::Block(Box::new(vec![ + Gene::GeneInt(20), + Gene::GeneInt(1), + Gene::GeneBoolean(false), + ]))], + test_state.code + ); + } } diff --git a/src/push/state.rs b/src/push/state.rs index 0b09171..5b9d885 100644 --- a/src/push/state.rs +++ b/src/push/state.rs @@ -54,3 +54,170 @@ pub enum Gene { Block(Box>), CrossoverPadding, } + +impl Gene { + /// Returns the len of a gene. If the gene is a block, returns + /// the size of the block counting the size of nested blocks. + pub fn rec_len(&self) -> usize { + let mut size: usize = 0; + match self { + Gene::Block(val) => { + for el in val.iter() { + match el { + iblock @ Gene::Block(val) => size += iblock.rec_len() + 1, + _ => size += 1, + } + } + } + _ => size += 1, + }; + size + } + + /// Extracts code at a point in the genome. Recurses into a block + /// if necessary. Point is based on an int. Pulled straight from HushGP. + pub fn code_at_point(self, index: usize) -> Option { + if index == 0 { + return Some(self); + } + let mut idx = index; + match self { + Gene::Block(val) => { + for el in val.iter() { + idx -= 1; + if idx == 0 { + return Some(el.clone()); + } + match el { + iblock @ Gene::Block(_) => { + if let Some(next_depth) = iblock.clone().code_at_point(idx) { + return Some(next_depth); + } + idx -= iblock.rec_len(); + } + _ => continue, + } + } + return None; + } + val => Some(val), + } + } + + /// Insert an element into a block recursively counting along the way. + /// Depth first. Modifies in-place. + pub fn with_code_inserted_at_point(&mut self, gene: Gene, idx: usize) { + if idx > self.rec_len() { + match self { + Gene::Block(val) => val.push(gene.clone()), + _ => { + panic!("Error: self must be a block for with_code_inserted_at_point to work!") + } + } + } + let _ = self.attempt_code_insert(gene, idx); + } + + /// Attempts to insert an item into a block. + fn attempt_code_insert(&mut self, gene: Gene, index: usize) -> bool { + let mut idx = index; + match self { + Gene::Block(val) => { + for (n, el) in val.iter_mut().enumerate() { + if idx == 0 { + val.insert(n, gene.clone()); + return true; + } + match el { + iblock @ Gene::Block(_) => { + // This line has side effects on iblock if inserts properly. + let success = iblock.attempt_code_insert(gene.clone(), idx - 1); + if success { + return true; + } + idx -= iblock.rec_len() + 1 + } + _ => (), + } + idx -= 1; + } + if idx == 0 { + return true; + } + false + } + _ => panic!("Error: self must be a block for attempt_code_insert to work!"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rec_len_test() { + let block = Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])), + ])); + assert_eq!(4, block.rec_len()); + + let block = Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ])); + assert_eq!(10, block.rec_len()); + + let block = Gene::Block(Box::new(vec![])); + assert_eq!(0, block.rec_len()); + } + + #[test] + fn insert_test() { + let mut block = Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])), + ])); + let inserted_block = Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::GeneInt(20), + Gene::Block(Box::new(vec![Gene::GeneInt(1), Gene::GeneInt(1)])), + ])); + block.with_code_inserted_at_point(Gene::GeneInt(20), 1); + assert_eq!(inserted_block, block); + + let mut block = Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ])); + let inserted_block = Gene::Block(Box::new(vec![ + Gene::GeneBoolean(true), + Gene::GeneInt(1), + Gene::Block(Box::new(vec![ + Gene::GeneInt(4), + Gene::GeneInt(20), + Gene::GeneFloat(dec!(6.0)), + Gene::Block(Box::new(vec![Gene::GeneString(vec!['t'])])), + ])), + Gene::GeneInt(10), + Gene::Block(Box::new(vec![Gene::GeneBoolean(false)])), + ])); + block.with_code_inserted_at_point(Gene::GeneInt(20), 4); + assert_eq!(inserted_block, block); + } +}