Guest User

Untitled

a guest
Dec 19th, 2023
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 7.41 KB | None | 0 0
  1. use std::collections::HashMap;
  2. use std::hash::BuildHasherDefault;
  3. use regex::Regex;
  4. use rustc_hash::{FxHasher, FxHashMap};
  5. use crate::Operator::Immediate;
  6.  
  7. #[derive(Eq, PartialEq, Debug, Copy, Clone)]
  8. enum Operator {
  9.     GT,
  10.     LT,
  11.     Immediate,
  12. }
  13.  
  14. #[derive(Eq, PartialEq, Debug)]
  15. struct Rule<'a> {
  16.    category: Option<&'a str>,
  17.     operator: Operator,
  18.     value: Option<i32>,
  19.     target: &'a str,
  20. }
  21.  
  22. impl<'a> Rule<'a> {
  23.    fn new(input: &'a str) -> Self {
  24.         let rule_re = Regex::new(r"^(?<category>\w)(?<operator>[<|>])(?<value>\d+):(?<target>\w+)$").unwrap();
  25.         if let Some(captures) = rule_re.captures(input) {
  26.             let category = Some(captures.name("category").unwrap().as_str());
  27.             let operator = match captures.name("operator").unwrap().as_str() {
  28.                 ">" => Operator::GT,
  29.                 "<" => Operator::LT,
  30.                 other => panic!("Unknown operator: {}", other)
  31.             };
  32.             let value = Some(captures.name("value").unwrap().as_str().parse().unwrap());
  33.             let target = captures.name("target").unwrap().as_str();
  34.  
  35.             return Self {
  36.                 category,
  37.                 operator,
  38.                 value,
  39.                 target,
  40.             };
  41.         }
  42.  
  43.         Self {
  44.             category: None,
  45.             value: None,
  46.             operator: Immediate,
  47.             target: input,
  48.         }
  49.     }
  50.  
  51.     fn apply(&self, input: &Part) -> Option<&str> {
  52.         match (self.operator, self.category, self.value) {
  53.             (Immediate, _, _) => Some(self.target),
  54.             (Operator::GT, Some(category), Some(value)) =>
  55.                 if input.get(category) > value {
  56.                     Some(self.target)
  57.                 } else {
  58.                     None
  59.                 },
  60.             (Operator::LT, Some(category), Some(value)) =>
  61.                 if input.get(category) < value {
  62.                     Some(self.target)
  63.                 } else {
  64.                     None
  65.                 },
  66.             _ => panic!(),
  67.         }
  68.     }
  69. }
  70.  
  71. #[derive(Debug)]
  72. struct Workflow<'a> {
  73.    name: &'a str,
  74.     rules: Vec<Rule<'a>>,
  75. }
  76.  
  77. impl<'a> Workflow<'a> {
  78.    fn new(input: &'a str) -> Self {
  79.         let re = Regex::new(r"^(?<name>\w+)\{(?<rules>\S+)}$").unwrap();
  80.         let captures = re.captures(input).unwrap();
  81.  
  82.         let name = captures.name("name").unwrap().as_str();
  83.         let rules = captures.name("rules").unwrap().as_str()
  84.             .split(",")
  85.             .map(Rule::new)
  86.             .collect::<Vec<_>>();
  87.  
  88.         Self {
  89.             name,
  90.             rules,
  91.         }
  92.     }
  93.  
  94.     fn apply(&self, part: &Part) -> &str {
  95.         for rule in &self.rules {
  96.             match rule.apply(part) {
  97.                 None => continue,
  98.                 Some(dest) => return dest
  99.             }
  100.         }
  101.         panic!("No destination found");
  102.     }
  103. }
  104.  
  105. #[derive(Debug)]
  106. struct Part {
  107.     x: i32,
  108.     m: i32,
  109.     a: i32,
  110.     s: i32,
  111. }
  112.  
  113. impl Part {
  114.     fn new(input: &str) -> Self {
  115.         let re = Regex::new(r"^\{x=(?<x>\d+),m=(?<m>\d+),a=(?<a>\d+),s=(?<s>\d+)}$").unwrap();
  116.         let captures = re.captures(input).unwrap();
  117.         let x = captures.name("x").unwrap().as_str().parse().unwrap();
  118.         let m = captures.name("m").unwrap().as_str().parse().unwrap();
  119.         let a = captures.name("a").unwrap().as_str().parse().unwrap();
  120.         let s = captures.name("s").unwrap().as_str().parse().unwrap();
  121.         Self { x, m, a, s }
  122.     }
  123.  
  124.     fn get(&self, category: &str) -> i32 {
  125.         match category {
  126.             "x" => self.x,
  127.             "m" => self.m,
  128.             "a" => self.a,
  129.             "s" => self.s,
  130.             x => panic!("Invalid category: {}", x)
  131.         }
  132.     }
  133.  
  134.     fn value(&self) -> i32 {
  135.         self.x + self.m + self.a + self.s
  136.     }
  137. }
  138.  
  139. fn parse(input: &str) -> (HashMap<&str, Workflow, BuildHasherDefault<FxHasher>>, Vec<Part>) {
  140.     let [input_workflows, input_parts] = input.split("\n\n").collect::<Vec<_>>().try_into().unwrap();
  141.  
  142.     let workflow_map = FxHashMap::from_iter(input_workflows.lines()
  143.         .map(Workflow::new)
  144.         .map(|w| (w.name, w))
  145.     );
  146.  
  147.     let parts: Vec<Part> = input_parts.lines()
  148.         .map(Part::new)
  149.         .collect();
  150.  
  151.     (workflow_map, parts)
  152. }
  153.  
  154. fn run_workflows<'a>(workflows: &HashMap<&str, Workflow, BuildHasherDefault<FxHasher>>, parts: &'a Vec<Part>) -> (Vec<&'a Part>, Vec<&'a Part>) {
  155.     let mut accepted = Vec::new();
  156.     let mut rejected = Vec::new();
  157.  
  158.     for part in parts {
  159.  
  160.         let mut dest: &str = "in";
  161.  
  162.         while dest != "A" && dest != "R" {
  163.             print!("{} -> ", dest);
  164.             let workflow = workflows.get(dest).unwrap();
  165.             dest = workflow.apply(part);
  166.         }
  167.  
  168.         println!("{}", dest);
  169.  
  170.         if dest == "A" {
  171.             accepted.push(part);
  172.         }
  173.  
  174.         if dest == "R" {
  175.             rejected.push(part);
  176.         }
  177.     }
  178.  
  179.     return (accepted, rejected);
  180. }
  181.  
  182. fn main() {
  183.     let input = include_str!("../input.txt");
  184.  
  185.     let (workflows, parts) = parse(input);
  186.     let (accepted, _rejected) = run_workflows(&workflows, &parts);
  187.  
  188.     let result = accepted.iter()
  189.         .map(|part| part.value())
  190.         .sum::<i32>();
  191.  
  192.     println!("Result: {}", result);
  193. }
  194.  
  195. #[cfg(test)]
  196. mod test {
  197.     use super::*;
  198.  
  199.     static TEST_INPUT: &str = "px{a<2006:qkq,m>2090:A,rfg}
  200. pv{a>1716:R,A}
  201. lnx{m>1548:A,A}
  202. rfg{s<537:gd,x>2440:R,A}
  203. qs{s>3448:A,lnx}
  204. qkq{x<1416:A,crn}
  205. crn{x>2662:A,R}
  206. in{s<1351:px,qqz}
  207. qqz{s>2770:qs,m<1801:hdj,R}
  208. gd{a>3333:R,R}
  209. hdj{m>838:A,pv}
  210.  
  211. {x=787,m=2655,a=1222,s=2876}
  212. {x=1679,m=44,a=2067,s=496}
  213. {x=2036,m=264,a=79,s=2244}
  214. {x=2461,m=1339,a=466,s=291}
  215. {x=2127,m=1623,a=2188,s=1013}";
  216.  
  217.     #[test]
  218.     fn test_input_1() {
  219.         let input = TEST_INPUT;
  220.         let (workflows, parts) = parse(input);
  221.         let (accepted, rejected) = run_workflows(&workflows, &parts);
  222.  
  223.         let result = accepted.iter()
  224.             .map(|part| part.value())
  225.             .sum::<i32>();
  226.  
  227.         assert_eq!(result, 19114);
  228.     }
  229.  
  230.     #[test]
  231.     fn test_parse() {
  232.         let input = TEST_INPUT;
  233.         parse(input);
  234.     }
  235.  
  236.     #[test]
  237.     fn test_parse_workflow() {
  238.         let input = "px{a<2006:qkq,m>2090:A,rfg}";
  239.         let workflow = Workflow::new(input);
  240.  
  241.         assert_eq!(workflow.name, "px");
  242.         assert_eq!(workflow.rules[0], Rule {
  243.             category: Some("a"),
  244.             operator: Operator::LT,
  245.             value: Some(2006),
  246.             target: "qkq",
  247.         });
  248.         assert_eq!(workflow.rules[1], Rule {
  249.             category: Some("m"),
  250.             operator: Operator::GT,
  251.             value: Some(2090),
  252.             target: "A",
  253.         });
  254.         assert_eq!(workflow.rules[2], Rule {
  255.             category: None,
  256.             operator: Immediate,
  257.             value: None,
  258.             target: "rfg",
  259.         });
  260.     }
  261.  
  262.     #[test]
  263.     fn test_apply_workflow_1() {
  264.         let workflow = Workflow::new("px{a<2006:qkq,m>2090:A,rfg}");
  265.         let part = Part::new("{x=787,m=2655,a=1222,s=2876}");
  266.         let destination = workflow.apply(&part);
  267.         assert_eq!(destination, "qkq");
  268.     }
  269.  
  270.     #[test]
  271.     fn test_apply_workflow_2() {
  272.         let workflow = Workflow::new("px{a<2006:qkq,m>2090:A,rfg}");
  273.         let part = Part::new("{x=1679,m=44,a=2067,s=496}");
  274.         let destination = workflow.apply(&part);
  275.         assert_eq!(destination, "rfg");
  276.     }
  277. }
Advertisement
Add Comment
Please, Sign In to add comment