diff options
author | Irene Knapp <ireneista@gmail.com> | 2020-12-15 22:07:54 -0800 |
---|---|---|
committer | Irene Knapp <ireneista@gmail.com> | 2020-12-15 22:07:54 -0800 |
commit | eaac11df01dea60def680f61861fe7b728f03df1 (patch) | |
tree | 5a6f8db5d45999e08957c6f481c7c83fb7c34209 /16/src | |
parent | 7357cbdbd4424a06db8fb11546626f97783135a6 (diff) |
16
Diffstat (limited to '16/src')
-rw-r--r-- | 16/src/main.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/16/src/main.rs b/16/src/main.rs new file mode 100644 index 0000000..10c5877 --- /dev/null +++ b/16/src/main.rs @@ -0,0 +1,185 @@ +use advent_lib::prelude::*; + +use std::collections::BTreeMap; +use std::collections::BTreeSet; + +#[derive(Clone,Debug)] +struct Range { + min: i64, + max: i64, +} + + +fn main() -> Result<()> { + let mut args = std::env::args(); + if args.len() != 2 { + eprintln!("Usage: advent input"); + } + let _ = args.next(); + let filename = args.next().unwrap(); + + let input = advent_lib::read_lines_file(&filename)?; + let input = advent_lib::group_lines_by_blanks(input); + + let mut rules: BTreeMap<String,Vec<Range>> = BTreeMap::new(); + + for line in &input[0] { + let colon_point = line.find(':').unwrap(); + let (field_name, rest) = line.split_at(colon_point); + let (_, rest) = rest.split_at(2); + + let mut ranges: Vec<Range> = Vec::new(); + for word in rest.split(' ') { + if word == "or" { + continue; + } + + let parts: Vec<&str> = word.split('-').collect(); + ranges.push(Range { + min: parts[0].parse::<i64>().unwrap(), + max: parts[1].parse::<i64>().unwrap(), + }); + + } + rules.insert(field_name.to_string(), ranges); + } + + let player_ticket = parse_comma_separated_ints(&input[1][1]); + + let mut nearby_tickets = Vec::new(); + for line in input[2].iter().skip(1) { + nearby_tickets.push(parse_comma_separated_ints(&line)); + } + + let mut valid_tickets = Vec::new(); + valid_tickets.push(player_ticket.clone()); + + let mut error_sum = 0; + for ticket in &nearby_tickets { + let mut is_valid = true; + for value in ticket { + let mut found_match = false; + for rule in rules.values() { + if value_obeys_rule(*value, rule) { + found_match = true; + break; + } + } + + if !found_match { + error_sum += value; + is_valid = false; + } + } + + if is_valid { + valid_tickets.push(ticket.clone()); + } + } + + println!("{}", error_sum); + + let mut possibilities: Vec<BTreeSet<String>> = Vec::new(); + for _ in 0 .. player_ticket.len() { + let mut all_fields = BTreeSet::new(); + for field_name in rules.keys() { + all_fields.insert(field_name.clone()); + } + possibilities.push(all_fields); + } + + for ticket in valid_tickets { + //println!("{:?}", possibilities); + for i in 0 .. ticket.len() { + let mut fields_to_remove = Vec::new(); + + for field_name in possibilities[i].iter() { + let rule = rules.get(field_name).unwrap(); + if !value_obeys_rule(ticket[i], rule) { + /* + println!("{}: value {} does not obey rule for {}", + i, ticket[i], field_name); + */ + fields_to_remove.push(field_name.clone()); + } + } + + for field_name in fields_to_remove { + possibilities[i].remove(&field_name); + } + } + } + + //println!("{:?}", possibilities); + + let mut finalized_fields = BTreeSet::new(); + loop { + let mut removed_any = false; + + for i in 0 .. player_ticket.len() { + let mut unique_field = None; + if possibilities[i].len() == 1 { + let field_name = possibilities[i].iter().next().unwrap().clone(); + if finalized_fields.contains(&field_name) { + continue; + } + unique_field = Some((i, field_name)); + } + + match unique_field { + Some((i_to_skip, field_name)) => { + removed_any = true; + + for j in 0 .. possibilities.len() { + if j == i_to_skip { + continue; + } + + possibilities[j].remove(&field_name); + } + + finalized_fields.insert(field_name); + }, + None => { }, + } + } + + if !removed_any { + break; + } + } + + let mut product = 1; + for i in 0 .. possibilities.len() { + let field_name = possibilities[i].iter().next().unwrap(); + if field_name.starts_with("departure ") { + product *= player_ticket[i]; + } + } + + println!("{}", product); + + Ok(()) +} + + +fn parse_comma_separated_ints(line: &str) -> Vec<i64> { + let mut result = Vec::new(); + + for word in line.split(',') { + result.push(word.parse::<i64>().unwrap()); + } + + result +} + + +fn value_obeys_rule(value: i64, rule: &Vec<Range>) -> bool { + for range in rule { + if value >= range.min && value <= range.max { + return true; + } + } + false +} + |