This commit is contained in:
Acvaxoort
2023-12-20 20:11:35 +01:00
parent 867a987508
commit 73f6305410
5 changed files with 1064 additions and 0 deletions

187
day19/src/main.rs Normal file
View 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);
}