Files
advent-of-code-2022-rust/src/day13.rs
2022-12-13 22:17:25 +01:00

145 lines
4.7 KiB
Rust

use std::cmp::Ordering;
use crate::day_solver::DaySolver;
use super::util;
pub struct Day13 {
packet_pairs: Vec<(Packet, Packet)>
}
impl Day13 {
pub fn create() -> Self {
// let lines = util::read_file("input/day13_example.txt");
let lines = util::read_file("input/day13.txt");
let mut line_iter = lines.iter();
let mut packet_pairs = Vec::new();
while let Some(line) = line_iter.next() {
if line.is_empty() {
continue
}
let left = Packet::parse(line);
let right = Packet::parse(line_iter.next().unwrap());
// println!("left: {:?}, right: {:?}", left, right);
packet_pairs.push((left, right));
}
// println!("{:?}", packet_pairs);
// Put the input into the day struct
return Day13 {
packet_pairs
}
}
fn compare(left: &Packet, right: &Packet) -> Ordering {
if let Packet::Num(n_left) = left {
if let Packet::Num(n_right) = right {
if n_left != n_right {
return if n_left < n_right { Ordering::Less } else { Ordering::Greater };
}
} else {
// The right value is a vec-packet, so we need to act like the left value is as well:
return Self::compare(&Packet::Vec(vec![Packet::Num(n_left.to_owned())]), right);
}
} else if let Packet::Vec(vec_left) = left {
// Left is a vec-packet
if let Packet::Vec(vec_right) = right {
for i in 0..vec_left.len().min(vec_right.len()) {
let comp = Self::compare(&vec_left[i], &vec_right[i]);
if comp != Ordering::Equal {
return comp
}
}
if vec_left.len() != vec_right.len() {
return if vec_left.len() < vec_right.len() { Ordering::Less } else { Ordering::Greater };
}
} else if let Packet::Num(n_right) = right {
// The left value is a vec-packet, so we need to act like the right value is as well:
return Self::compare(left, &Packet::Vec(vec![Packet::Num(n_right.to_owned())]));
}
}
return Ordering::Equal;
}
}
impl DaySolver for Day13 {
fn solve_part1(&mut self) -> String {
self.packet_pairs.iter().enumerate()
.filter_map(|(i, p)| {
let comp = Day13::compare(&p.0, &p.1);
// println!("{:?} vs {:?} is {:?}", p.0, p.1, comp);
if comp == Ordering::Less { Some(i + 1) } else { None }
})
.sum::<usize>().to_string()
}
fn solve_part2(&mut self) -> String {
let mut full_vec: Vec<Packet> = self.packet_pairs.iter()
.flat_map(|p| vec![p.0.to_owned(), p.1.to_owned()].into_iter())
.collect();
let div_packet1 = Packet::parse("[[2]]");
full_vec.push(div_packet1.to_owned());
let div_packet2 = Packet::parse("[[6]]");
full_vec.push(div_packet2.to_owned());
full_vec.sort_by(|l, r| Day13::compare(l, r));
full_vec.iter().enumerate()
.filter_map(|(i, p)| if p == &div_packet1 || p == &div_packet2 { Some(i + 1) } else { None })
.reduce(|a, b| a * b)
.unwrap()
.to_string()
}
}
#[derive(Debug, Clone, PartialEq)]
enum Packet {
Num(u8),
Vec(Vec<Packet>)
}
impl Packet {
fn parse(str: &str) -> Packet {
let (p, _) = Self::parse_internal(str);
p
}
fn parse_internal(str: &str) -> (Packet, usize) {
if str.chars().next() == Some('[') {
if str.starts_with("[]") {
// We're dealing with an empty vec:
(Packet::Vec(Vec::new()), 2)
} else {
// We're dealing with a vec
let mut idx = 0;
let mut vec_content = Vec::new();
loop {
idx += 1;
let (p, i) = Packet::parse_internal(&str[idx..]);
vec_content.push(p);
idx += i;
if str.chars().nth(idx) != Some(',') {
break;
}
};
assert_eq!(str.chars().nth(idx), Some(']'), "Vec not ending: {} at idx {}", str, idx);
(Packet::Vec(vec_content), idx + 1)
}
} else {
// We should be dealing with a number
let n_str = str.chars().take_while(|c| c.is_numeric()).collect::<String>();
let n = n_str.parse::<u8>().expect(format!("Vec is not a number: {}", str).as_str());
(Packet::Num(n), n_str.len())
}
}
}