day 19
This commit is contained in:
187
day19/src/main.rs
Normal file
187
day19/src/main.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
use std::cmp;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::read_to_string;
|
||||
use std::ops::Range;
|
||||
use std::time::Instant;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
enum RuleConditionVariable {
|
||||
X,
|
||||
M,
|
||||
A,
|
||||
S,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
enum RuleConditionOperation {
|
||||
Less,
|
||||
Greater,
|
||||
}
|
||||
|
||||
struct Rule<'a> {
|
||||
variable: RuleConditionVariable,
|
||||
operation: RuleConditionOperation,
|
||||
argument: i32,
|
||||
result: &'a str,
|
||||
}
|
||||
|
||||
fn check_part(workflows: &HashMap<&str, Vec<Rule>>, part: &[i32; 4]) -> bool {
|
||||
let mut workflow: &Vec<Rule> = &workflows["in"];
|
||||
let mut workflow_index: usize = 0;
|
||||
while workflow_index < workflow.len() {
|
||||
let rule = &workflow[workflow_index];
|
||||
let mut passed_condition = true;
|
||||
if rule.variable != RuleConditionVariable::None {
|
||||
match rule.operation {
|
||||
RuleConditionOperation::Less => {
|
||||
passed_condition = part[rule.variable as usize] < rule.argument;
|
||||
}
|
||||
RuleConditionOperation::Greater => {
|
||||
passed_condition = part[rule.variable as usize] > rule.argument;
|
||||
}
|
||||
}
|
||||
}
|
||||
if passed_condition {
|
||||
match rule.result {
|
||||
"R" => return false,
|
||||
"A" => return true,
|
||||
_ => {
|
||||
workflow = &workflows[rule.result];
|
||||
workflow_index = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
workflow_index += 1;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn count_options(workflows: &HashMap<&str, Vec<Rule>>,
|
||||
start: &str, parts: &[Range<i32>; 4]) -> u64 {
|
||||
let workflow: &Vec<Rule> = &workflows[start];
|
||||
let mut workflow_index: usize = 0;
|
||||
let mut options = 0u64;
|
||||
let mut checked_parts = parts.clone();
|
||||
let count_ranges_options = |parts: &[Range<i32>; 4], next: &str| {
|
||||
match next {
|
||||
"R" => 0,
|
||||
"A" => parts.iter().fold(1u64, |acc, range| {
|
||||
acc * cmp::max(0, range.end - range.start) as u64
|
||||
}),
|
||||
_ => count_options(workflows, next, parts)
|
||||
}
|
||||
};
|
||||
while workflow_index < workflow.len() {
|
||||
let rule = &workflow[workflow_index];
|
||||
if rule.variable != RuleConditionVariable::None {
|
||||
let mut new_parts = checked_parts.clone();
|
||||
let new_parts_var = &mut new_parts[rule.variable as usize];
|
||||
let checked_parts_var = &mut checked_parts[rule.variable as usize];
|
||||
match rule.operation {
|
||||
RuleConditionOperation::Less => {
|
||||
new_parts_var.end = rule.argument;
|
||||
checked_parts_var.start = rule.argument;
|
||||
}
|
||||
RuleConditionOperation::Greater => {
|
||||
new_parts_var.start = rule.argument + 1;
|
||||
checked_parts_var.end = rule.argument + 1;
|
||||
}
|
||||
}
|
||||
if new_parts_var.start < new_parts_var.end {
|
||||
options += count_ranges_options(&new_parts, rule.result);
|
||||
}
|
||||
if checked_parts_var.start >= checked_parts_var.end {
|
||||
return options;
|
||||
}
|
||||
} else {
|
||||
return options + count_ranges_options(&checked_parts, rule.result);
|
||||
}
|
||||
workflow_index += 1;
|
||||
}
|
||||
panic!("No rules for remaining ranges");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let time_start = Instant::now();
|
||||
let input_str = read_to_string("input.txt").unwrap();
|
||||
let time_start_no_io = Instant::now();
|
||||
let mut workflows: HashMap<&str, Vec<Rule>> = HashMap::new();
|
||||
let mut parts: Vec<[i32; 4]> = vec![];
|
||||
let mut currently_parsing_rules = true;
|
||||
let rule_re = Regex::new("(?:([xmas])([><])([0-9]+):)?([a-zAR]+),?").unwrap();
|
||||
let part_re = Regex::new("\\{x=([0-9]+),m=([0-9]+),a=([0-9]+),s=([0-9]+)}").unwrap();
|
||||
for line in input_str.lines() {
|
||||
if currently_parsing_rules {
|
||||
let mut workflow_key: &str = "";
|
||||
let mut rules: Vec<Rule> = vec![];
|
||||
for captures in rule_re.captures_iter(line) {
|
||||
if workflow_key.is_empty() {
|
||||
workflow_key = captures.get(4).unwrap().as_str();
|
||||
} else {
|
||||
if let Some(_) = captures.get(1) {
|
||||
rules.push(Rule {
|
||||
variable: match captures[1].as_bytes()[0] {
|
||||
b'x' => RuleConditionVariable::X,
|
||||
b'm' => RuleConditionVariable::M,
|
||||
b'a' => RuleConditionVariable::A,
|
||||
b's' => RuleConditionVariable::S,
|
||||
_ => RuleConditionVariable::None
|
||||
},
|
||||
operation: match captures[2].as_bytes()[0] {
|
||||
b'<' => RuleConditionOperation::Less,
|
||||
_ => RuleConditionOperation::Greater
|
||||
},
|
||||
argument: captures[3].parse::<i32>().unwrap(),
|
||||
result: captures.get(4).unwrap().as_str(),
|
||||
});
|
||||
} else {
|
||||
rules.push(Rule {
|
||||
variable: RuleConditionVariable::None,
|
||||
operation: RuleConditionOperation::Less,
|
||||
argument: 0,
|
||||
result: captures.get(4).unwrap().as_str(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if workflow_key.is_empty() {
|
||||
currently_parsing_rules = false;
|
||||
continue;
|
||||
} else {
|
||||
// Remove useless conditions (those that give the same result as the last)
|
||||
while rules.len() > 1 {
|
||||
if rules[rules.len() - 1].variable == RuleConditionVariable::None
|
||||
&& rules[rules.len() - 1].result == rules[rules.len() - 2].result {
|
||||
rules.remove(rules.len() - 2);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
workflows.insert(workflow_key, rules);
|
||||
}
|
||||
} else {
|
||||
if let Some(capture) = part_re.captures(line) {
|
||||
let mut part = [0; 4];
|
||||
for (i, re_match) in capture.iter().skip(1).enumerate() {
|
||||
part[i] = re_match.unwrap().as_str().parse::<i32>().unwrap();
|
||||
}
|
||||
parts.push(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
let sum1 = parts.iter()
|
||||
.filter(|&part| check_part(&workflows, part))
|
||||
.fold(0, |acc, part| acc + part.iter().sum::<i32>());
|
||||
let sum2 = count_options(&workflows, "in", &[(1..4001), (1..4001), (1..4001), (1..4001)]);
|
||||
let elapsed = time_start.elapsed().as_micros();
|
||||
let elapsed_no_io = time_start_no_io.elapsed().as_micros();
|
||||
println!("Time: {}us", elapsed);
|
||||
println!("Time without file i/o: {}us", elapsed_no_io);
|
||||
println!("Sum1: {}", sum1);
|
||||
println!("Sum1: {}", sum2);
|
||||
}
|
Reference in New Issue
Block a user