[TASK] Solved Day 16

This commit is contained in:
2020-12-17 00:02:19 +01:00
parent b118845a5e
commit 040965a034
5 changed files with 500 additions and 1 deletions

212
src/day16.rs Normal file
View File

@@ -0,0 +1,212 @@
use super::util;
use std::cmp::{min, max};
pub fn solve() {
let lines = util::read_file("input/day16.txt");
let range_sets = lines.iter().take_while(|s| !s.is_empty()).map(|s| ValidRangeSet::parse(s)).collect::<Vec<_>>();
let your_ticket = lines.iter().skip_while(|s| s != &"your ticket:").skip(1).next().unwrap()
.split(",").map(|s| s.parse::<u32>().unwrap()).collect::<Vec<u32>>();
let nearby_tickets = lines.iter().skip_while(|s| s != &"nearby tickets:").skip(1)
.map(|t| t.split(",").map(|s| s.parse::<u32>().unwrap()).collect::<Vec<_>>()).collect::<Vec<_>>();
let combined_ranges = ValidRange::combine(&range_sets.iter().flat_map(|r| &r.ranges).collect::<Vec<_>>());
let invalids_per_ticket = nearby_tickets.iter()
.map(|t| find_invalid(t, &combined_ranges))
.collect::<Vec<_>>();
// println!("{:?}", combined_ranges);
let part1: u32 = invalids_per_ticket.iter().map(|t| -> u32 { t.iter().sum() }).sum();
println!("Day X Part 1: {}", part1);
let mut valid_tickets = Vec::new();
for i in 0..nearby_tickets.len() {
if invalids_per_ticket[i].is_empty() {
valid_tickets.push(&nearby_tickets[i]);
}
}
let ordered_range_sets = resolve_column_ordering(&valid_tickets, &range_sets);
// println!("{:?}", ordered_range_sets);
let fields = ordered_range_sets.iter().map(|r| &r.name).filter(|n| n.starts_with("departure")).collect::<Vec<&String>>();
let part2 =
get_ticket_values(&your_ticket, &fields, &ordered_range_sets)
.iter()
.fold(1u64, |a, b| (a as u64) * (*b as u64));
println!("Day X Part 2: {}", part2);
}
fn find_invalid(nums: &Vec<u32>, combined_ranges: &Vec<ValidRange>) -> Vec<u32> {
return nums.iter().filter(|n| not_in_ranges(n, combined_ranges)).map(|n| n.clone().clone()).collect::<Vec<u32>>()
}
fn not_in_ranges(n: &u32, ranges: &Vec<ValidRange>) -> bool {
let closest_range_idx_res = ranges.binary_search_by_key(n, |r| r.min);
let closest_range_idx = if closest_range_idx_res.is_err() {
closest_range_idx_res.unwrap_err()
} else {
// If we're matching a range, the index will be the same as the range, but if we're not matching, it will be one higher
closest_range_idx_res.unwrap() + 1
};
let optimized_res = closest_range_idx == 0 || !ranges[closest_range_idx - 1].includes(n);
return optimized_res;
}
fn resolve_column_ordering(tickets: &Vec<&Vec<u32>>, range_sets: &Vec<ValidRangeSet>) -> Vec<ValidRangeSet> {
let mut available_order_per_range_set = Vec::new();
for i in 0..range_sets.len() {
available_order_per_range_set.push(vec!{ true; range_sets.len() });
}
let mut solved_range_sets = vec!{ false; range_sets.len() };
for ticket in tickets {
for range_set_idx in 0..available_order_per_range_set.len() {
if solved_range_sets[range_set_idx] {
continue;
}
let range_set = &range_sets[range_set_idx];
let available_orders = &mut available_order_per_range_set[range_set_idx];
let mut removed_options = false;
for j in 0..available_orders.len() {
if available_orders[j] && !range_set.matches(&ticket[j]) {
available_orders[j] = false;
removed_options = true;
}
}
if removed_options {
check_options_left(range_set_idx, &mut solved_range_sets, &mut available_order_per_range_set);
}
}
}
let order_per_range_set = available_order_per_range_set.iter()
.inspect(|a| if a.iter().filter(|b| **b).count() > 1 { panic!(format!("Inconclusive in terms of ordering"))} )
.map(|a| a.iter().position(|b| *b).unwrap())
.collect::<Vec<usize>>();
// Sort the range sets so that they're in their available order
let mut res = vec!{ ValidRangeSet{ name: String::from("temp"), ranges: Vec::new() }; range_sets.len()};
for i in 0..range_sets.len() {
res[order_per_range_set[i]] = range_sets[i].clone();
}
return res;
}
fn check_options_left(orig_range_set_idx: usize, solved_range_sets: &mut Vec<bool>, available_order_per_range_set: &mut Vec<Vec<bool>>) {
let options_left = available_order_per_range_set[orig_range_set_idx].iter().filter(|o| **o).count();
if options_left == 1 {
// We now know for sure where range_set i is supposed to be, so we can cross
// that option out for all other range sets:
solved_range_sets[orig_range_set_idx] = true;
let known_order = available_order_per_range_set[orig_range_set_idx].iter().position(|o|*o).unwrap();
// println!("Original range set position '{}' must be position: {}", orig_range_set_idx, known_order);
for j in 0..available_order_per_range_set.len() {
if j == orig_range_set_idx { continue; }
if available_order_per_range_set[j][known_order] {
available_order_per_range_set[j][known_order] = false;
check_options_left(j, solved_range_sets, available_order_per_range_set);
}
}
} else if options_left == 0 {
panic!(format!("Couldn't find any valid ordering for range set {}", orig_range_set_idx));
}
}
fn get_ticket_values(ticket: &Vec<u32>, fields: &Vec<&String>, ordered_range_sets: &Vec<ValidRangeSet>) -> Vec<u32> {
let mut res = Vec::new();
for field in fields {
let idx = ordered_range_sets.iter().position(|r| &&r.name == field).unwrap();
res.push(ticket[idx]);
}
return res;
}
#[derive(Debug, Copy, Clone)]
struct ValidRange {
min: u32,
max: u32
}
impl ValidRange {
fn parse(input: &str) -> ValidRange {
let values = input.split("-").map(|s| s.trim()).collect::<Vec<_>>();
return ValidRange {
min: values[0].parse::<u32>().unwrap(),
max: values[1].parse::<u32>().unwrap(),
}
}
fn includes(&self, n: &u32) -> bool {
return &self.min <= n && &self.max >= n;
}
fn combine(ranges: &Vec<&ValidRange>) -> Vec<ValidRange> {
let mut res = Vec::new();
for range in ranges {
let overlaps = res.iter().enumerate().filter(|(_, r)| range.overlaps(r))
.collect::<Vec<_>>();
let indices_to_remove = overlaps.iter().map(|(i, _)| i.clone()).rev().collect::<Vec<_>>();
if overlaps.len() == 0 {
res.push(ValidRange { min: range.min, max: range.max });
} else {
let combined_min = min(range.min, overlaps.iter().map(|(_, r)| r.min).min().unwrap());
let combined_max = max(range.max, overlaps.iter().map(|(_, r)| r.max).max().unwrap());
res.push(ValidRange { min: combined_min, max: combined_max })
}
for i in indices_to_remove {
res.remove(i);
}
}
res.sort_by_key(|r| r.min);
return res;
}
fn overlaps(&self, other: &ValidRange) -> bool {
return self.min <= other.max && self.max >= other.min;
}
}
#[derive(Debug, Clone)]
struct ValidRangeSet {
name: String,
ranges: Vec<ValidRange>,
}
impl ValidRangeSet {
fn parse(input: &String) -> ValidRangeSet {
let spl = input.split(": ").collect::<Vec<_>>();
return ValidRangeSet {
name: String::from(spl[0]),
ranges: spl[1].split(" or ").map(|r| ValidRange::parse(r)).collect::<Vec<_>>()
}
}
fn matches(&self, n: &u32) -> bool {
return self.ranges.iter().any(|r| r.includes(n));
}
}

View File

@@ -17,8 +17,9 @@ mod day12;
mod day13;
mod day14;
mod day15;
mod day16;
const MAX_DAY: u8 = 15;
const MAX_DAY: u8 = 16;
const BENCHMARK_AMOUNT: u32 = 100;
fn solve(day: u8) {
@@ -38,6 +39,7 @@ fn solve(day: u8) {
13 => day13::solve(),
14 => day14::solve(),
15 => day15::solve(),
16 => day16::solve(),
_ => println!("This day is not yet implemented")
}
}