day-15 done and dusted
This commit is contained in:
9
2023/Cargo.lock
generated
9
2023/Cargo.lock
generated
@@ -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
15
2023/day-15/Cargo.toml
Normal 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
4
2023/day-15/src/lib.rs
Normal 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
12
2023/day-15/src/main.rs
Normal 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
61
2023/day-15/src/part1.rs
Normal 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
98
2023/day-15/src/part2.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user