day-15 done and dusted

This commit is contained in:
Dylan Thies
2023-12-16 10:15:15 -05:00
parent 688e5d00ef
commit 694f6429a5
6 changed files with 199 additions and 0 deletions

9
2023/Cargo.lock generated
View File

@@ -143,6 +143,15 @@ dependencies = [
"rstest",
]
[[package]]
name = "day-15"
version = "2023.0.0"
dependencies = [
"itertools",
"nom",
"rstest",
]
[[package]]
name = "day-2"
version = "2023.0.0"

15
2023/day-15/Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "day-15"
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 }
[dev-dependencies]
rstest.workspace = true

4
2023/day-15/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod part1;
pub use crate::part1::*;
pub mod part2;
pub use crate::part2::*;

12
2023/day-15/src/main.rs Normal file
View File

@@ -0,0 +1,12 @@
#![warn(clippy::all, clippy::pedantic)]
use day_15::part1;
use day_15::part2;
fn main() {
let input = include_str!("./input.txt");
let part1_result = part1(input);
println!("part 1: {part1_result}");
let part2_result = part2(input);
println!("part 2: {part2_result}");
}

61
2023/day-15/src/part1.rs Normal file
View File

@@ -0,0 +1,61 @@
#![warn(clippy::all, clippy::pedantic)]
/// day 15 part 1 of aoc 2023
///
/// # Arguments
/// - input the input for today's puzzle
///
/// # Panics
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
#[must_use]
pub fn part1(input: &str) -> String {
input
.lines()
.map(|x| {
x.split(',')
.map(unhash)
.sum::<usize>()
.to_string()
})
.next()
.unwrap()
}
fn unhash(hash: &str) -> usize {
hash.chars()
.fold(0, |acc, x| (acc + (x as usize)) * 17 % 256)
}
#[cfg(test)]
mod test {
use rstest::rstest;
use super::*;
#[rstest]
#[case("HASH", 52)]
#[case("rn=1", 30)]
#[case("cm-", 253)]
#[case("qp=3", 97)]
#[case("cm=2", 47)]
#[case("qp-", 14)]
#[case("pc=4", 180)]
#[case("ot=9", 9)]
#[case("ab=5", 197)]
#[case("pc-", 48)]
#[case("pc=6", 214)]
#[case("ot=7", 231)]
#[case("kqzb-\n", 127)]
#[case("dx-", 153)]
fn hash_test(#[case] input: &str, #[case] expected: usize) {
assert_eq!(unhash(input), expected);
}
const INPUT: &str = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7\n";
#[test]
fn part1_works() {
let result = part1(INPUT);
assert_eq!(result, "1320".to_string());
}
}

98
2023/day-15/src/part2.rs Normal file
View File

@@ -0,0 +1,98 @@
#![warn(clippy::all, clippy::pedantic)]
use std::iter::repeat_with;
use nom::{
branch::alt,
bytes::complete::tag,
character::complete,
multi::separated_list1,
sequence::{pair, preceded},
IResult, Parser,
};
enum Op {
Set(u8),
Remove,
}
struct Lens {
pub label: String,
pub power: u8,
}
fn unhash(hash: &str) -> usize {
hash.chars()
.fold(0, |acc, x| (acc + (x as usize)) * 17 % 256)
}
/// day 15 part 2 of aoc 2023
///
/// # Arguments
/// - input the input for today's puzzle
///
/// # Panics
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
#[must_use]
pub fn part2(input: &str) -> String {
let (_, steps) = parse_input(input).expect("aoc always good");
let mut boxes = repeat_with(Vec::<Lens>::new).take(256).collect::<Vec<_>>();
for (label, op) in steps {
let box_index = unhash(label);
let lenses = boxes.get_mut(box_index).unwrap(); //u8 should always be there
if let Some(lens_index) = lenses.iter().position(|lens| lens.label == label) {
match op {
Op::Set(power) => lenses.get_mut(lens_index).unwrap().power = power,
Op::Remove => {
lenses.remove(lens_index);
}
}
} else {
match op {
Op::Set(power) => lenses.push(Lens {
label: label.to_string(),
power,
}),
Op::Remove => (),
}
}
}
boxes
.iter()
.enumerate()
.map(|(box_num, lenses)| {
lenses
.iter()
.enumerate()
.map(|(lens_index, lens)| (box_num + 1) * (lens_index + 1) * (lens.power as usize))
.sum::<usize>()
})
.sum::<usize>()
.to_string()
}
fn parse_input(input: &str) -> IResult<&str, Vec<(&str, Op)>> {
separated_list1(
tag(","),
pair(
complete::alpha1,
alt((
tag("-").map(|_| Op::Remove),
preceded(tag("="), complete::u8).map(Op::Set),
)),
),
)(input)
}
#[cfg(test)]
mod test {
use super::*;
const INPUT: &str = "rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7\n";
#[test]
fn part2_works() {
let result = part2(INPUT);
assert_eq!(result, "145".to_string());
}
}