This commit is contained in:
Andrew Glaze 2023-12-20 12:05:52 -05:00
parent e3f0f932e4
commit d3c11dcbed
4 changed files with 292 additions and 0 deletions

45
Cargo.lock generated
View File

@ -11,11 +11,21 @@ dependencies = [
"array2d",
"itertools",
"prev-iter",
"regex",
"rust-crypto",
"strum",
"strum_macros",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "aoc-runner"
version = "0.3.0"
@ -96,6 +106,12 @@ version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "prev-iter"
version = "0.1.2"
@ -167,6 +183,35 @@ dependencies = [
"rand_core 0.3.1",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rust-crypto"
version = "0.2.36"

View File

@ -15,3 +15,4 @@ array2d = "0.3.0"
strum = "0.25.0"
strum_macros = "0.25"
prev-iter = "0.1.2"
regex = "1.10.2"

245
src/day19.rs Normal file
View File

@ -0,0 +1,245 @@
use std::collections::HashMap;
use regex::{Regex, Match};
enum Rule {
ACCEPTED,
REJECTED,
GOTO(String),
GT(String, usize, String),
LT(String, usize, String),
}
use aoc_runner_derive::{aoc, aoc_generator};
#[aoc_generator(day19, part1)]
fn parse(input: &str) -> (HashMap<String, Vec<Rule>>, Vec<HashMap<String, usize>>) {
let (workflows_input, ratings_input) = input.split_once("\n\n").unwrap();
let workflows = parse_workflows(workflows_input);
// screw it im using regex
let rer = Regex::new(r"^\{x=(\d+),m=(\d+),a=(\d+),s=(\d+)\}$").unwrap();
let ratings: Vec<HashMap<String, usize>> = ratings_input.lines().map(|line| {
let caps = rer.captures(line).unwrap();
let x = parse_usize(caps.get(1));
let m = parse_usize(caps.get(2));
let a = parse_usize(caps.get(3));
let s = parse_usize(caps.get(4));
let mut vals = HashMap::new();
vals.insert("x".to_string(), x);
vals.insert("m".to_string(), m);
vals.insert("a".to_string(), a);
vals.insert("s".to_string(), s);
vals
}).collect();
(workflows, ratings)
}
fn parse_usize(g: Option<Match>) -> usize {
g.map_or(0, |m| m.as_str().parse().unwrap())
}
fn parse_workflows(workflows_input: &str) -> HashMap<String, Vec<Rule>> {
let mut workflows: HashMap<String, Vec<Rule>> = HashMap::new();
let rew = Regex::new(r"^(.+)\{(.+)\}$").unwrap();
workflows_input.lines().for_each(|line| {
let caps = rew.captures(line).unwrap();
let key = caps.get(1).unwrap().as_str();
let rules_str = caps.get(2).unwrap().as_str();
let rules = rules_str.split(",").map(|expr| {
if expr == "A" {
return Rule::ACCEPTED;
}
if expr == "R" {
return Rule::REJECTED;
}
if !expr.contains(":") {
return Rule::GOTO(expr.to_string());
}
let (rule, to) = expr.split_once(":").unwrap();
return if rule.contains(">") {
let (prop, val) = rule.split_once(">").unwrap();
Rule::GT(prop.to_string(), val.parse().unwrap(), to.to_string())
} else if rule.contains("<") {
let (prop, val) = rule.split_once("<").unwrap();
Rule::LT(prop.to_string(), val.parse().unwrap(), to.to_string())
} else {
panic!("Unknown rule {}", rule)
};
}).collect();
workflows.insert(key.to_string(), rules);
});
workflows
}
#[aoc(day19, part1)]
fn part1((workflows, ratings): &(HashMap<String, Vec<Rule>>, Vec<HashMap<String, usize>>)) -> usize {
let mut accepted: Vec<HashMap<String, usize>> = vec!();
'outer: for rating in ratings {
let mut wf_key = "in";
'middle: loop {
if wf_key == "A" {
accepted.push(rating.clone());
continue 'outer;
} else if wf_key == "R" {
continue 'outer;
}
let rules = workflows.get(wf_key).unwrap();
for rule in rules {
match rule {
Rule::ACCEPTED => {
accepted.push(rating.clone());
continue 'outer;
}
Rule::REJECTED => {
continue 'outer;
}
Rule::GOTO(new_wf_key) => {
wf_key = new_wf_key;
continue 'middle;
}
Rule::GT(prop, val, to) => {
if rating.get(prop.as_str()).unwrap() > val {
wf_key = to;
continue 'middle;
}
}
Rule::LT(prop, val, to) => {
if rating.get(prop.as_str()).unwrap() < val {
wf_key = to;
continue 'middle;
}
}
}
}
}
}
accepted.iter().map(|rating| rating.values().sum::<usize>()).sum::<usize>()
}
#[aoc_generator(day19, part2)]
fn parse_part2(input: &str) -> HashMap<String, Vec<Rule>> {
let (workflows_input, _) = input.split_once("\n\n").unwrap();
parse_workflows(workflows_input)
}
#[aoc(day19, part2)]
fn part2(workflows: &HashMap<String, Vec<Rule>>) -> usize {
let mut stack: Vec<((usize, usize), (usize, usize), (usize, usize), (usize, usize), &str, usize)> =
vec! {((1, 4000), (1, 4000), (1, 4000), (1, 4000), "in", 0)};
let mut accepted: Vec<((usize, usize), (usize, usize), (usize, usize), (usize, usize))> = vec!();
while let Some(range) = stack.pop() {
let (x, m, a, s, wf_key, rule_key) = range;
if wf_key == "A" {
accepted.push((x, m, a, s));
continue;
} else if wf_key == "R" {
continue;
}
if x.0 > x.1 || m.0 > m.1 || a.0 > a.1 || s.0 > s.1 { continue }
let rules = workflows.get(wf_key).unwrap();
let rule = &rules[rule_key];
match rule {
Rule::ACCEPTED => {
accepted.push((x, m, a, s));
continue;
}
Rule::REJECTED => {
continue;
}
Rule::GOTO(new_wf_key) => {
stack.push((x, m, a, s, new_wf_key, 0));
continue;
}
Rule::GT(prop, val, to) => {
match prop.as_str() {
"x" => {
stack.push(((val + 1, x.1), m, a, s, to.as_str(), 0));
stack.push(((x.0, *val), m, a, s, wf_key, rule_key + 1));
}
"m" => {
stack.push((x, (val + 1, m.1), a, s, to.as_str(), 0));
stack.push((x, (m.0, *val), a, s, wf_key, rule_key + 1));
}
"a" => {
stack.push((x, m, (val + 1, a.1), s, to.as_str(), 0));
stack.push((x, m, (a.0, *val), s, wf_key, rule_key + 1));
}
"s" => {
stack.push((x, m, a, (val + 1, s.1), to.as_str(), 0));
stack.push((x, m, a, (s.0, *val), wf_key, rule_key + 1));
}
_ => { panic!("unknown prop {}", prop) }
}
}
Rule::LT(prop, val, to) => {
match prop.as_str() {
"x" => {
stack.push(((x.0, val - 1), m, a, s, to.as_str(), 0));
stack.push(((*val, x.1), m, a, s, wf_key, rule_key + 1));
}
"m" => {
stack.push((x, (m.0, val - 1), a, s, to.as_str(), 0));
stack.push((x, (*val, m.1), a, s, wf_key, rule_key + 1));
}
"a" => {
stack.push((x, m, (a.0, val - 1), s, to.as_str(), 0));
stack.push((x, m, (*val, a.1), s, wf_key, rule_key + 1));
}
"s" => {
stack.push((x, m, a, (s.0, val - 1), to.as_str(), 0));
stack.push((x, m, a, (*val, s.1), wf_key, rule_key + 1));
}
_ => { panic!("unknown prop {}", prop) }
}
}
}
}
accepted.iter().map(|(x, m, a, s)| {
(x.1 - x.0 + 1) * (m.1 - m.0 + 1) * (a.1 - a.0 + 1) * (s.1 - s.0 + 1)
}).sum()
}
#[cfg(test)]
mod tests {
use super::*;
const EX: &str = r"px{a<2006:qkq,m>2090:A,rfg}
pv{a>1716:R,A}
lnx{m>1548:A,A}
rfg{s<537:gd,x>2440:R,A}
qs{s>3448:A,lnx}
qkq{x<1416:A,crn}
crn{x>2662:A,R}
in{s<1351:px,qqz}
qqz{s>2770:qs,m<1801:hdj,R}
gd{a>3333:R,R}
hdj{m>838:A,pv}
{x=787,m=2655,a=1222,s=2876}
{x=1679,m=44,a=2067,s=496}
{x=2036,m=264,a=79,s=2244}
{x=2461,m=1339,a=466,s=291}
{x=2127,m=1623,a=2188,s=1013}";
#[test]
fn part1_example() {
assert_eq!(part1(&parse(EX)), 19114);
}
#[test]
fn part2_example() {
assert_eq!(part2(&parse_part2(EX)), 167409079868000);
}
}

View File

@ -1,3 +1,4 @@
mod day19;
mod day18;
mod day17;
mod day16;