2024 day-2 done
This commit is contained in:
24
2024/day-2/Cargo.toml
Normal file
24
2024/day-2/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "day-2"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nom.workspace = true
|
||||||
|
itertools.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
error-stack.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
dhat.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
test-log.workspace = true
|
||||||
|
rstest.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dhat-heap = []
|
||||||
4
2024/day-2/src/lib.rs
Normal file
4
2024/day-2/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod part1;
|
||||||
|
pub use crate::part1::*;
|
||||||
|
pub mod part2;
|
||||||
|
pub use crate::part2::*;
|
||||||
31
2024/day-2/src/main.rs
Normal file
31
2024/day-2/src/main.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use day_2::part1;
|
||||||
|
use day_2::part2;
|
||||||
|
|
||||||
|
use error_stack::{Result, ResultExt};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum Day2Error {
|
||||||
|
#[error("Part 1 failed")]
|
||||||
|
Part1Error,
|
||||||
|
#[error("Part 2 failed")]
|
||||||
|
Part2Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Day2Error> {
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
|
let input = include_str!("./input.txt");
|
||||||
|
let part1_result = part1(input).change_context(Day2Error::Part1Error)?;
|
||||||
|
println!("part 1: {part1_result}");
|
||||||
|
let part2_result = part2(input).change_context(Day2Error::Part2Error)?;
|
||||||
|
println!("part 2: {part2_result}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
99
2024/day-2/src/part1.rs
Normal file
99
2024/day-2/src/part1.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use error_stack::{Report, Result, ResultExt};
|
||||||
|
use nom::{character::complete, multi::separated_list1, IResult};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum Safety {
|
||||||
|
Safe,
|
||||||
|
UnSafe,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct XmasReport {
|
||||||
|
levels: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XmasReport {
|
||||||
|
pub fn is_safe(&self) -> Safety {
|
||||||
|
let mut dir = Ordering::Equal;
|
||||||
|
for i in 1..self.levels.len() {
|
||||||
|
if !(1_u32..=3).contains(&(self.levels[i - 1].abs_diff(self.levels[i]))) {
|
||||||
|
return Safety::UnSafe;
|
||||||
|
}
|
||||||
|
let new_dir = self.levels[i - 1].cmp(&self.levels[i]);
|
||||||
|
if dir != Ordering::Equal && dir != new_dir {
|
||||||
|
return Safety::UnSafe;
|
||||||
|
}
|
||||||
|
dir = new_dir;
|
||||||
|
}
|
||||||
|
Safety::Safe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// day-2
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Day2Part1Error {
|
||||||
|
#[error("Problem parsing Day 2")]
|
||||||
|
ParseError,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Day-2 Part 1 for 2024 advent of code
|
||||||
|
/// Problem can be found here: <https://adventofcode.com/2024/day/2>
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - `ParseError` there was an issue with the parser
|
||||||
|
pub fn part1(input: &str) -> Result<String, Day2Part1Error> {
|
||||||
|
let (_, reports) = parse_input(input)
|
||||||
|
.map_err(|x| Report::from(x.to_owned()))
|
||||||
|
.change_context(Day2Part1Error::ParseError)?;
|
||||||
|
Ok(reports
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.is_safe() == Safety::Safe)
|
||||||
|
.count()
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_level(input: &str) -> IResult<&str, XmasReport> {
|
||||||
|
let (input, v) = separated_list1(complete::space1, complete::u32)(input)?;
|
||||||
|
Ok((input, XmasReport { levels: v }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, Vec<XmasReport>> {
|
||||||
|
separated_list1(complete::line_ending, parse_level)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("7 6 4 2 1", Safety::Safe)]
|
||||||
|
#[case("1 2 7 8 9", Safety::UnSafe)]
|
||||||
|
#[case("9 7 6 2 1", Safety::UnSafe)]
|
||||||
|
#[case("1 3 2 4 5", Safety::UnSafe)]
|
||||||
|
#[case("8 6 4 4 1", Safety::UnSafe)]
|
||||||
|
#[case("1 3 6 7 9", Safety::Safe)]
|
||||||
|
fn part1_report_safety(#[case] input: &str, #[case] expected: Safety) {
|
||||||
|
let (_, tester) = parse_level(input).expect("should be valid input");
|
||||||
|
assert_eq!(tester.is_safe(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
const INPUT: &str = "7 6 4 2 1
|
||||||
|
1 2 7 8 9
|
||||||
|
9 7 6 2 1
|
||||||
|
1 3 2 4 5
|
||||||
|
8 6 4 4 1
|
||||||
|
1 3 6 7 9";
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
#[test_log(default_log_filter = "trace")]
|
||||||
|
fn part1_works() {
|
||||||
|
let result = part1(INPUT).unwrap();
|
||||||
|
assert_eq!(result, "2".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
117
2024/day-2/src/part2.rs
Normal file
117
2024/day-2/src/part2.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use error_stack::{Report, Result, ResultExt};
|
||||||
|
use nom::{character::complete, multi::separated_list1, IResult};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
enum Safety {
|
||||||
|
Safe,
|
||||||
|
UnSafe,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct XmasReport {
|
||||||
|
levels: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XmasReport {
|
||||||
|
pub fn is_safe(&self) -> Safety {
|
||||||
|
fn internal_check(levels: &[u32]) -> Safety {
|
||||||
|
let mut dir = Ordering::Equal;
|
||||||
|
for i in 1..levels.len() {
|
||||||
|
if !(1_u32..=3).contains(&(levels[i - 1].abs_diff(levels[i]))) {
|
||||||
|
return Safety::UnSafe;
|
||||||
|
}
|
||||||
|
let new_dir = levels[i - 1].cmp(&levels[i]);
|
||||||
|
if dir != Ordering::Equal && dir != new_dir {
|
||||||
|
return Safety::UnSafe;
|
||||||
|
}
|
||||||
|
dir = new_dir;
|
||||||
|
}
|
||||||
|
Safety::Safe
|
||||||
|
}
|
||||||
|
let ret = internal_check(&self.levels);
|
||||||
|
if ret == Safety::Safe {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
for i in 0..self.levels.len() {
|
||||||
|
let mut attempt = self.levels.clone();
|
||||||
|
let _ = attempt.remove(i);
|
||||||
|
let ret = internal_check(&attempt);
|
||||||
|
if ret == Safety::Safe {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Safety::UnSafe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// day-2
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum Day2Part2Error {
|
||||||
|
#[error("Problem parsing Day 2")]
|
||||||
|
ParseError,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Day-2 Part 2 for 2024 advent of code
|
||||||
|
/// Problem can be found here: <https://adventofcode.com/2024/day/2#part2>
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - `ParseError` there was an issue with the parser
|
||||||
|
pub fn part2(input: &str) -> Result<String, Day2Part2Error> {
|
||||||
|
let (_, reports) = parse_input(input)
|
||||||
|
.map_err(|x| Report::from(x.to_owned()))
|
||||||
|
.change_context(Day2Part2Error::ParseError)?;
|
||||||
|
Ok(reports
|
||||||
|
.iter()
|
||||||
|
.filter(|x| x.is_safe() == Safety::Safe)
|
||||||
|
.count()
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_level(input: &str) -> IResult<&str, XmasReport> {
|
||||||
|
let (input, v) = separated_list1(complete::space1, complete::u32)(input)?;
|
||||||
|
Ok((input, XmasReport { levels: v }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, Vec<XmasReport>> {
|
||||||
|
separated_list1(complete::line_ending, parse_level)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("7 6 4 2 1", Safety::Safe)]
|
||||||
|
#[case("1 2 7 8 9", Safety::UnSafe)]
|
||||||
|
#[case("9 7 6 2 1", Safety::UnSafe)]
|
||||||
|
#[case("1 3 2 4 5", Safety::Safe)]
|
||||||
|
#[case("8 6 4 4 1", Safety::Safe)]
|
||||||
|
#[case("1 3 6 7 9", Safety::Safe)]
|
||||||
|
#[case("5 3 6 7 9", Safety::Safe)]
|
||||||
|
#[case("5 3 6 7 6", Safety::UnSafe)]
|
||||||
|
#[case("8 6 4 6 4", Safety::UnSafe)]
|
||||||
|
fn part1_report_safety(#[case] input: &str, #[case] expected: Safety) {
|
||||||
|
let (_, tester) = parse_level(input).expect("should be valid input");
|
||||||
|
assert_eq!(tester.is_safe(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
const INPUT: &str = "7 6 4 2 1
|
||||||
|
1 2 7 8 9
|
||||||
|
9 7 6 2 1
|
||||||
|
1 3 2 4 5
|
||||||
|
8 6 4 4 1
|
||||||
|
1 3 6 7 9";
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
#[test_log(default_log_filter = "trace")]
|
||||||
|
fn part2_works() {
|
||||||
|
let result = part2(INPUT).unwrap();
|
||||||
|
assert_eq!(result, "4".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user