From 702a77a31edd531600ce7f1999878115ce4fc14c Mon Sep 17 00:00:00 2001 From: kageru Date: Tue, 19 Dec 2023 16:19:54 +0100 Subject: [PATCH] add 2023/19/2 --- 2023/src/bin/day19.rs | 65 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/2023/src/bin/day19.rs b/2023/src/bin/day19.rs index 75c4aff..77a59b2 100644 --- a/2023/src/bin/day19.rs +++ b/2023/src/bin/day19.rs @@ -1,15 +1,20 @@ -#![feature(test, iter_array_chunks)] +#![feature(test, iter_array_chunks, inline_const)] extern crate test; use self::Result::*; use aoc2023::{boilerplate, common::*}; use fnv::FnvHashMap as HashMap; +use std::ops::RangeInclusive; const DAY: usize = 19; -type I = u32; -type Rule<'a> = (Comp, Result<'a>); -type Parsed<'a> = (HashMap<&'a str, Vec>>, Vec); const LIMIT: I = 4000; +type I = usize; +type Rule<'a> = (Comp, Result<'a>); +type Rules<'a> = HashMap<&'a str, Vec>>; +type Parsed<'a> = (Rules<'a>, Vec); +type Part = [I; 4]; +type Ranges = [RangeInclusive; 4]; + #[derive(Clone, Copy, Debug)] enum Result<'a> { Accepted, @@ -17,8 +22,7 @@ enum Result<'a> { Jump(&'a str), } -type Part = [I; 4]; - +#[derive(Clone, Copy, Debug)] enum Comp { GT(usize, I), LT(usize, I), @@ -57,11 +61,10 @@ fn parse_rule<'a>(r: &'a str) -> Rule { fn parse_input(raw: &str) -> Parsed { let (rules, parts) = raw.split_once("\n\n").unwrap(); - let parts = - parts.lines().map(|l| l[1..].trim_matches('}').split(',').array_chunks().next().unwrap().map(|s| parse_num(&s[2..]))).collect(); + let parts = parts.lines().map(|l| l[1..(l.len() - 1)].split(',').array_chunks().next().unwrap().map(|s| parse_num(&s[2..]))).collect(); let rules = rules .lines() - .map(|line| line.trim_end_matches('}').split_once('{').unwrap()) + .map(|line| line[..(line.len() - 1)].split_once('{').unwrap()) .map(|(name, rules)| (name, rules.split(',').map(parse_rule).collect())) .collect(); (rules, parts) @@ -95,8 +98,41 @@ fn part1((rules, parts): &Parsed) -> I { .sum() } -fn part2(parsed: &Parsed) -> usize { - unimplemented!() +fn num_ranges(ranges: &Ranges) -> usize { + ranges.iter().map(|r| r.end() - r.start() + 1).product() +} + +fn split(all_rules: &Rules, rules: &[Rule], rule_idx: usize, matching_range: Ranges, nonmatching_range: Ranges, res: Result<'_>) -> usize { + let nonmatching = accepted(all_rules, rules, rule_idx + 1, nonmatching_range); + match res { + Jump(dst) => accepted(all_rules, all_rules.get(dst).unwrap(), 0, matching_range) + nonmatching, + Accepted => num_ranges(&matching_range) + nonmatching, + Rejected => nonmatching, + } +} + +fn accepted(all_rules: &Rules, rules: &[Rule], rule_idx: usize, mut ranges: Ranges) -> usize { + match rules[rule_idx] { + (Comp::None, Jump(dst)) => accepted(all_rules, all_rules.get(dst).unwrap(), 0, ranges), + (Comp::None, Accepted) => num_ranges(&ranges), + (Comp::None, Rejected) => 0, + (Comp::LT(idx, val), res) => { + let mut matching_ranges = ranges.clone(); + matching_ranges[idx] = *matching_ranges[idx].start()..=val - 1; + ranges[idx] = val..=*ranges[idx].end(); + split(all_rules, rules, rule_idx, matching_ranges, ranges, res) + } + (Comp::GT(idx, val), res) => { + let mut matching_ranges = ranges.clone(); + matching_ranges[idx] = (val + 1)..=*matching_ranges[idx].end(); + ranges[idx] = *ranges[idx].start()..=val; + split(all_rules, rules, rule_idx, matching_ranges, ranges, res) + } + } +} + +fn part2((rules, _): &Parsed) -> usize { + accepted(rules, rules.get("in").unwrap(), 0, [const { 1..=LIMIT }; 4]) } boilerplate! { @@ -120,9 +156,12 @@ hdj{m>838:A,pv} {x=2127,m=1623,a=2188,s=1013}" for tests: { part1: { TEST_INPUT => 19114 }, - part2: { TEST_INPUT => 0 }, + part2: { TEST_INPUT => 167409079868000 }, + }, + unittests : { + num_ranges: { &[1..=10, 1..=10, 1..=10, 1..=10] => 10_000 }, }, bench1 == 333263, - bench2 == 0, + bench2 == 130745440937650, bench_parse: |(rules, parts): &Parsed| (rules.len(), parts.len()) => (572, 200), }