adventofcode-2020/day-16/src/main.rs

225 lines
5.9 KiB
Rust

use std::collections::HashMap;
use std::fs;
use std::ops::RangeInclusive;
use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
type Rules = HashMap<String, (RangeInclusive<u32>, RangeInclusive<u32>)>;
type Ticket = Vec<u32>;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let input = fs::read_to_string("input")?;
let (rules, own_ticket, tickets) = parse_tickets(&input)?;
// Part 1
let invalid = get_invalid_values(&rules, &tickets);
println!("{}", invalid.iter().sum::<u32>());
// Part 2
let valid = get_valid_tickets(&rules, &tickets);
let order = get_value_order(&rules, &valid);
let result: u32 = order
.iter()
.zip(own_ticket)
.filter_map(|(name, value)| {
if name.starts_with("departure") {
Some(value)
} else {
None
}
})
.product();
println!("{}", result);
Ok(())
}
fn parse_tickets(input: &str) -> Result<(Rules, Ticket, Vec<Ticket>), String> {
let blocks: Vec<&str> = input.split("\n\n").collect();
let parse_u32 = |val: &str| {
val.parse::<u32>()
.map_err(|e| format!("Invalid number: {}", e))
};
if blocks.len() < 3 {
Err("Input incomplete")?;
}
let rules = blocks[0]
.lines()
.map(|line| {
lazy_static! {
static ref RULE_RE: Regex =
Regex::new(r"([[:alpha:]]+?): (\d+)-(\d+) or (\d+)-(\d+)")
.expect("Regex should compile");
}
let caps = RULE_RE
.captures(line)
.ok_or(format!("Invalid rule line: {}", line))?;
let name = caps[1].to_string();
let range1 = parse_u32(&caps[2])?..=parse_u32(&caps[3])?;
let range2 = parse_u32(&caps[4])?..=parse_u32(&caps[5])?;
Ok((name, (range1, range2)))
})
.collect::<Result<Rules, String>>()?;
let own_ticket = blocks[1]
.lines()
.skip(1)
.next()
.ok_or("Input incomplete")?
.split(',')
.map(|c| parse_u32(c))
.collect::<Result<Ticket, String>>()?;
let other_tickets = blocks[2]
.lines()
.skip(1)
.map(|line| line.split(',').map(|c| parse_u32(c)).collect())
.collect::<Result<Vec<Ticket>, String>>()?;
Ok((rules, own_ticket, other_tickets))
}
fn get_invalid_values(rules: &Rules, tickets: &Vec<Ticket>) -> Vec<u32> {
tickets
.iter()
.flat_map(|ticket| {
ticket.iter().filter(|value| {
!rules
.values()
.any(|(rule1, rule2)| rule1.contains(value) || rule2.contains(value))
})
})
.copied()
.collect()
}
fn get_valid_tickets(rules: &Rules, tickets: &Vec<Ticket>) -> Vec<Ticket> {
tickets
.iter()
.filter(|ticket| {
ticket.iter().all(|value| {
rules
.values()
.any(|(rule1, rule2)| rule1.contains(value) || rule2.contains(value))
})
})
.cloned()
.collect()
}
fn get_value_order(rules: &Rules, tickets: &Vec<Ticket>) -> Vec<String> {
let rules: Vec<(String, (RangeInclusive<u32>, RangeInclusive<u32>))> = rules
.iter()
.map(|tup| (tup.0.clone(), (tup.1 .0, tup.1 .1)))
.collect();
rules.sort_by(|name, (_, (rule1, rule2))| rule1.count() + rule2.count());
let mut next_ticket = 0;
let mut order: Vec<String> = rules.keys().cloned().collect();
let order_fits = |order: &Vec<String>, ticket: &Ticket| {
order.iter().zip(ticket).all(|(name, value)| {
let (rule1, rule2) = rules.get(name).expect("Must be there");
rule1.contains(value) || rule2.contains(value)
})
};
loop {
if order_fits(&order, &tickets[next_ticket]) {}
}
// rules
// .iter()
// .permutations(rules.len())
// .filter_map(|rules| {
// if tickets.iter().all(|ticket| {
// rules
// .iter()
// .zip(ticket)
// .all(|((_, (rule1, rule2)), value)| {
// rule1.contains(value) | rule2.contains(value)
// })
// }) {
// Some(
// rules
// .iter()
// .map(|rule| rule.0.clone())
// .collect::<Vec<String>>(),
// )
// } else {
// None
// }
// })
// .next()
// .expect("Must have a valid solution")
unimplemented!()
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn test_simple() -> Result<(), Box<dyn std::error::Error>> {
let input = indoc!(
"
class: 1-3 or 5-7
row: 6-11 or 33-44
seat: 13-40 or 45-50
your ticket:
7,1,14
nearby tickets:
7,3,47
40,4,50
55,2,20
38,6,12
"
);
let (rules, _, tickets) = parse_tickets(input)?;
let invalid = get_invalid_values(&rules, &tickets);
assert_eq!(invalid.iter().sum::<u32>(), 71);
Ok(())
}
#[test]
fn test_simple2() -> Result<(), Box<dyn std::error::Error>> {
let input = indoc!(
"
class: 0-1 or 4-19
row: 0-5 or 8-19
seat: 0-13 or 16-19
your ticket:
11,12,13
nearby tickets:
3,9,18
15,1,5
5,14,9
"
);
let (rules, _, tickets) = parse_tickets(input)?;
let valid = get_valid_tickets(&rules, &tickets);
let order = get_value_order(&rules, &valid);
assert_eq!(order, vec!["row", "class", "seat"]);
Ok(())
}
}