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",
|
"rstest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day-15"
|
||||||
|
version = "2023.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"nom",
|
||||||
|
"rstest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day-2"
|
name = "day-2"
|
||||||
version = "2023.0.0"
|
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