day19: problem 1 solution

This commit is contained in:
2023-12-18 22:06:03 -08:00
parent 8200c1a8cf
commit a5dea64b32
4 changed files with 1029 additions and 0 deletions

239
19/src/main.rs Normal file
View File

@ -0,0 +1,239 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// DATA
#[derive(Debug)]
struct RulePredicate {
op: PredicateOperator,
var: char,
}
#[derive(Debug, Clone)]
enum RuleAction {
Terminate(bool),
Jump(String),
}
impl From<&str> for RuleAction {
fn from(s: &str) -> Self {
match s {
"A" => Self::Terminate(true),
"R" => Self::Terminate(false),
s => Self::Jump(s.into()),
}
}
}
#[derive(Debug)]
struct Rule {
pred: RulePredicate,
action: RuleAction,
}
#[derive(Debug)]
enum PredicateOperator {
Always,
Lt(u64),
Gt(u64),
}
impl From<&str> for PredicateOperator {
fn from(s: &str) -> Self {
let (op_s, val_s) = s.split_at(1);
match op_s {
"<" => PredicateOperator::Lt(val_s.parse().unwrap()),
">" => PredicateOperator::Gt(val_s.parse().unwrap()),
s => panic!("unknown operator {}", s),
}
}
}
impl From<&str> for RulePredicate {
fn from(s: &str) -> Self {
let (var_s, pred_s) = s.split_at(1);
Self {
op: pred_s.into(),
var: var_s.chars().next().unwrap(),
}
}
}
impl RulePredicate {
fn check(&self, part: &Part) -> bool {
match self.op {
PredicateOperator::Always => true,
PredicateOperator::Gt(val) => part.0[&self.var] > val,
PredicateOperator::Lt(val) => part.0[&self.var] < val,
}
}
}
impl From<&str> for Rule {
fn from(s: &str) -> Self {
println!("parsing {}", s);
if let Some((predicate_s, action_s)) = s.split_once(':') {
Self {
pred: predicate_s.into(),
action: action_s.into(),
}
} else {
Self {
pred: RulePredicate {
op: PredicateOperator::Always,
var: '\0',
},
action: s.into(),
}
}
}
}
#[derive(Debug)]
struct Workflow {
name: String,
rules: Vec<Rule>,
}
impl From<&str> for Workflow {
fn from(s: &str) -> Self {
let (name_s, rest_s) = s.split_once('{').unwrap();
println!("name: {} rest: {}", name_s, rest_s);
let rules = rest_s.split_once('}').unwrap().0.split(',').map(|r| r.into()).collect();
Self {
name: name_s.into(),
rules,
}
}
}
impl Workflow {
fn execute(&self, part: &Part) -> RuleAction {
for r in &self.rules {
if r.pred.check(part) {
return r.action.clone()
}
}
panic!("unhandled part {:?}", part);
}
}
impl Workflows {
fn execute(&self, part: &Part) -> bool {
let mut action = RuleAction::Jump("in".into());
loop {
match &action {
RuleAction::Terminate(b) => return *b,
RuleAction::Jump(j) => {
let next_workflow = &self.0[j];
action = next_workflow.execute(part)
}
}
}
}
}
#[derive(Debug)]
struct Workflows(HashMap<String, Workflow>);
#[derive(Debug)]
struct Part(HashMap<char, u64>);
impl From<&str> for Part {
fn from(s: &str) -> Self {
let (_, vars_s) = s.split_once('{').unwrap();
let vars = vars_s.split_once('}').unwrap().0.split(',');
let mut part = HashMap::new();
for var in vars {
let (name, val) = var.split_once('=').unwrap();
part.insert(name.chars().next().unwrap(), val.parse().unwrap());
}
Self(part)
}
}
type Parts = Vec<Part>;
// PROBLEM 1 solution
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
let mut wfs = Workflows(HashMap::new());
let mut parts: Parts = Vec::new();
while let Some(Ok(line)) = input.next() {
println!("line: {}", line);
if line != "" {
let wf: Workflow = line.as_str().into();
wfs.0.insert(wf.name.clone(), wf);
} else {
break;
}
}
while let Some(Ok(line)) = input.next() {
parts.push(line.as_str().into());
}
parts.iter().filter(|part| wfs.execute(part)).map(|part| part.0.values().sum::<u64>()).sum::<u64>()
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"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 problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 19114);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}