aoc/2023/src/bin/day18.rs
2023-12-18 13:29:58 +01:00

153 lines
4.1 KiB
Rust

#![feature(test)]
use std::{
collections::{HashSet, VecDeque},
fmt::Pointer,
};
use anyhow::Result;
use aoc::Solver;
// -- Runners --
fn main() -> Result<()> {
Day::solve()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_test1() -> Result<()> {
Day::test(Day::part1, "test-1", 62)
}
#[test]
fn part1_solution() -> Result<()> {
Day::test(Day::part1, "input", 95356)
}
#[test]
fn part2_test1() -> Result<()> {
Day::test(Day::part2, "test-1", 952408144115)
}
#[test]
fn part2_solution() -> Result<()> {
Day::test(Day::part2, "input", 92291468914147)
}
// Benchmarks
extern crate test;
#[bench]
#[ignore]
fn part1_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part1, b)
}
#[bench]
#[ignore]
fn part2_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part2, b)
}
}
// -- Solution --
pub struct Day;
impl aoc::Solver for Day {
type Output1 = usize;
type Output2 = usize;
fn day() -> u8 {
18
}
fn part1(input: &str) -> Self::Output1 {
let instructions: Vec<_> = input
.lines()
.map(|line| {
let (direction, rest) = line.split_once(' ').unwrap();
let direction = direction.chars().next().unwrap();
let (distance, _) = rest.split_once(" (#").unwrap();
let distance: usize = distance.parse().unwrap();
(direction, distance)
})
.collect();
let mut location = (0_isize, 0_isize);
let mut points = Vec::new();
let mut boundary = 0;
for (direction, distance) in &instructions {
for _ in 0..*distance {
match direction {
'U' => location.1 -= 1,
'D' => location.1 += 1,
'L' => location.0 -= 1,
'R' => location.0 += 1,
_ => unreachable!("Invalid input"),
}
}
boundary += distance;
points.push(location);
}
let mut area = 0;
for i in 0..points.len() as isize {
let n_min = (i - 1).rem_euclid(points.len() as isize);
let n_plus = (i + 1).rem_euclid(points.len() as isize);
area += points[i as usize].1 * (points[n_min as usize].0 - points[n_plus as usize].0);
}
let area = area as usize / 2;
let interior = area - boundary / 2 + 1;
interior + boundary
}
fn part2(input: &str) -> Self::Output2 {
let instructions: Vec<_> = input
.lines()
.map(|line| {
let (_, rest) = line.split_once(' ').unwrap();
let (_, rest) = rest.split_once(" (#").unwrap();
let (distance, direction) = rest.split_at(5);
let distance = usize::from_str_radix(distance, 16).unwrap();
let direction = direction.chars().next().unwrap();
(direction, distance)
})
.collect();
let mut location = (0_isize, 0_isize);
let mut points = Vec::new();
let mut boundary = 0;
for (direction, distance) in &instructions {
for _ in 0..*distance {
match direction {
'3' => location.1 -= 1,
'1' => location.1 += 1,
'2' => location.0 -= 1,
'0' => location.0 += 1,
_ => unreachable!("Invalid input"),
}
}
boundary += distance;
points.push(location);
}
let mut area = 0;
for i in 0..points.len() as isize {
let n_min = (i - 1).rem_euclid(points.len() as isize);
let n_plus = (i + 1).rem_euclid(points.len() as isize);
area += points[i as usize].1 * (points[n_min as usize].0 - points[n_plus as usize].0);
}
let area = area as usize / 2;
let interior = area - boundary / 2 + 1;
interior + boundary
}
}