1
0
Fork 0
advent-of-code/2023/src/bin/18.rs

112 lines
2.8 KiB
Rust

advent_of_code::solution!(18);
use aoc_parse::{parser, prelude::*};
use std::ops::{Add, Div};
#[derive(Clone, Copy)]
enum Dir {
N,
E,
S,
W,
}
struct Move {
dir: Dir,
count: usize,
color: String,
}
fn parse(input: &str) -> Vec<Move> {
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::<Vec<_>>()
}
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)
}
pub fn part_one(input: &str) -> Option<isize> {
Some(shoelace(&get_perimeter(&parse(input))))
}
pub fn part_two(input: &str) -> Option<isize> {
let moves = parse(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::<Vec<_>>();
Some(shoelace(&get_perimeter(&moves)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(62));
}
#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(952408144115));
}
}