From 8bd03989a8ec6ca8e50c3e91ac65dd2e5818f3b3 Mon Sep 17 00:00:00 2001 From: Gabe Venberg Date: Tue, 5 Dec 2023 17:51:17 -0600 Subject: [PATCH] day05 part1 --- days/day05/src/main.rs | 2 +- days/day05/src/parse.rs | 83 ++++++++++++++++++++--- days/day05/src/part1.rs | 147 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 219 insertions(+), 13 deletions(-) diff --git a/days/day05/src/main.rs b/days/day05/src/main.rs index e282b24..bafdbd2 100644 --- a/days/day05/src/main.rs +++ b/days/day05/src/main.rs @@ -7,7 +7,7 @@ fn main() { let structured_input = parse::parse(input); println!("Part One"); - println!("Result: {}", part1::part1()); + println!("Result: {}", part1::part1(&structured_input)); println!("Part Two"); println!("Result: {}", part2::part2()); diff --git a/days/day05/src/parse.rs b/days/day05/src/parse.rs index 8f0e878..d479a90 100644 --- a/days/day05/src/parse.rs +++ b/days/day05/src/parse.rs @@ -8,18 +8,25 @@ use nom::{ #[derive(Debug, PartialEq, Eq)] pub struct Map { - from: String, - to: String, - ranges: Vec, + pub from: String, + pub to: String, + pub ranges: Vec, } impl Map { - pub fn parse(input: &str) -> IResult<&str, Self> { + pub fn map(&self, src: u64)->u64 { + for range in &self.ranges { + if range.is_applicable(&src) { + return range.map(src); + } + } + src + } + fn parse(input: &str) -> IResult<&str, Self> { let (input, (from, to)) = delimited(multispace0, Self::parse_to_from, multispace0)(input)?; let (input, ranges) = many1(Range::parse)(input)?; Ok((input, Map { from, to, ranges })) } - fn parse_to_from(input: &str) -> IResult<&str, (String, String)> { let (input, from) = alpha1(input)?; let (input, to) = terminated(preceded(tag("-to-"), alpha1), tag(" map:\n"))(input)?; @@ -29,13 +36,26 @@ impl Map { #[derive(Debug, PartialEq, Eq)] pub struct Range { - dest_start: u64, - src_start: u64, - len: u64, + pub dest_start: u64, + pub src_start: u64, + pub len: u64, } impl Range { - pub fn parse(input: &str) -> IResult<&str, Self> { + fn is_applicable(&self, src: &u64) -> bool { + self.src_start <= *src && *src < (self.src_start + self.len) + } + + fn map(&self, src: u64) -> u64 { + if self.is_applicable(&src) { + let offset = src - self.src_start; + self.dest_start + offset + } else { + src + } + } + + fn parse(input: &str) -> IResult<&str, Self> { let number = delimited(multispace0, nom::character::complete::u64, multispace0); let (input, numbers) = count(number, 3)(input)?; Ok(( @@ -66,6 +86,51 @@ pub fn parse(input: &str) -> (Vec, Vec) { mod tests { use super::*; + #[test] + fn test_map() { + let tested = Map { + from: "seed".to_string(), + to: "soil".to_string(), + ranges: vec![ + Range { + dest_start: 50, + src_start: 98, + len: 2, + }, + Range { + dest_start: 52, + src_start: 50, + len: 48, + }, + ], + }; + let input = [79, 14, 55, 13]; + let output = input.map(|i| tested.map(i)); + assert_eq!(output, [81, 14, 57, 13]) + } + + #[test] + fn test_is_appliccable() { + let tested = Range { + dest_start: 50, + src_start: 98, + len: 2, + }; + assert!(tested.is_applicable(&99)); + assert!(!tested.is_applicable(&100)); + assert!(!tested.is_applicable(&97)); + } + #[test] + fn test_range_map() { + let tested = Range { + dest_start: 52, + src_start: 50, + len: 48, + }; + assert_eq!(tested.map(79), 81); + assert_eq!(tested.map(100), 100); + } + #[test] fn test_map_parse() { let input = concat!("seed-to-soil map:\n", "50 98 2\n", "52 50 48\n", "\n",); diff --git a/days/day05/src/part1.rs b/days/day05/src/part1.rs index 3ab311b..e79db7e 100644 --- a/days/day05/src/part1.rs +++ b/days/day05/src/part1.rs @@ -1,7 +1,11 @@ use crate::parse::*; -pub fn part1() -> usize { - unimplemented!() +pub fn part1(input: &(Vec, Vec)) -> usize { + let mut seeds = input.0.clone(); + for map in &input.1{ + seeds = seeds.into_iter().map(|s| map.map(s)).collect() + } + *seeds.iter().min().unwrap() as usize } #[cfg(test)] @@ -10,6 +14,143 @@ mod tests { #[test] fn test_part1() { - assert_eq!(0, 0); + let input = ( + vec![79, 14, 55, 13], + vec![ + Map { + from: "seed".to_string(), + to: "soil".to_string(), + ranges: vec![ + Range { + dest_start: 50, + src_start: 98, + len: 2 + }, + Range { + dest_start: 52, + src_start: 50, + len: 48 + } + ] + }, + Map { + from: "soil".to_string(), + to: "fertilizer".to_string(), + ranges: vec![ + Range { + dest_start: 0, + src_start: 15, + len: 37 + }, + Range { + dest_start: 37, + src_start: 52, + len: 2 + }, + Range { + dest_start: 39, + src_start: 0, + len: 15 + } + ] + }, + Map { + from: "fertilizer".to_string(), + to: "water".to_string(), + ranges: vec![ + Range { + dest_start: 49, + src_start: 53, + len: 8 + }, + Range { + dest_start: 0, + src_start: 11, + len: 42 + }, + Range { + dest_start: 42, + src_start: 0, + len: 7 + }, + Range { + dest_start: 57, + src_start: 7, + len: 4 + } + ] + }, + Map { + from: "water".to_string(), + to: "light".to_string(), + ranges: vec![ + Range { + dest_start: 88, + src_start: 18, + len: 7 + }, + Range { + dest_start: 18, + src_start: 25, + len: 70 + } + ] + }, + Map { + from: "light".to_string(), + to: "temperature".to_string(), + ranges: vec![ + Range { + dest_start: 45, + src_start: 77, + len: 23 + }, + Range { + dest_start: 81, + src_start: 45, + len: 19 + }, + Range { + dest_start: 68, + src_start: 64, + len: 13 + } + ] + }, + Map { + from: "temperature".to_string(), + to: "humidity".to_string(), + ranges: vec![ + Range { + dest_start: 0, + src_start: 69, + len: 1 + }, + Range { + dest_start: 1, + src_start: 0, + len: 69 + } + ] + }, + Map { + from: "humidity".to_string(), + to: "location".to_string(), + ranges: vec![ + Range { + dest_start: 60, + src_start: 56, + len: 37 + }, + Range { + dest_start: 56, + src_start: 93, + len: 4 + } + ] + } + ] + ); + assert_eq!(part1(&input), 35); } }