diff --git a/2023/day-18/src/part1.rs b/2023/day-18/src/part1.rs index e491d6e..ed05970 100644 --- a/2023/day-18/src/part1.rs +++ b/2023/day-18/src/part1.rs @@ -1,8 +1,7 @@ #![warn(clippy::all, clippy::pedantic)] -use std::collections::HashMap; - -use glam::IVec2; +use glam::I64Vec2; +use itertools::Itertools; use nom::{ branch::alt, bytes::complete::tag, @@ -21,7 +20,7 @@ enum Direction { struct Step { pub direction: Direction, - pub count: i32, + pub count: i64, pub _color: String, } @@ -35,69 +34,38 @@ struct Step { #[must_use] pub fn part1(input: &str) -> String { let (_, steps) = parse_input(input).expect("valid aoc content not found"); - let mut cursor = IVec2::splat(0); - let mut grid = HashMap::from([(IVec2::splat(0), '#')]); - for step in &steps { - match step.direction { - Direction::Up => { - (1..=step.count).for_each(|x| { - let pos = cursor - IVec2::new(0, x); - grid.insert(pos, '#'); - }); - cursor -= IVec2::new(0, step.count); - } - Direction::Down => { - (1..=step.count).for_each(|x| { - let pos = cursor + IVec2::new(0, x); - grid.insert(pos, '#'); - }); - cursor += IVec2::new(0, step.count); - } - Direction::Left => { - (1..=step.count).for_each(|x| { - let pos = cursor - IVec2::new(x, 0); - grid.insert(pos, '#'); - }); - cursor -= IVec2::new(step.count, 0); - } - Direction::Right => { - (1..=step.count).for_each(|x| { - let pos = cursor + IVec2::new(x, 0); - grid.insert(pos, '#'); - }); - cursor += IVec2::new(step.count, 0); - } + let corners = steps + .iter() + .scan(I64Vec2::splat(0), |cursor, next| { + let dir =match next.direction { + Direction::Up => I64Vec2::NEG_Y, + Direction::Down => I64Vec2::Y, + Direction::Left => I64Vec2::NEG_X, + Direction::Right => I64Vec2::X, + }; + *cursor += next.count * dir; + Some( *cursor) + }) + .collect::>(); + let perimeter = corners + .iter() + .tuple_windows() + .map(|(a, b)| { + let dist = (*b - *a).abs(); + dist.x + dist.y + }) + .sum::() + + { + let a = corners.last().unwrap(); + let b = corners.first().unwrap(); + let dist = (*b - *a).abs(); + dist.x + dist.y }; - } - let (min_x, min_y, max_x, max_y) = grid.keys().fold( - (i32::MAX, i32::MAX, i32::MIN, i32::MIN), - |(min_x, min_y, max_x, max_y), pos| { - ( - min_x.min(pos.x), - min_y.min(pos.y), - max_x.max(pos.x), - max_y.max(pos.y), - ) - }, - ); - (min_y..=max_y).for_each(|y| { - let mut inside = false; - (min_x..=max_x).for_each(|x| { - let square = grid.get(&IVec2::new(x, y)); - //print!("{}", square.unwrap_or(&'.')); - //is it in or out ogf the loop - inside = if square.is_some() && grid.get(&IVec2::new(x, y + 1)).is_some() { - !inside - } else { - inside - }; - if square.is_none() && inside { - grid.insert(IVec2::new(x, y), '#'); - } - }); - //print!("\n"); - }); - grid.len().to_string() + let area = (corners.iter().tuple_windows().map(|(a,b)| { + a.x * b.y -a.y *b.x + }).sum::() + perimeter + )/2 +1; + area.to_string() } fn parse_step(input: &str) -> IResult<&str, Step> { @@ -110,7 +78,7 @@ fn parse_step(input: &str) -> IResult<&str, Step> { tag("R").map(|_| Direction::Right), )), complete::space1, - complete::i32, + complete::i64, ), complete::space1, delimited(tag("("), preceded(tag("#"), complete::hex_digit1), tag(")")), diff --git a/2023/day-18/src/part2.rs b/2023/day-18/src/part2.rs index d8e6695..0c6729d 100644 --- a/2023/day-18/src/part2.rs +++ b/2023/day-18/src/part2.rs @@ -1,19 +1,138 @@ #![warn(clippy::all, clippy::pedantic)] +//use std::collections::HashMap; + +use glam::I64Vec2; +use itertools::Itertools; +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete, + multi::separated_list1, + sequence::{delimited, preceded, separated_pair}, + IResult, Parser, +}; + +#[derive(Debug)] +enum Direction { + Up, + Down, + Left, + Right, +} + +#[derive(Debug)] +struct Step { + pub direction: Direction, + pub count: i64, +} + +/// day 18 part 2 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than #[must_use] -pub fn part2(_input: &str) -> String { - "Not Finished".to_string() +pub fn part2(input: &str) -> String { + let (_, steps) = parse_input(input).expect("valid aoc content not found"); + let corners = steps + .iter() + .scan(I64Vec2::splat(0), |cursor, next| { + let dir = match next.direction { + Direction::Up => I64Vec2::NEG_Y, + Direction::Down => I64Vec2::Y, + Direction::Left => I64Vec2::NEG_X, + Direction::Right => I64Vec2::X, + }; + *cursor += next.count * dir; + Some( *cursor) + }) + .collect::>(); + + let perimeter = corners + .iter() + .tuple_windows() + .map(|(a, b)| { + let dist = (*b - *a).abs(); + dist.x + dist.y + }) + .sum::() + + { + let a = corners.last().unwrap(); + let b = corners.first().unwrap(); + let dist = (*b - *a).abs(); + dist.x + dist.y + }; + + let area = ((corners + .iter() + .tuple_windows() + .map(|(a, b)| a.x * b.y - a.y * b.x) + .sum::() + + perimeter) + / 2) + .abs() + + 1; + + (area ).to_string() +} + +fn parse_step(input: &str) -> IResult<&str, Step> { + let (input, (_, color)) = separated_pair( + separated_pair( + alt(( + tag("U").map(|_| Direction::Up), + tag("D").map(|_| Direction::Down), + tag("L").map(|_| Direction::Left), + tag("R").map(|_| Direction::Right), + )), + complete::space1, + complete::i32, + ), + complete::space1, + delimited(tag("("), preceded(tag("#"), complete::hex_digit1), tag(")")), + )(input)?; + let direction = match color.chars().last().unwrap() { + '0' => Direction::Right, + '1' => Direction::Down, + '2' => Direction::Left, + '3' => Direction::Up, + x => unimplemented!("Direction for value {x} not iimplemented"), + }; + let count = i64::from_str_radix(&color.chars().take(5).collect::(), 16) + .expect("this should just you know work"); + + Ok((input, Step { direction, count })) +} + +fn parse_input(input: &str) -> IResult<&str, Vec> { + separated_list1(complete::line_ending, parse_step)(input) } #[cfg(test)] mod test { use super::*; - const INPUT: &str = ""; + const INPUT: &str = "R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3)"; #[test] fn part2_works() { let result = part2(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "952408144115".to_string()); } }