diff --git a/2023/src/day18.rs b/2023/src/day18.rs new file mode 100644 index 0000000..0ff6898 --- /dev/null +++ b/2023/src/day18.rs @@ -0,0 +1,123 @@ +use aoc_parse::{parser, prelude::*}; +use aoc_runner_derive::{aoc, aoc_generator}; +use std::ops::{Add, Div}; + +#[derive(Clone, Copy)] +enum Dir { + N, + E, + S, + W, +} + +struct Move { + dir: Dir, + count: usize, + color: String, +} + +#[aoc_generator(day18)] +fn parse(input: &str) -> Vec { + let p = parser!(lines({ "U" => Dir::N, "R" => Dir::E, "D" => Dir::S, "L" => Dir::W } " " usize " (#" string(digit_hex+) ")")); + p.parse(input) + .unwrap() + .iter() + .map(|e| Move { + dir: e.0, + count: e.1, + color: e.2.clone(), + }) + .collect::>() +} + +fn get_perimeter(moves: &[Move]) -> Vec<(isize, isize)> { + let mut c: (isize, isize) = (0, 0); + let mut r: Vec<(isize, isize)> = vec![]; + r.push(c); + for m in moves.iter() { + match m.dir { + Dir::N => c.0 -= m.count as isize, + Dir::E => c.1 += m.count as isize, + Dir::S => c.0 += m.count as isize, + Dir::W => c.1 -= m.count as isize, + }; + r.push(c); + } + r +} + +fn distance(s: &(isize, isize), d: &(isize, isize)) -> usize { + s.0.abs_diff(d.0) + s.1.abs_diff(d.1) +} + +fn shoelace(coords: &[(isize, isize)]) -> isize { + let len = coords.len(); + let (area, perimeter) = + coords + .iter() + .enumerate() + .fold((0isize, 0isize), |(sum, perimeter), (index, c)| { + let c2 = coords[(index + 1) % len]; + let next_perimeter = perimeter + distance(c, &c2) as isize; + let next_area = sum + (c.1 * c2.0) - (c.0 * c2.1); + (next_area, next_perimeter) + }); + + area.abs().add(perimeter).div(2).add(1) +} + +#[aoc(day18, part1)] +fn part1(input: &[Move]) -> isize { + shoelace(&get_perimeter(input)) +} + +#[aoc(day18, part2)] +fn part2(input: &[Move]) -> isize { + let moves = input + .iter() + .map(|m| { + let mut s = m.color.clone(); + let dir = match s.remove(s.len() - 1) { + '0' => Dir::E, + '1' => Dir::S, + '2' => Dir::W, + '3' => Dir::N, + n @ _ => panic!("parsed wrong number {}", n), + }; + let count = usize::from_str_radix(&s, 16).unwrap(); + Move { + dir, + count, + color: s, + } + }) + .collect::>(); + shoelace(&get_perimeter(&moves)) +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE_DATA: &'static str = r#"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 sample_data() { + let input = parse(&SAMPLE_DATA); + assert_eq!(part1(&input), 62); + assert_eq!(part2(&input), 952408144115); + } +} diff --git a/2023/src/lib.rs b/2023/src/lib.rs index 1fdf8f6..dc153f3 100644 --- a/2023/src/lib.rs +++ b/2023/src/lib.rs @@ -17,5 +17,6 @@ mod day14; mod day15; mod day16; mod day17; +mod day18; aoc_lib! { year = 2023 }