[TASK] Solved Day 16
This commit is contained in:
212
src/day16.rs
Normal file
212
src/day16.rs
Normal 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));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user