mirror of
https://github.com/anthonyoteri/advent-of-code-2023.git
synced 2026-06-05 18:46:54 -04:00
@@ -21,6 +21,7 @@ thiserror = "1.0.50"
|
||||
dhat = "0.3.2"
|
||||
env_logger = "0.10.1"
|
||||
test-log = "0.2.13"
|
||||
regex = "1.10.2"
|
||||
|
||||
|
||||
[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