nesfuzz/src/input.rs

263 lines
8.8 KiB
Rust

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<FrameInput>,
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,
}
}
}