2019 cleanup: Introduced proper Error types for most days up until day 14

This commit is contained in:
Chris Alge 2023-03-13 21:47:29 +01:00
parent 9c8f23161a
commit 2851f7c80d
11 changed files with 150 additions and 85 deletions

View file

@ -1,16 +1,22 @@
pub fn run(input: &str) -> (usize, usize) {
let first = input.lines().map(|line| line.parse::<usize>().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::<usize>().map(|i| i/3-2)).sum::<Result<usize, _>>()?;
let second = input.lines().map(|line| {
let weight = line.parse::<usize>().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::<usize>();
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::<usize>();
(first, second)
}).sum::<Result<usize, _>>()?;
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)));
}
}

View file

@ -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::<isize>().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)));
}
}

View file

@ -1,9 +1,11 @@
pub fn run(input: &str) -> (usize, usize) {
let range: Vec<_> = input.split('-').map(|n| n.parse::<usize>().unwrap()).collect();
use std::num::ParseIntError;
pub fn run(input: &str) -> Result<(usize, usize), ParseIntError> {
let range: Vec<_> = input.split('-').map(|n| n.parse::<usize>()).collect::<Result<Vec<_>, _>>()?;
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)));
}
}

View file

@ -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::<isize>().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)));
}
}

View file

@ -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<Orbit> {
fn graph_from(map: &str) -> Result<Vec<Orbit>, String> {
let mut bodies = HashMap::from([("COM", 0), ("YOU", 1), ("SAN", 2)]);
let mut graph: Vec<Orbit> = 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)));
}
}

View file

@ -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<isize>) -> Vec<Vec<isize>> {
@ -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)));
}
}

View file

@ -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)));
}
}

View file

@ -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())));
}
}

View file

@ -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<ParseIntError> 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<Self, Self::Error> {
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<Moon> = input.lines().map(Moon::try_from).collect::<Result<Vec<Moon>, _>>().unwrap();
pub fn run(input: &str) -> Result<(usize, usize), ParseMoonError> {
let moons: Vec<Moon> = input.lines().map(Moon::try_from).collect::<Result<Vec<Moon>, _>>()?;
//.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)));
}
}

View file

@ -1,4 +1,4 @@
<x=-8, y=-10, z=0>
<x=-8, y=-10, z=O>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>

View file

@ -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<ParseIntError> 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<String>) -> Self {
fn try_from(line: &str, chemicals: &mut Vec<String>) -> Result<Self, ParseError> {
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::<usize>().unwrap(), }).collect();
let input = in_components.chunks(3).map(|c| Reagent { id: get_chemical(c[1]), amount: c[0].parse::<usize>().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::<Result<Vec<_>, _>>()?;
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)));
}
}