mirror of
https://github.com/kristoferssolo/Advent-of-Code.git
synced 2025-10-21 18:00:35 +00:00
day-05 part-1
This commit is contained in:
parent
c28bd8a08a
commit
355a685e42
15
2024/Cargo.lock
generated
15
2024/Cargo.lock
generated
@ -174,6 +174,21 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day-05"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"divan",
|
||||
"itertools",
|
||||
"miette",
|
||||
"nom",
|
||||
"rstest",
|
||||
"test-log",
|
||||
"thiserror 2.0.3",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "divan"
|
||||
version = "0.1.16"
|
||||
|
||||
24
2024/day-05/Cargo.toml
Normal file
24
2024/day-05/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "day-05"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
itertools.workspace = true
|
||||
nom.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
miette.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
divan.workspace = true
|
||||
rstest.workspace = true
|
||||
test-log.workspace = true
|
||||
|
||||
[[bench]]
|
||||
name = "day-05-bench"
|
||||
path = "benches/benchmarks.rs"
|
||||
harness = false
|
||||
21
2024/day-05/benches/benchmarks.rs
Normal file
21
2024/day-05/benches/benchmarks.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use day_05::*;
|
||||
|
||||
fn main() {
|
||||
divan::main();
|
||||
}
|
||||
|
||||
#[divan::bench]
|
||||
fn part1() {
|
||||
part1::process(divan::black_box(include_str!(
|
||||
"../input1.txt",
|
||||
)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[divan::bench]
|
||||
fn part2() {
|
||||
part2::process(divan::black_box(include_str!(
|
||||
"../input2.txt",
|
||||
)))
|
||||
.unwrap();
|
||||
}
|
||||
12
2024/day-05/src/bin/part1.rs
Normal file
12
2024/day-05/src/bin/part1.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use day_05::part1::process;
|
||||
use miette::{Context, Result};
|
||||
|
||||
#[tracing::instrument]
|
||||
fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let file = include_str!("../../input1.txt");
|
||||
let result = process(file).context("process part 1")?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
12
2024/day-05/src/bin/part2.rs
Normal file
12
2024/day-05/src/bin/part2.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use day_05::part2::process;
|
||||
use miette::{Context, Result};
|
||||
|
||||
#[tracing::instrument]
|
||||
fn main() -> Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let file = include_str!("../../input2.txt");
|
||||
let result = process(file).context("process part 2")?;
|
||||
println!("{}", result);
|
||||
Ok(())
|
||||
}
|
||||
2
2024/day-05/src/lib.rs
Normal file
2
2024/day-05/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod part1;
|
||||
pub mod part2;
|
||||
228
2024/day-05/src/part1.rs
Normal file
228
2024/day-05/src/part1.rs
Normal file
@ -0,0 +1,228 @@
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use miette::{Diagnostic, Result};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
enum OrderingError {
|
||||
#[error("No separator '{0}' was found")]
|
||||
NoSeparator(char),
|
||||
#[error("Too many values found ({0}) expected 2")]
|
||||
TooManyValues(usize),
|
||||
#[error("Not enough values found ({0}) expected 2")]
|
||||
NotEnoughtValues(usize),
|
||||
#[error("Failed to parse number")]
|
||||
ParseError,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Ordering(usize, usize);
|
||||
|
||||
impl FromStr for Ordering {
|
||||
type Err = OrderingError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let splitter = '|';
|
||||
if !s.contains(splitter) {
|
||||
return Err(OrderingError::NoSeparator(splitter));
|
||||
}
|
||||
|
||||
let pages = s.split(splitter).collect::<Vec<_>>();
|
||||
|
||||
if pages.len() > 2 {
|
||||
return Err(OrderingError::TooManyValues(pages.len()));
|
||||
}
|
||||
if pages.len() < 2 {
|
||||
return Err(OrderingError::NotEnoughtValues(pages.len()));
|
||||
}
|
||||
|
||||
let x = pages[0]
|
||||
.trim()
|
||||
.parse::<usize>()
|
||||
.map_err(|_| OrderingError::ParseError)?;
|
||||
let y = pages[1]
|
||||
.trim()
|
||||
.parse::<usize>()
|
||||
.map_err(|_| OrderingError::ParseError)?;
|
||||
|
||||
Ok(Ordering(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ordering {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}|{}", self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Rules(Vec<Ordering>);
|
||||
|
||||
impl Rules {
|
||||
fn check(&self, pages: &Pages) -> Option<Page> {
|
||||
let len = pages.0.len();
|
||||
if len < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
pages
|
||||
.0
|
||||
.windows(2)
|
||||
.all(|window| self.is_pair(&window[0], &window[1]))
|
||||
.then(|| pages.0[len / 2])
|
||||
}
|
||||
|
||||
fn is_pair(&self, a: &Page, b: &Page) -> bool {
|
||||
self.0.iter().any(|Ordering(x, y)| *x == a.0 && *y == b.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Rules {
|
||||
type Err = OrderingError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let rules = s
|
||||
.lines()
|
||||
.map(Ordering::from_str)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(Rules(rules))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
enum PageError {
|
||||
#[error("Failed to parse number")]
|
||||
ParseError,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Page(usize);
|
||||
|
||||
impl FromStr for Page {
|
||||
type Err = PageError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let page = s.parse().map_err(|_| PageError::ParseError);
|
||||
Ok(Page(page?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Pages(Vec<Page>);
|
||||
|
||||
impl FromStr for Pages {
|
||||
type Err = PageError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
let pages = s
|
||||
.split(',')
|
||||
.map(Page::from_str)
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
Ok(Pages(pages?))
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn process(input: &str) -> Result<usize> {
|
||||
let (rules, pages) = parse(input)?;
|
||||
let sum = pages
|
||||
.iter()
|
||||
.filter_map(|page| rules.check(page))
|
||||
.map(|page| page.0)
|
||||
.sum();
|
||||
Ok(sum)
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> Result<(Rules, Vec<Pages>)> {
|
||||
let sections = input.split("\n\n").collect::<Vec<_>>();
|
||||
|
||||
let rules = Rules::from_str(sections.first().unwrap())?;
|
||||
let pages = get_pages(sections.last().unwrap())?;
|
||||
|
||||
Ok((rules, pages))
|
||||
}
|
||||
|
||||
fn get_pages(section: &str) -> Result<Vec<Pages>> {
|
||||
Ok(section
|
||||
.lines()
|
||||
.map(Pages::from_str)
|
||||
.collect::<Result<Vec<_>, _>>()?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
const TEST_RULES: &str = "47|53
|
||||
97|13
|
||||
97|61
|
||||
97|47
|
||||
75|29
|
||||
61|13
|
||||
75|53
|
||||
29|13
|
||||
97|29
|
||||
53|29
|
||||
61|53
|
||||
97|53
|
||||
61|29
|
||||
47|13
|
||||
75|47
|
||||
97|75
|
||||
47|61
|
||||
75|61
|
||||
47|29
|
||||
75|13
|
||||
53|13";
|
||||
|
||||
#[rstest]
|
||||
#[case("75,47,61,53,29", Some(Page(61)))]
|
||||
#[case("97,61,53,29,13", Some(Page(53)))]
|
||||
#[case("75,29,13", Some(Page(29)))]
|
||||
#[case("75,97,47,61,53", None)]
|
||||
#[case("61,13,29", None)]
|
||||
#[case("97,13,75,29,47", None)]
|
||||
fn test_pages(#[case] input: &str, #[case] expected: Option<Page>) -> Result<()> {
|
||||
let rules = Rules::from_str(TEST_RULES)?;
|
||||
let pages = Pages::from_str(input)?;
|
||||
|
||||
assert_eq!(rules.check(&pages), expected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process() -> Result<()> {
|
||||
let input = "47|53
|
||||
97|13
|
||||
97|61
|
||||
97|47
|
||||
75|29
|
||||
61|13
|
||||
75|53
|
||||
29|13
|
||||
97|29
|
||||
53|29
|
||||
61|53
|
||||
97|53
|
||||
61|29
|
||||
47|13
|
||||
75|47
|
||||
97|75
|
||||
47|61
|
||||
75|61
|
||||
47|29
|
||||
75|13
|
||||
53|13
|
||||
|
||||
75,47,61,53,29
|
||||
97,61,53,29,13
|
||||
75,29,13
|
||||
75,97,47,61,53
|
||||
61,13,29
|
||||
97,13,75,29,47";
|
||||
let result = 143;
|
||||
assert_eq!(process(input)?, result);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
21
2024/day-05/src/part2.rs
Normal file
21
2024/day-05/src/part2.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use miette::Result;
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn process(input: &str) -> Result<usize> {
|
||||
todo!("day xx - part 2");
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_process() -> Result<()> {
|
||||
let input = "";
|
||||
todo!("haven't built test yet");
|
||||
let result = 0;
|
||||
assert_eq!(process(input)?, result);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user