From 2851f7c80d70ee9d33a8e0c106c00749fd28965d Mon Sep 17 00:00:00 2001 From: Chris Alge Date: Mon, 13 Mar 2023 21:47:29 +0100 Subject: [PATCH] 2019 cleanup: Introduced proper Error types for most days up until day 14 --- .../src/lib.rs | 32 ++++++---- 2019/day02_1202_program_alarm/src/lib.rs | 13 ++-- 2019/day04_secure_container/src/lib.rs | 12 ++-- .../src/lib.rs | 10 +-- 2019/day06_universal_orbit_map/src/lib.rs | 29 +++++---- 2019/day07_amplification_circuit/src/lib.rs | 14 ++-- 2019/day09_sensor_boost/src/lib.rs | 10 +-- 2019/day11_space_police/src/lib.rs | 10 +-- 2019/day12_the_n-body_problem/src/lib.rs | 39 +++++++++-- .../tests/sample_input | 2 +- 2019/day14_space_stoichiometry/src/lib.rs | 64 +++++++++++++------ 11 files changed, 150 insertions(+), 85 deletions(-) diff --git a/2019/day01_the_tyranny_of_the_rocket_equation/src/lib.rs b/2019/day01_the_tyranny_of_the_rocket_equation/src/lib.rs index e67dcd4..f24960d 100644 --- a/2019/day01_the_tyranny_of_the_rocket_equation/src/lib.rs +++ b/2019/day01_the_tyranny_of_the_rocket_equation/src/lib.rs @@ -1,16 +1,22 @@ -pub fn run(input: &str) -> (usize, usize) { - let first = input.lines().map(|line| line.parse::().unwrap() / 3 -2).sum(); +use std::num::ParseIntError; + +pub fn run(input: &str) -> Result<(usize, usize), ParseIntError> { + let first = input.lines().map(|line| line.parse::().map(|i| i/3-2)).sum::>()?; let second = input.lines().map(|line| { - let weight = line.parse::().unwrap(); - let mut total = (weight/3).saturating_sub(2); - let mut next = (total/3).saturating_sub(2); - while next > 0 { - total += next; - next = (next/3).saturating_sub(2); + let parsed = line.parse::(); + if let Ok(weight) = parsed { + let mut total = (weight/3).saturating_sub(2); + let mut next = (total/3).saturating_sub(2); + while next > 0 { + total += next; + next = (next/3).saturating_sub(2); + } + Ok(total) + } else { + parsed } - total - }).sum::(); - (first, second) + }).sum::>()?; + Ok((first, second)) } #[cfg(test)] @@ -25,12 +31,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (34241, 51316)); + assert_eq!(run(&sample_input), Ok((34241, 51316))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (3363033, 5041680)); + assert_eq!(run(&challenge_input), Ok((3363033, 5041680))); } } diff --git a/2019/day02_1202_program_alarm/src/lib.rs b/2019/day02_1202_program_alarm/src/lib.rs index 10a14a8..ca78574 100644 --- a/2019/day02_1202_program_alarm/src/lib.rs +++ b/2019/day02_1202_program_alarm/src/lib.rs @@ -1,8 +1,9 @@ +use std::num::ParseIntError; + use intcode_processor::intcode_processor::Cpu; -pub fn run(input: &str) -> (isize, isize) { - let memory = input.split(',').map(|s| s.parse::().unwrap()).collect(); - let cpu = Cpu::with_memory(memory); +pub fn run(input: &str) -> Result<(isize, isize), ParseIntError> { + let cpu = Cpu::try_with_memory_from_str(input)?; let mut cpu_1 = cpu.clone(); cpu_1.set(1, 12); cpu_1.set(2, 2); @@ -21,7 +22,7 @@ pub fn run(input: &str) -> (isize, isize) { } } } - (first, second) + Ok((first, second)) } #[cfg(test)] @@ -36,12 +37,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (3500, 213)); + assert_eq!(run(&sample_input), Ok((3500, 213))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (3760627, 7195)); + assert_eq!(run(&challenge_input), Ok((3760627, 7195))); } } diff --git a/2019/day04_secure_container/src/lib.rs b/2019/day04_secure_container/src/lib.rs index d242b5c..c30c224 100644 --- a/2019/day04_secure_container/src/lib.rs +++ b/2019/day04_secure_container/src/lib.rs @@ -1,9 +1,11 @@ -pub fn run(input: &str) -> (usize, usize) { - let range: Vec<_> = input.split('-').map(|n| n.parse::().unwrap()).collect(); +use std::num::ParseIntError; + +pub fn run(input: &str) -> Result<(usize, usize), ParseIntError> { + let range: Vec<_> = input.split('-').map(|n| n.parse::()).collect::, _>>()?; let valid_1: Vec<_> = (range[0]..=range[1]).filter(is_valid_1).collect(); let first = valid_1.len(); let second = valid_1.into_iter().filter(is_valid_2).count(); - (first, second) + Ok((first, second)) } fn is_valid_1(password: &usize) -> bool { @@ -55,12 +57,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (36, 14)); + assert_eq!(run(&sample_input), Ok((36, 14))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (921, 603)); + assert_eq!(run(&challenge_input), Ok((921, 603))); } } diff --git a/2019/day05_sunny_with_a_chance_of_asteroids/src/lib.rs b/2019/day05_sunny_with_a_chance_of_asteroids/src/lib.rs index a79eea9..9799ceb 100644 --- a/2019/day05_sunny_with_a_chance_of_asteroids/src/lib.rs +++ b/2019/day05_sunny_with_a_chance_of_asteroids/src/lib.rs @@ -1,7 +1,9 @@ +use std::num::ParseIntError; + use intcode_processor::intcode_processor::{Cpu, OutputState}; -pub fn run(input: &str) -> (isize, isize) { - let mut cpu = Cpu::with_memory(input.split(',').map(|s| s.parse::().unwrap()).collect()); +pub fn run(input: &str) -> Result<(isize, isize), ParseIntError> { + let mut cpu = Cpu::try_with_memory_from_str(input)?; let mut cpu_2 = cpu.clone(); cpu.set_input(1); @@ -9,7 +11,7 @@ pub fn run(input: &str) -> (isize, isize) { cpu_2.set_input(5); let second = run_diagnostics(&mut cpu_2); - (first, second) + Ok((first, second)) } fn run_diagnostics(cpu: &mut Cpu) -> isize { @@ -36,6 +38,6 @@ mod tests { #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (9025675, 11981754)); + assert_eq!(run(&challenge_input), Ok((9025675, 11981754))); } } diff --git a/2019/day06_universal_orbit_map/src/lib.rs b/2019/day06_universal_orbit_map/src/lib.rs index 01e848a..e12c89f 100644 --- a/2019/day06_universal_orbit_map/src/lib.rs +++ b/2019/day06_universal_orbit_map/src/lib.rs @@ -6,28 +6,31 @@ struct Orbit { center_id: usize, } -pub fn run(input: &str) -> (usize, usize) { - let graph: Vec<_> = graph_from(input); +pub fn run(input: &str) -> Result<(usize, usize), String> { + let graph: Vec<_> = graph_from(input)?; let first = count_direct_and_indirect_orbits(&graph); let common_orbit = get_common_center(&graph, 1, 2); let second = distance(&graph, common_orbit, 1) + distance(&graph, common_orbit, 2) - 2; - (first, second) + Ok((first, second)) } -fn graph_from(map: &str) -> Vec { +fn graph_from(map: &str) -> Result, String> { let mut bodies = HashMap::from([("COM", 0), ("YOU", 1), ("SAN", 2)]); let mut graph: Vec = Vec::new(); for line in map.lines() { - let (center, trabant) = line.split_once(')').unwrap(); - let mut bodies_len = bodies.len(); - let center_id = *bodies.entry(center).or_insert(bodies_len); - bodies_len = bodies.len(); - let trabant_id = *bodies.entry(trabant).or_insert(bodies_len); - graph.push(Orbit { center_id, trabant_id } ); + if let Some((center, trabant)) = line.split_once(')') { + let mut bodies_len = bodies.len(); + let center_id = *bodies.entry(center).or_insert(bodies_len); + bodies_len = bodies.len(); + let trabant_id = *bodies.entry(trabant).or_insert(bodies_len); + graph.push(Orbit { center_id, trabant_id } ); + } else { + return Err(format!("Malformed input: '{line}' doesn't contain a ')'.")); + } } graph.sort(); - graph + Ok(graph) } fn count_direct_and_indirect_orbits(graph: &[Orbit]) -> usize { @@ -94,12 +97,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (54, 4)); + assert_eq!(run(&sample_input), Ok((54, 4))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (417916, 523)); + assert_eq!(run(&challenge_input), Ok((417916, 523))); } } diff --git a/2019/day07_amplification_circuit/src/lib.rs b/2019/day07_amplification_circuit/src/lib.rs index a2ce4b6..9de24ac 100644 --- a/2019/day07_amplification_circuit/src/lib.rs +++ b/2019/day07_amplification_circuit/src/lib.rs @@ -1,9 +1,9 @@ -use std::{sync::{mpsc, Arc}, thread, collections::VecDeque}; +use std::{sync::{mpsc, Arc}, thread, collections::VecDeque, num::ParseIntError}; use intcode_processor::intcode_processor::{Cpu, OutputState}; -pub fn run(input: &str) -> (isize, isize) { - let template = Cpu::try_with_memory_from_str(input).unwrap(); +pub fn run(input: &str) -> Result<(isize, isize), ParseIntError> { + let template = Cpu::try_with_memory_from_str(input)?; let mut first = 0; for perm in get_permutations(&(0..5).collect()) { let mut output = 0; @@ -82,7 +82,7 @@ pub fn run(input: &str) -> (isize, isize) { } second = second.max(*output.lock().unwrap()); } - (first, second) + Ok((first, second)) } fn get_permutations(numbers: &Vec) -> Vec> { @@ -116,19 +116,19 @@ mod tests { fn test_sample() { let sample_input = read_file("tests/sample_input"); // The second part of the output is not verified, but that's what my solution produces. - assert_eq!(run(&sample_input), (65210, 76543)); + assert_eq!(run(&sample_input), Ok((65210, 76543))); } #[test] fn test_sample_2() { let sample_input = read_file("tests/sample_input_2"); // The first part of the output is not verified, but that's what my solution produces. - assert_eq!(run(&sample_input), (0, 18216)); + assert_eq!(run(&sample_input), Ok((0, 18216))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (46248, 54163586)); + assert_eq!(run(&challenge_input), Ok((46248, 54163586))); } } diff --git a/2019/day09_sensor_boost/src/lib.rs b/2019/day09_sensor_boost/src/lib.rs index 4f81637..225e1d0 100644 --- a/2019/day09_sensor_boost/src/lib.rs +++ b/2019/day09_sensor_boost/src/lib.rs @@ -1,7 +1,9 @@ +use std::num::ParseIntError; + use intcode_processor::intcode_processor::{Cpu, OutputState}; -pub fn run(input: &str) -> (isize, isize) { - let mut cpu = Cpu::try_with_memory_from_str(input).unwrap(); +pub fn run(input: &str) -> Result<(isize, isize), ParseIntError> { + let mut cpu = Cpu::try_with_memory_from_str(input)?; let mut cpu_2 = cpu.clone(); cpu.set_input(1); let first = match cpu.run() { @@ -13,7 +15,7 @@ pub fn run(input: &str) -> (isize, isize) { OutputState::DiagnosticCode(d) => d, e => panic!("Unexpected return code: {e:?}"), }; - (first, second) + Ok((first, second)) } #[cfg(test)] @@ -28,6 +30,6 @@ mod tests { #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (2316632620, 78869)); + assert_eq!(run(&challenge_input), Ok((2316632620, 78869))); } } diff --git a/2019/day11_space_police/src/lib.rs b/2019/day11_space_police/src/lib.rs index 1043232..997ead2 100644 --- a/2019/day11_space_police/src/lib.rs +++ b/2019/day11_space_police/src/lib.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, isize}; +use std::{collections::HashMap, isize, num::ParseIntError}; use intcode_processor::intcode_processor::{Cpu, OutputState}; enum Direction { Up, Left, Down, Right } @@ -39,8 +39,8 @@ impl Direction { } } -pub fn run(input: &str) -> (usize, String) { - let mut cpu_1 = Cpu::try_with_memory_from_str(input).unwrap(); +pub fn run(input: &str) -> Result<(usize, String), ParseIntError> { + let mut cpu_1 = Cpu::try_with_memory_from_str(input)?; let mut cpu_2 = cpu_1.clone(); let mut panels_1 = HashMap::new(); let mut panels_2 = HashMap::from([((0, 0), 1)]); @@ -48,7 +48,7 @@ pub fn run(input: &str) -> (usize, String) { paint(&mut cpu_2, &mut panels_2); let first = panels_1.len(); let second = print(&panels_2); - (first, second) + Ok((first, second)) } fn paint(cpu: &mut Cpu, panels: &mut HashMap<(isize, isize), isize>) { @@ -100,6 +100,6 @@ mod tests { # # # # # # # # # # # # # #### #### #### ### # # ## # # # # "#; - assert_eq!(run(&challenge_input), (2539, expected[1..].to_string())); + assert_eq!(run(&challenge_input), Ok((2539, expected[1..].to_string()))); } } diff --git a/2019/day12_the_n-body_problem/src/lib.rs b/2019/day12_the_n-body_problem/src/lib.rs index d522a7f..ffe7926 100644 --- a/2019/day12_the_n-body_problem/src/lib.rs +++ b/2019/day12_the_n-body_problem/src/lib.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::{iter::Sum, ops::AddAssign, num::ParseIntError}; #[derive(Clone, Copy, PartialEq, Eq)] @@ -37,6 +38,27 @@ impl Coordinate { } } +#[derive(Debug, PartialEq, Eq)] +pub enum ParseMoonError { + ParseIntError(std::num::ParseIntError), + InvalidCoordinates(usize), +} + +impl From for ParseMoonError { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +impl fmt::Display for ParseMoonError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ParseIntError(v) => write!(f, "Error parsing coordinates: {v}"), + Self::InvalidCoordinates(n) => write!(f, "Error reading the coordinates list. It contains {n} components instead of 7."), + } + } +} + #[derive(Clone, PartialEq, Eq)] struct Moon { position: Coordinate, @@ -44,11 +66,13 @@ struct Moon { } impl TryFrom<&str> for Moon { - type Error = ParseIntError; + type Error = ParseMoonError; fn try_from(value: &str) -> Result { let components: Vec<_> = value.split(&['=', ',', '>']).collect(); - assert_eq!(components.len(), 7); + if components.len() != 7 { + return Err(ParseMoonError::InvalidCoordinates(components.len())); + } let x = components[1].parse()?; let y = components[3].parse()?; let z = components[5].parse()?; @@ -82,8 +106,9 @@ impl LunarSystem { } } -pub fn run(input: &str) -> (usize, usize) { - let moons: Vec = input.lines().map(Moon::try_from).collect::, _>>().unwrap(); +pub fn run(input: &str) -> Result<(usize, usize), ParseMoonError> { + let moons: Vec = input.lines().map(Moon::try_from).collect::, _>>()?; + //.unwrap_or_else(|err| panic!("Error parsing input into moons: {err}")); let mut system = LunarSystem { moons: moons.to_vec(), }; for _ in 0..1000 { system.step_motion(); @@ -114,7 +139,7 @@ pub fn run(input: &str) -> (usize, usize) { } } let second = scm(periods[0], scm(periods[1], periods[2])); - (first, second) + Ok((first, second)) } fn scm(lhs: usize, rhs: usize) -> usize { @@ -142,12 +167,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (14645, 4686774924)); + assert_eq!(run(&sample_input), Ok((14645, 4686774924))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (7687, 334945516288044)); + assert_eq!(run(&challenge_input), Ok((7687, 334945516288044))); } } diff --git a/2019/day12_the_n-body_problem/tests/sample_input b/2019/day12_the_n-body_problem/tests/sample_input index 1078293..75064c4 100644 --- a/2019/day12_the_n-body_problem/tests/sample_input +++ b/2019/day12_the_n-body_problem/tests/sample_input @@ -1,4 +1,4 @@ - + diff --git a/2019/day14_space_stoichiometry/src/lib.rs b/2019/day14_space_stoichiometry/src/lib.rs index 1b195e2..b6a9fdf 100644 --- a/2019/day14_space_stoichiometry/src/lib.rs +++ b/2019/day14_space_stoichiometry/src/lib.rs @@ -1,4 +1,25 @@ -use std::collections::{HashMap, VecDeque}; +use std::{collections::{HashMap, VecDeque}, fmt::Display, num::ParseIntError}; + +#[derive(Debug, PartialEq, Eq)] +pub enum ParseError { + ParseIntError(std::num::ParseIntError), + LineMalformed(String), +} + +impl From for ParseError { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +impl Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ParseIntError(e) => write!(f, "Unable to parse into integer: {e}"), + Self::LineMalformed(v) => write!(f, "Line is malformed: {v}"), + } + } +} type Chemical = usize; @@ -13,7 +34,7 @@ struct Reaction { } impl Reaction { - fn from(line: &str, chemicals: &mut Vec) -> Self { + fn try_from(line: &str, chemicals: &mut Vec) -> Result { let mut get_chemical = |name: &str| -> usize { if let Some(idx) = chemicals.iter().position(|c| c == &name.to_string()) { idx @@ -22,34 +43,37 @@ impl Reaction { chemicals.len()-1 } }; - let (in_str, out_str) = line.split_once(" => ").unwrap(); - let in_components: Vec<_> = in_str.split(&[' ', ',']).chain([""].into_iter()).collect(); - assert_eq!(in_components.len()%3, 0); - let out_components: Vec<_> = out_str.split(' ').collect(); - assert_eq!(out_components.len(), 2); - let output = Reagent { - id: get_chemical(out_components[1]), - amount: out_components[0].parse().unwrap(), - }; + if let Some((in_str, out_str)) = line.split_once(" => ") { + let in_components: Vec<_> = in_str.split(&[' ', ',']).chain([""].into_iter()).collect(); + assert_eq!(in_components.len()%3, 0); + let out_components: Vec<_> = out_str.split(' ').collect(); + assert_eq!(out_components.len(), 2); + let output = Reagent { + id: get_chemical(out_components[1]), + amount: out_components[0].parse()?, + }; - let input = in_components.chunks(3).map(|c| Reagent { id: get_chemical(c[1]), amount: c[0].parse::().unwrap(), }).collect(); + let input = in_components.chunks(3).map(|c| Reagent { id: get_chemical(c[1]), amount: c[0].parse::().unwrap(), }).collect(); - Self { - input, - output, + Ok(Self { + input, + output, + }) + } else { + Err(ParseError::LineMalformed(line.to_string())) } } } -pub fn run(input: &str) -> (usize, usize) { +pub fn run(input: &str) -> Result<(usize, usize), ParseError> { let mut chemicals = Vec::new(); - let reactions: Vec<_> = input.lines().map(|line| Reaction::from(line, &mut chemicals)).collect(); + let reactions: Vec<_> = input.lines().map(|line| Reaction::try_from(line, &mut chemicals)).collect::, _>>()?; let fuel = chemicals.iter().position(|chem| chem == &String::from("FUEL")).unwrap(); let ore = chemicals.iter().position(|chem| chem == &String::from("ORE")).unwrap(); // dbg!(&chemicals); let first = break_down(&reactions, fuel, ore, 1); let second = bisection_find(1_000_000_000_000/first, 10_000_000_000_000/first, &reactions, fuel, ore, 1_000_000_000_000); - (first, second) + Ok((first, second)) } fn bisection_find(lower: usize, upper: usize, reactions: &[Reaction], target: usize, raw: usize, stock: usize) -> usize { @@ -109,12 +133,12 @@ mod tests { #[test] fn test_sample() { let sample_input = read_file("tests/sample_input"); - assert_eq!(run(&sample_input), (2210736, 460664)); + assert_eq!(run(&sample_input), Ok((2210736, 460664))); } #[test] fn test_challenge() { let challenge_input = read_file("tests/challenge_input"); - assert_eq!(run(&challenge_input), (1582325, 2267486)); + assert_eq!(run(&challenge_input), Ok((1582325, 2267486))); } }