diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..6a3d848 --- /dev/null +++ b/src/args.rs @@ -0,0 +1,74 @@ +use clap::{App,Arg,SubCommand}; +use crate::machine::Computer; +use std::fs; + +pub enum Command { + ComputeFuel(Vec), + RunComputer(Computer), +} + +fn is_number(s: String) -> Result<(), String> { + match u64::from_str_radix(&s, 10) { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } +} + +fn is_file(s: String) -> Result<(), String> { + match fs::metadata(&s) { + Err(e) => Err(e.to_string()), + Ok(md) if md.is_file() => Ok(()), + _ => Err(format!("{} is not a file.", s)) + } +} + +impl Command { + pub fn get() -> Command { + let matches = App::new("My Advent of Code Thing") + .version("1.0") + .author("Adam Wick ") + .about("Runs advent of code programs") + .subcommand(SubCommand::with_name("fuel") + .about("runs the fuel computation from day1") + .arg(Arg::with_name("NUM") + .help("The mass of the ship") + .multiple(true) + .validator(is_number)) + ) + .subcommand(SubCommand::with_name("compute") + .about("run the given computer") + .arg(Arg::with_name("START_POSITION") + .short("p") + .long("start-position") + .help("The starting position to execute from.") + .default_value("0") + .validator(is_number)) + .arg(Arg::with_name("COMPUTER") + .index(1) + .help("The computer to run.") + .required(true) + .validator(is_file)) + ) + .get_matches(); + + if let Some(problem1) = matches.subcommand_matches("fuel") { + match problem1.values_of("NUM") { + None => + println!("ERROR: No values to compute fuel for!"), + Some(masses) => { + let args = masses.map(|x| u64::from_str_radix(x, 10).unwrap()).collect(); + return Command::ComputeFuel(args); + } + } + } + + if let Some(problem2) = matches.subcommand_matches("compute") { + let start_pos_str = problem2.value_of("START_POSITION").unwrap(); + let start_pos = usize::from_str_radix(&start_pos_str, 10).unwrap(); + let mut computer = Computer::load(problem2.value_of("COMPUTER").unwrap(), start_pos); + return Command::RunComputer(computer); + } + + panic!("Failed to run a reasonable command."); + } +} \ No newline at end of file diff --git a/src/fuel.rs b/src/fuel.rs new file mode 100644 index 0000000..47c6d79 --- /dev/null +++ b/src/fuel.rs @@ -0,0 +1,25 @@ +fn calculate_base_fuel(mass: u64) -> u64 { + let div3 = mass / 3; + + if div3 >= 2 { + div3 - 2 + } else { + 0 + } +} + +pub fn calculate_fuel(mass: u64) -> u64 { + let mut res = calculate_base_fuel(mass); + let mut last_round = res; + + loop { + let new_round = calculate_base_fuel(last_round); + if new_round == 0 { + return res; + } else { + res += new_round; + last_round = new_round; + } + } +} + diff --git a/src/machine.rs b/src/machine.rs new file mode 100644 index 0000000..88aa289 --- /dev/null +++ b/src/machine.rs @@ -0,0 +1,119 @@ +use std::fs; +use std::str; + +const ADD: u64 = 1; +const MULTIPLY: u64 = 2; +const HALT: u64 = 99; + +#[derive(Clone, Debug, PartialEq)] +pub struct Computer { + memory: Vec, + position: usize +} + +impl Computer { + pub fn load(path: &str, position: usize) -> Computer { + let mut memory = vec![]; + let byte_buffer = fs::read(path).unwrap(); + let char_buffer = str::from_utf8(&byte_buffer).unwrap(); + + let mut current = 0; + for c in char_buffer.chars() { + match c { + ',' => { + memory.push(current); + current = 0; + } + _ if c.is_digit(10) => { + let val = c.to_digit(10).unwrap() as u64; + current = (current * 10) + val; + } + _ if c.is_whitespace() => { + } + _ => { + panic!("Unrecognized character: '{}'", c); + } + } + } + memory.push(current); + + Computer{ memory, position } + } + + pub fn show(&self) { + for (idx, val) in self.memory.iter().enumerate() { + println!("{:08}: {}", idx, val); + } + println!("POSITION: {}", self.position); + } + + pub fn read(&self, idx: usize) -> u64 { + self.memory[idx] + } + + pub fn write(&mut self, idx: usize, val: u64) { + self.memory[idx] = val; + } + + fn step(&mut self) { + match self.read(self.position) { + ADD => { + let arg1 = self.read(self.position + 1) as usize; + let arg2 = self.read(self.position + 2) as usize; + let dest = self.read(self.position + 3) as usize; + + self.write(dest, self.read(arg1) + self.read(arg2)); + self.position += 4; + } + MULTIPLY => { + let arg1 = self.read(self.position + 1) as usize; + let arg2 = self.read(self.position + 2) as usize; + let dest = self.read(self.position + 3) as usize; + + self.write(dest, self.read(arg1) * self.read(arg2)); + self.position += 4; + } + HALT => {} + /* */ + unknown_pos => + panic!("Unknown instruction {}", unknown_pos) + } + } + + fn halted(&self) -> bool { + self.read(self.position) == HALT + } + + pub fn run(&mut self) { + while !self.halted() { + self.step(); + } + } +} + +#[test] +fn test_examples() { + let mut example1 = Computer{ memory: vec![1,0,0,0,99], position: 0 }; + let answer1 = Computer{ memory: vec![2,0,0,0,99], position: 4 }; + example1.step(); + assert_eq!(example1, answer1); + assert!(example1.halted()); + + let mut example2 = Computer{ memory: vec![2,3,0,3,99], position: 0 }; + let answer2 = Computer{ memory: vec![2,3,0,6,99], position: 4 }; + example2.step(); + assert_eq!(example2, answer2); + assert!(example2.halted()); + + let mut example3 = Computer{ memory: vec![2,4,4,5,99,0], position: 0 }; + let answer3 = Computer{ memory: vec![2,4,4,5,99,9801], position: 4 }; + example3.step(); + assert_eq!(example3, answer3); + assert!(example3.halted()); + + let mut example4 = Computer{ memory: vec![1,1,1,4,99,5,6,0,99], position: 0 }; + let answer4 = Computer{ memory: vec![30,1,1,4,2,5,6,0,99], position: 8 }; + example4.run(); + assert_eq!(example4, answer4); + assert!(example4.halted()); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8a9cd2d..5af7805 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,144 +1,44 @@ -use clap::{App,Arg,SubCommand}; +mod args; +mod fuel; +mod machine; + +use crate::args::Command; +use crate::fuel::calculate_fuel; +use crate::machine::{Computer}; use std::fs; -use std::str; - -fn is_number(s: String) -> Result<(), String> { - match u64::from_str_radix(&s, 10) { - Ok(_) => Ok(()), - Err(e) => Err(e.to_string()), - } -} - -fn is_file(s: String) -> Result<(), String> { - match fs::metadata(&s) { - Err(e) => Err(e.to_string()), - Ok(md) if md.is_file() => Ok(()), - _ => Err(format!("{} is not a file.", s)) - } -} - -fn calculate_base_fuel(mass: u64) -> u64 { - let div3 = mass / 3; - - if div3 >= 2 { - div3 - 2 - } else { - 0 - } -} - -fn calculate_fuel(mass: u64) -> u64 { - let mut res = calculate_base_fuel(mass); - let mut last_round = res; - - loop { - let new_round = calculate_base_fuel(last_round); - if new_round == 0 { - return res; - } else { - res += new_round; - last_round = new_round; - } - } -} - -struct Computer { - memory: Vec, - position: usize -} - -impl Computer { - fn load(path: &str, position: usize) -> Computer { - let mut memory = vec![]; - let byte_buffer = fs::read(path).unwrap(); - let char_buffer = str::from_utf8(&byte_buffer).unwrap(); - - let mut current = 0; - for c in char_buffer.chars() { - match c { - ',' => { - memory.push(current); - current = 0; - } - _ if c.is_digit(10) => { - let val = c.to_digit(10).unwrap() as u64; - current = (current * 10) + val; - } - _ if c.is_whitespace() => { - } - _ => { - panic!("Unrecognized character: '{}'", c); - } - } - } - memory.push(current); - - Computer{ memory, position } - } - - fn show(&self) { - for (idx, val) in self.memory.iter().enumerate() { - println!("{:08}: {}", idx, val); - } - println!("POSITION: {}", self.position); - } -} fn main() { - let matches = App::new("My Advent of Code Thing") - .version("1.0") - .author("Adam Wick ") - .about("Runs advent of code programs") - .subcommand(SubCommand::with_name("fuel") - .about("runs the fuel computation from day1") - .arg(Arg::with_name("NUM") - .help("The mass of the ship") - .multiple(true) - .validator(is_number)) - ) - .subcommand(SubCommand::with_name("compute") - .about("run the given computer") - .arg(Arg::with_name("START_POSITION") - .short("p") - .long("start-position") - .help("The starting position to execute from.") - .default_value("0") - .validator(is_number)) - .arg(Arg::with_name("COMPUTER") - .index(1) - .help("The computer to run.") - .required(true) - .validator(is_file)) - ) - .get_matches(); + match Command::get() { + Command::ComputeFuel(masses) => { + let mut total = 0; + + for mass in masses { + let fuel = calculate_fuel(mass); + println!("Mass {}: {} fuel", mass, fuel); + total += fuel; + } + + println!("TOTAL FUEL: {}", total); + } - if let Some(problem1) = matches.subcommand_matches("fuel") { - let mut total = 0; - - match problem1.values_of("NUM") { - None => - println!("ERROR: No values to compute fuel for!"), - Some(masses) => { - for mass_str in masses { - let mass = u64::from_str_radix(&mass_str, 10).unwrap(); - let fuel = calculate_fuel(mass); - println!("Mass {}: {} fuel", mass, fuel); - total += fuel; + Command::RunComputer(initial) => { + println!("Initial Computer:"); + initial.show(); + println!("Searching ..."); + for noun in 0..99 { + for verb in 0..99 { + let mut proposed = initial.clone(); + proposed.write(1, noun); + proposed.write(2, verb); + proposed.run(); + if proposed.read(0) == 19690720 { + println!("Noun: {}", noun); + println!("Verb: {}", verb); + println!("ANSWER: {}", 100 * noun + verb); + break; + } } - - println!("TOTAL FUEL: {}", total); - std::process::exit(0); } } } - - if let Some(problem2) = matches.subcommand_matches("compute") { - let start_pos_str = problem2.value_of("START_POSITION").unwrap(); - let start_pos = usize::from_str_radix(&start_pos_str, 10).unwrap(); - let mut computer = Computer::load(problem2.value_of("COMPUTER").unwrap(), start_pos); - println!("Initial Computer:"); - computer.show(); - } - - println!("Failed to run a reasonable command."); -} + }