day19 solution
parent
86f39513b0
commit
05861a06b0
|
@ -0,0 +1,251 @@
|
|||
use aoc_parse::{parser, prelude::*};
|
||||
use aoc_runner_derive::aoc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct Part {
|
||||
x: u64,
|
||||
m: u64,
|
||||
a: u64,
|
||||
s: u64,
|
||||
}
|
||||
|
||||
impl Part {
|
||||
fn sum(&self) -> u64 {
|
||||
self.x + self.m + self.a + self.s
|
||||
}
|
||||
|
||||
fn val_for(&self, field: &str) -> u64 {
|
||||
match field {
|
||||
"x" => self.x,
|
||||
"m" => self.m,
|
||||
"a" => self.a,
|
||||
"s" => self.s,
|
||||
n @ _ => panic!("bad val_for field {}", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
enum RuleOp {
|
||||
NextState,
|
||||
LessThan,
|
||||
GreaterThan,
|
||||
}
|
||||
|
||||
struct Rule {
|
||||
op: RuleOp,
|
||||
field: String,
|
||||
count: u64,
|
||||
dest: String,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
fn next_state(&self) -> bool {
|
||||
self.op == RuleOp::NextState
|
||||
}
|
||||
|
||||
fn match_lt(&self, part: &Part) -> bool {
|
||||
self.op == RuleOp::LessThan && part.val_for(&self.field) < self.count
|
||||
}
|
||||
|
||||
fn match_gt(&self, part: &Part) -> bool {
|
||||
self.op == RuleOp::GreaterThan && part.val_for(&self.field) > self.count
|
||||
}
|
||||
}
|
||||
|
||||
type Rules = HashMap<String, Vec<Rule>>;
|
||||
|
||||
fn parse(input: &str) -> (Rules, Vec<Part>) {
|
||||
let mut rules: Rules = HashMap::new();
|
||||
let rparse = parser!(string(lower+) "{" string(any_char+) "}");
|
||||
let ltparse = parser!(string(lower) "<" u64 ":" string(alpha+));
|
||||
let gtparse = parser!(string(lower) ">" u64 ":" string(alpha+));
|
||||
for line in input.lines() {
|
||||
if line.is_empty() {
|
||||
break;
|
||||
}
|
||||
let (rname, raw_rules) = rparse.parse(line).unwrap();
|
||||
let new_rules = raw_rules
|
||||
.split(",")
|
||||
.map(|t| {
|
||||
if t.contains("<") {
|
||||
let (f, c, d) = ltparse.parse(t).unwrap();
|
||||
Rule {
|
||||
op: RuleOp::LessThan,
|
||||
field: f,
|
||||
count: c,
|
||||
dest: d,
|
||||
}
|
||||
} else if t.contains(">") {
|
||||
let (f, c, d) = gtparse.parse(t).unwrap();
|
||||
Rule {
|
||||
op: RuleOp::GreaterThan,
|
||||
field: f,
|
||||
count: c,
|
||||
dest: d,
|
||||
}
|
||||
} else {
|
||||
Rule {
|
||||
op: RuleOp::NextState,
|
||||
field: String::from("f"),
|
||||
count: 0,
|
||||
dest: String::from(t),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
rules.insert(rname, new_rules);
|
||||
}
|
||||
|
||||
let mut parts: Vec<Part> = vec![];
|
||||
let pparse = parser!("{x=" u64 ",m=" u64 ",a=" u64 ",s=" u64 "}");
|
||||
let mut skipped = false;
|
||||
for line in input.lines() {
|
||||
if !skipped {
|
||||
if line.is_empty() {
|
||||
skipped = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let (x, m, a, s) = pparse.parse(line).unwrap();
|
||||
parts.push(Part { x, m, a, s });
|
||||
}
|
||||
|
||||
(rules, parts)
|
||||
}
|
||||
|
||||
fn filter_part(rules: &Rules, part: &Part) -> u64 {
|
||||
let mut b = rules.get("in").unwrap();
|
||||
loop {
|
||||
let mut found = false;
|
||||
for r in b.iter() {
|
||||
if r.match_lt(part) || r.match_gt(part) || r.next_state() {
|
||||
match r.dest.as_str() {
|
||||
"A" => return part.sum(),
|
||||
"R" => return 0,
|
||||
_ => {
|
||||
found = true;
|
||||
b = rules.get(&r.dest).unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
break;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[aoc(day19, part1)]
|
||||
fn part1(input: &str) -> u64 {
|
||||
let (rules, parts) = parse(input);
|
||||
parts.iter().map(|p| filter_part(&rules, &p)).sum()
|
||||
}
|
||||
|
||||
fn combinations(weights: [(u64, u64); 4]) -> u64 {
|
||||
(0..4)
|
||||
.into_iter()
|
||||
.map(|i| weights[i].0.abs_diff(weights[i].1))
|
||||
.product()
|
||||
}
|
||||
|
||||
fn windex(field: &str) -> usize {
|
||||
match field {
|
||||
"x" => 0,
|
||||
"m" => 1,
|
||||
"a" => 2,
|
||||
"s" => 3,
|
||||
_ => panic!("bad field"),
|
||||
}
|
||||
}
|
||||
|
||||
fn split_lt(count: u64, input: (u64, u64)) -> ((u64, u64), (u64, u64)) {
|
||||
let one = input.0..input.1.min(count);
|
||||
let two = count..input.1;
|
||||
|
||||
((one.start, one.end), (two.start, two.end))
|
||||
}
|
||||
|
||||
fn split_gt(count: u64, input: (u64, u64)) -> ((u64, u64), (u64, u64)) {
|
||||
let one = (count + 1)..input.1;
|
||||
let two = input.0..(count + 1);
|
||||
|
||||
((one.start, one.end), (two.start, two.end))
|
||||
}
|
||||
|
||||
fn traverse_rules(rules: &Rules, cur: &str, mut weights: [(u64, u64); 4]) -> u64 {
|
||||
let mut c = 0;
|
||||
for r in rules.get(cur).unwrap().iter() {
|
||||
match r.op {
|
||||
RuleOp::NextState => {
|
||||
c += match r.dest.as_str() {
|
||||
"A" => combinations(weights),
|
||||
"R" => 0,
|
||||
_ => traverse_rules(rules, &r.dest, weights),
|
||||
}
|
||||
}
|
||||
RuleOp::LessThan => {
|
||||
let i = windex(&r.field);
|
||||
let mut next_weights = weights.clone();
|
||||
let (next_range, cur_range) = split_lt(r.count, weights[i]);
|
||||
weights[i] = cur_range;
|
||||
next_weights[i] = next_range;
|
||||
c += match r.dest.as_str() {
|
||||
"A" => combinations(next_weights),
|
||||
"R" => 0,
|
||||
_ => traverse_rules(rules, &r.dest, next_weights),
|
||||
}
|
||||
}
|
||||
RuleOp::GreaterThan => {
|
||||
let i = windex(&r.field);
|
||||
let mut next_weights = weights.clone();
|
||||
let (next_range, cur_range) = split_gt(r.count, weights[i]);
|
||||
weights[i] = cur_range;
|
||||
next_weights[i] = next_range;
|
||||
c += match r.dest.as_str() {
|
||||
"A" => combinations(next_weights),
|
||||
"R" => 0,
|
||||
_ => traverse_rules(rules, &r.dest, next_weights),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
c
|
||||
}
|
||||
|
||||
#[aoc(day19, part2)]
|
||||
fn part2(input: &str) -> u64 {
|
||||
let (rules, _parts) = parse(input);
|
||||
traverse_rules(&rules, "in", [(1, 4001); 4])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE_DATA: &'static 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 sample_data() {
|
||||
assert_eq!(part1(&SAMPLE_DATA), 19114);
|
||||
assert_eq!(part2(&SAMPLE_DATA), 167409079868000);
|
||||
}
|
||||
}
|
|
@ -18,5 +18,6 @@ mod day15;
|
|||
mod day16;
|
||||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
|
||||
aoc_lib! { year = 2023 }
|
||||
|
|
Loading…
Reference in New Issue