use std::collections::HashMap; #[path = "../lib.rs"] mod lib; #[derive(Debug)] enum Rule { Less(usize, u64, String), More(usize, u64, String), Always(String), } fn get_cat(s: &str) -> Option { return match s { "x" => Some(0), "m" => Some(1), "a" => Some(2), "s" => Some(3), _ => None, }; } fn parse_rule(s: &str) -> Option { if let Some((c, rest)) = s.split_once('>') { let (k, target) = rest.split_once(':')?; return Some(Rule::More( get_cat(c)?, k.parse().ok()?, target.to_string(), )); } else if let Some((c, rest)) = s.split_once('<') { let (k, target) = rest.split_once(':')?; return Some(Rule::Less( get_cat(c)?, k.parse().ok()?, target.to_string(), )); } else { return Some(Rule::Always(s.to_string())); } } fn parse_workflow(line: &str, workflows: &mut HashMap>) -> Option<()> { let mut rules = vec![]; let (name, rules_s) = line.strip_suffix('}')?.split_once('{')?; for s in rules_s.split(',') { rules.push(parse_rule(s)?); } workflows.insert(name.to_string(), rules); return Some(()); } fn parse_part(line: &str) -> Option<[[u64; 2]; 4]> { let mut result = [[0, 0]; 4]; for s in line.strip_prefix('{')?.strip_suffix('}')?.split(',') { let (c, k) = s.split_once('=')?; let kk = k.parse().ok()?; result[get_cat(c)?] = [kk, kk + 1]; } return Some(result); } fn apply_workflows(part: [[u64; 2]; 4], workflows: &HashMap>, name: &str) -> u64 { if name == "A" { return part.iter().map(|[a, b]| b - a).product(); } else if name == "R" { return 0; } let mut sum = 0; let mut mpart = part.clone(); let workflow = workflows.get(name).unwrap(); for rule in workflow.iter() { match rule { Rule::Less(c, k, target) => { if mpart[*c][0] < *k { if mpart[*c][1] <= *k { sum += apply_workflows(mpart, workflows, target); return sum; } else { let mut p = mpart.clone(); p[*c][1] = *k; mpart[*c][0] = *k; sum += apply_workflows(p, workflows, target); } } } Rule::More(c, k, target) => { if mpart[*c][1] > *k { if mpart[*c][0] >= *k { sum += apply_workflows(mpart, workflows, target); return sum; } else { let mut p = mpart.clone(); p[*c][0] = *k + 1; mpart[*c][1] = *k + 1; sum += apply_workflows(p, workflows, target); } } } Rule::Always(target) => { sum += apply_workflows(mpart, workflows, target); return sum; } } } unreachable!(); } fn main() { let mut workflows = HashMap::new(); let mut rules_done = false; let mut sum = 0; for line in lib::iter_input() { if rules_done { let part = parse_part(&line).unwrap(); if apply_workflows(part, &workflows, "in") != 0 { sum += part.iter().map(|[a, _]| a).sum::(); } } else if line == "" { rules_done = true; } else { parse_workflow(&line, &mut workflows).unwrap(); } } println!("part1: {}", sum); println!("part2: {}", apply_workflows([[1, 4001]; 4], &workflows, "in")); }