use crate::input::ConsoleAction::SoftReset; use crate::{Rng, DISABLE_START_PRESSES_AFTER, MUTATION_RATE, MUTATION_RATE_SOFT_RESET}; use std::fs::File; use std::io::BufRead; use std::{fs, io}; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum ConsoleAction { None, SoftReset, } #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct FrameInput { pub console_action: ConsoleAction, pub player_1_input: u8, pub player_2_input: u8, } #[derive(Clone, Eq, PartialEq, Hash)] pub struct FuzzingInput { frames: Vec, disable_start_after: usize, pub(crate) mutated: bool, } impl FuzzingInput { pub fn disable_start_after(&mut self, frames: usize) { self.disable_start_after = frames; } pub fn export(&self, filename: String, frames: usize) { let mut tas = format!(""); println!("Found path to world werid...dumping tas"); for i in 0..frames { tas = format!("{}|", tas); let input = self.get_frame_input(i).unwrap(); if input.console_action == SoftReset { tas = format!("{}1", tas); } else { tas = format!("{}0", tas); } tas = format!("{}|", tas); if input.player_1_input >> 7 == 1 { tas = format!("{}R", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 6) & 0x01 == 1 { tas = format!("{}L", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 5) & 0x01 == 1 { tas = format!("{}D", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 4) & 0x01 == 1 { tas = format!("{}U", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 3) & 0x01 == 1 { tas = format!("{}T", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 2) & 0x01 == 1 { tas = format!("{}S", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 1) & 0x01 == 1 { tas = format!("{}B", tas); } else { tas = format!("{}.", tas); } if (input.player_1_input >> 0) & 0x01 == 1 { tas = format!("{}A", tas); } else { tas = format!("{}.", tas); } tas = format!("{}|", tas); if input.player_2_input >> 7 == 1 { tas = format!("{}R", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 6) & 0x01 == 1 { tas = format!("{}L", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 5) & 0x01 == 1 { tas = format!("{}D", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 4) & 0x01 == 1 { tas = format!("{}U", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 3) & 0x01 == 1 { tas = format!("{}T", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 2) & 0x01 == 1 { tas = format!("{}S", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 1) & 0x01 == 1 { tas = format!("{}B", tas); } else { tas = format!("{}.", tas); } if (input.player_2_input >> 0) & 0x01 == 1 { tas = format!("{}A", tas); } else { tas = format!("{}.", tas); } tas = format!("{}||\n", tas); } fs::write(filename, tas).expect("Unable to write file"); } pub fn mutate_from(&mut self, rng: &mut Rng, frame: usize, frames_to_consider: usize) { self.mutated = true; for frame_num in frame..(frame + frames_to_consider) { while frame_num >= self.frames.len() { self.frames.push(FrameInput { console_action: ConsoleAction::None, player_1_input: rng.next() as u8, player_2_input: rng.next() as u8, }); } let check = (rng.next() % frames_to_consider as u32) as f64; if (check / frames_to_consider as f64) < MUTATION_RATE { if frame_num < self.frames.len() { self.frames[frame_num].player_1_input = rng.next() as u8; self.frames[frame_num].player_2_input = rng.next() as u8; } } if (check / frames_to_consider as f64) < MUTATION_RATE_SOFT_RESET { if frame_num < self.frames.len() { self.frames[frame_num].console_action = ConsoleAction::SoftReset } } if frame_num > DISABLE_START_PRESSES_AFTER { self.frames[frame_num].player_1_input &= 0b11110011; } } } // Get the fuzzing input for the give frame... pub fn get_frame_input(&self, frame: usize) -> Option<&FrameInput> { self.frames.get(frame) } // Hacky code to parse an fm2 movie/input file into our initial fuzzing input pub fn load(fm2: &str) -> FuzzingInput { if fm2 == "testrom" { return FuzzingInput { frames: vec![], disable_start_after: 0, mutated: false, }; } let file = File::open(fm2).unwrap(); let mut frames = vec![]; let lines = io::BufReader::new(file).lines(); for (_line_num, line) in lines.enumerate() { if let Ok(lip) = line { let ip = lip.as_bytes(); if ip.is_empty() { continue; } if ip[0] as char == '|' { let mut frame_input = FrameInput { console_action: ConsoleAction::None, player_1_input: 0, player_2_input: 0, }; if ip[1] as char == '1' { frame_input.console_action = ConsoleAction::SoftReset; } // RLDUTSBA if ip[3] as char == 'R' { frame_input.player_1_input |= 128 } if ip[4] as char == 'L' { frame_input.player_1_input |= 64 } if ip[5] as char == 'D' { frame_input.player_1_input |= 32 } if ip[6] as char == 'U' { frame_input.player_1_input |= 16 } if ip[7] as char == 'T' { frame_input.player_1_input |= 8 } if ip[8] as char == 'S' { frame_input.player_1_input |= 4 } if ip[9] as char == 'B' { frame_input.player_1_input |= 2 } if ip[10] as char == 'A' { frame_input.player_1_input |= 1 } // RLDUTSBA if ip[12] as char == 'R' { frame_input.player_2_input |= 128 } if ip[13] as char == 'L' { frame_input.player_2_input |= 64 } if ip[14] as char == 'D' { frame_input.player_2_input |= 32 } if ip[15] as char == 'U' { frame_input.player_2_input |= 16 } if ip[16] as char == 'T' { frame_input.player_2_input |= 8 } if ip[17] as char == 'S' { frame_input.player_2_input |= 4 } if ip[18] as char == 'B' { frame_input.player_2_input |= 2 } if ip[19] as char == 'A' { frame_input.player_2_input |= 1 } frames.push(frame_input); } } } FuzzingInput { frames, disable_start_after: 0xFFFFFFFF, mutated: false, } } }