mirror of
https://github.com/anthonyoteri/advent-of-code-2023.git
synced 2026-06-05 20:46:52 -04:00
@@ -21,6 +21,7 @@ thiserror = "1.0.50"
|
|||||||
dhat = "0.3.2"
|
dhat = "0.3.2"
|
||||||
env_logger = "0.10.1"
|
env_logger = "0.10.1"
|
||||||
test-log = "0.2.13"
|
test-log = "0.2.13"
|
||||||
|
regex = "1.10.2"
|
||||||
|
|
||||||
|
|
||||||
[profile.flamegraph]
|
[profile.flamegraph]
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "day-12"
|
||||||
|
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 }
|
||||||
|
dhat = { workspace = true }
|
||||||
|
regex = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
divan = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
test-log = { workspace = true }
|
||||||
|
rstest = { workspace = true }
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "day-00"
|
||||||
|
path = "benches/benchmark.rs"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
dhat-heap = []
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
use day_12::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
divan::main();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[divan::bench]
|
||||||
|
fn part1() {
|
||||||
|
part1::process(divan::black_box(include_str!("../test-input.txt"))).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[divan::bench]
|
||||||
|
fn part2() {
|
||||||
|
part2::process(divan::black_box(include_str!("../test-input.txt"))).unwrap();
|
||||||
|
}
|
||||||
+1000
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
use day_12::part1::process;
|
||||||
|
use miette::Context;
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
fn main() -> miette::Result<()> {
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "dhat-heap"))]
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
let file = include_str!("../../input.txt");
|
||||||
|
let result = process(file).context("process part 1")?;
|
||||||
|
|
||||||
|
println!("{}", result);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
use day_12::part2::process;
|
||||||
|
use miette::Context;
|
||||||
|
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
fn main() -> miette::Result<()> {
|
||||||
|
#[cfg(feature = "dhat-heap")]
|
||||||
|
let _profiler = dhat::Profiler::new_heap();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "dhat-heap"))]
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
let file = include_str!("../../input.txt");
|
||||||
|
let result = process(file).context("process part 1")?;
|
||||||
|
|
||||||
|
println!("{}", result);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use miette::Diagnostic;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Diagnostic, Debug)]
|
||||||
|
pub enum AocError {
|
||||||
|
#[error(transparent)]
|
||||||
|
#[diagnostic(code(aoc::io_error))]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod error;
|
||||||
|
|
||||||
|
pub mod part1;
|
||||||
|
pub mod part2;
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::{tag, take_until},
|
||||||
|
character::complete::{self, line_ending, space1},
|
||||||
|
combinator::eof,
|
||||||
|
multi::{many1, separated_list1},
|
||||||
|
sequence::{self, terminated},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::error::AocError;
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, &str> {
|
||||||
|
terminated(take_until(" "), space1)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_limits(input: &str) -> IResult<&str, Vec<usize>> {
|
||||||
|
let (input, limits) = separated_list1(tag(","), complete::u32)(input)?;
|
||||||
|
Ok((input, limits.into_iter().map(|n| n as usize).collect()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parser(input: &str) -> IResult<&str, Vec<(&str, Vec<usize>)>> {
|
||||||
|
many1(terminated(
|
||||||
|
sequence::tuple((parse_input, parse_limits)),
|
||||||
|
alt((line_ending, eof)),
|
||||||
|
))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_permutations(s: &mut String, index: usize) -> Vec<String> {
|
||||||
|
if index == s.len() {
|
||||||
|
return vec![s.clone()];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut permutations = Vec::new();
|
||||||
|
|
||||||
|
if s.chars().nth(index) == Some('?') {
|
||||||
|
s.replace_range(index..=index, ".");
|
||||||
|
permutations.extend(gen_permutations(s, index + 1));
|
||||||
|
|
||||||
|
s.replace_range(index..=index, "#");
|
||||||
|
permutations.extend(gen_permutations(s, index + 1));
|
||||||
|
|
||||||
|
s.replace_range(index..=index, "?");
|
||||||
|
} else {
|
||||||
|
permutations.extend(gen_permutations(s, index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
permutations
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_regex(limits: Vec<usize>) -> String {
|
||||||
|
let mut r = String::from(r"^\.*");
|
||||||
|
|
||||||
|
let middle: String = limits
|
||||||
|
.iter()
|
||||||
|
.map(|n| {
|
||||||
|
let mut s = String::from(r"#{");
|
||||||
|
s.push_str(&format!("{}", n));
|
||||||
|
s.push('}');
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(r"\.+");
|
||||||
|
|
||||||
|
r.push_str(&middle);
|
||||||
|
r.push_str(r"\.*$");
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn process(input: &str) -> miette::Result<usize, AocError> {
|
||||||
|
let (_, parsed) = parser(input).unwrap();
|
||||||
|
|
||||||
|
Ok(parsed
|
||||||
|
.into_iter()
|
||||||
|
.map(|(s, limits)| {
|
||||||
|
let mut s = String::from(s);
|
||||||
|
let re = regex::Regex::new(&make_regex(limits)).unwrap();
|
||||||
|
|
||||||
|
let length = gen_permutations(&mut s, 0)
|
||||||
|
.iter()
|
||||||
|
.filter(|s| re.is_match(s))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
Ok(length)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<usize>, AocError>>()?
|
||||||
|
.iter()
|
||||||
|
.sum::<usize>())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn test_parser() {
|
||||||
|
let input = "???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3";
|
||||||
|
let result = parser(input);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
|
||||||
|
let (rest, result) = result.unwrap();
|
||||||
|
assert_eq!(rest, "");
|
||||||
|
assert_eq!(result.len(), 2);
|
||||||
|
assert_eq!(result[0].0, "???.###");
|
||||||
|
assert_eq!(result[0].1, vec![1, 1, 3]);
|
||||||
|
assert_eq!(result[1].0, ".??..??...?##.");
|
||||||
|
assert_eq!(result[1].1, vec![1, 1, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test(rstest)]
|
||||||
|
#[case("???.### 1,1,3", 1)]
|
||||||
|
#[case(".??..??...?##. 1,1,3", 4)]
|
||||||
|
fn test_length(#[case] input: &str, #[case] expected: usize) {
|
||||||
|
let mut input = String::from(input);
|
||||||
|
|
||||||
|
let re = regex::Regex::new(r"^\.*#{1}\.+#{1}\.+#{3}\.*\s").unwrap();
|
||||||
|
|
||||||
|
let length = gen_permutations(&mut input, 0)
|
||||||
|
.iter()
|
||||||
|
.filter(|s| re.is_match(s))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
assert_eq!(length, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn test_make_regex() {
|
||||||
|
let limits = vec![1, 2, 3, 4];
|
||||||
|
let regex = make_regex(limits);
|
||||||
|
assert_eq!(regex, r"^\.*#{1}\.+#{2}\.+#{3}\.+#{4}\.*$");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test(rstest)]
|
||||||
|
#[case(("???.###", vec![1,1,3]), 1)]
|
||||||
|
#[case((".??..??...?##.", vec![1,1,3]), 4)]
|
||||||
|
#[case(("?###????????", vec![3,2,1]), 10)]
|
||||||
|
fn test_regex(#[case] params: (&str, Vec<usize>), #[case] expected: usize) {
|
||||||
|
let mut input = String::from(params.0);
|
||||||
|
let limits = params.1;
|
||||||
|
|
||||||
|
let re = regex::Regex::new(&make_regex(limits)).unwrap();
|
||||||
|
|
||||||
|
let length = gen_permutations(&mut input, 0)
|
||||||
|
.iter()
|
||||||
|
.filter(|s| re.is_match(s))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
assert_eq!(length, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn test_process() -> miette::Result<()> {
|
||||||
|
let input = include_str!("../test-input.txt");
|
||||||
|
assert_eq!(21, process(input)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::error::AocError;
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn process(input: &str) -> miette::Result<u64, AocError> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test_log::test]
|
||||||
|
fn test_process() -> miette::Result<()> {
|
||||||
|
let input = include_str!("../test-input.txt");
|
||||||
|
assert_eq!(0, process(input)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1
|
||||||
Reference in New Issue
Block a user