diff --git a/day19/Cargo.toml b/day19/Cargo.toml new file mode 100644 index 0000000..f4f4789 --- /dev/null +++ b/day19/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day19" +version = "0.1.0" +edition = "2021" + +[dependencies] +regex = "1.11.1" diff --git a/day19/src/main.rs b/day19/src/main.rs new file mode 100644 index 0000000..0957e3b --- /dev/null +++ b/day19/src/main.rs @@ -0,0 +1,126 @@ +use std::collections::HashMap; +use std::env; +use std::fs; + +use regex::Regex; + +fn main() { + println!("Hello, AoC day 13!"); + + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("wrong number of arguments!"); + std::process::exit(1); + } + + let file_path = &args[1]; + + let binding = fs::read_to_string(file_path) + .expect("should be able to read the file"); + + let (towels, designs) = binding.split_once("\n\n").expect("should have two sections"); + + let all_towels: Vec<&str> = towels.split(", ").collect(); + + let re = Regex::new( + &format!("^({})+$", all_towels.join("|")) + ).expect("making the regex"); + + let mut count = 0; + for line in designs.lines() { + if re.is_match(line) { + count += 1; + } + } + + println!("found {count} valid designs"); + + let mut tree = build_tree(&all_towels); + + //println!("{tree:?}"); + println!("{} nodes in the tree", tree.len()); + + let mut count = 0; + + for line in designs.lines() { + count += eat(&mut tree, line); + } + + println!("found {count} solutions total"); +} + +fn eat(tree: &mut Vec, haystack: &str) -> u128 { + let mut heads: HashMap = HashMap::new(); + heads.insert(0, 1); + + //println!("examining {haystack}"); + + for c in haystack.chars() { + // iterating by character + let mut new_heads = HashMap::new(); + + //println!("looking at {c}; the heads we're tracking are {heads:?}"); + + // this first loop branches to the start from valid states + for (node_idx, num_heads) in heads.drain() { + let TreeCell { next: _, valid } = &tree[node_idx]; + if *valid { + let next_idx = 0; + let existing_heads = new_heads.get(&next_idx).unwrap_or(&0); + new_heads.insert(next_idx, existing_heads + num_heads); + } + let existing_heads = new_heads.get(&node_idx).unwrap_or(&0); + new_heads.insert(node_idx, existing_heads + num_heads); + } + + // this second loop advances all the heads + for (node_idx, num_heads) in new_heads.drain() { + let TreeCell { next, valid: _ } = &tree[node_idx]; + if let Some(next_idx) = next.get(&c) { + heads.insert(*next_idx, num_heads); + } + } + } + + let mut count = 0; + + for (node_idx, number) in heads { + if tree[node_idx].valid { count += number; } + } + //println!("there are {count} heads in success states"); + + return count; +} + + +#[derive(PartialEq, Eq, Clone, Debug)] +struct TreeCell {next: HashMap, valid: bool} + +fn build_tree(patterns: &[&str]) -> Vec { + let mut cells = vec![TreeCell {next: HashMap::new(), valid: false}]; + + for stripes in patterns { + add_to_tree(stripes, &mut cells, 0); + } + + return cells; +} + +fn add_to_tree(pattern: &str, tree: &mut Vec, index: usize) { + match pattern.chars().next() { + None => tree[index].valid = true, + Some(c) => { + if tree[index].next.contains_key(&c) { + add_to_tree(&pattern[1..], tree, *tree[index].next.get(&c).unwrap()); + } else { + let new_index = tree.len(); + let new_treecell = TreeCell { next: HashMap::new(), valid: false }; + tree.push(new_treecell); // now this is at new_index + tree[index].next.insert(c, new_index); + + add_to_tree(&pattern[1..], tree, new_index); + + } + } + } +} \ No newline at end of file