139 lines
3.3 KiB
Rust
139 lines
3.3 KiB
Rust
use std::cmp::Ordering;
|
|
use std::fmt::{Debug, Formatter};
|
|
|
|
use itertools::Itertools;
|
|
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::character::complete;
|
|
use nom::character::complete::newline;
|
|
use nom::multi::{many1, separated_list0, separated_list1};
|
|
use nom::sequence::delimited;
|
|
use nom::{IResult, Parser};
|
|
|
|
pub fn process_part_1(input: &str) -> usize {
|
|
let packets = parse_input(input).unwrap().1;
|
|
|
|
packets
|
|
.iter()
|
|
.tuples::<(_, _)>()
|
|
.enumerate()
|
|
.filter_map(|(i, (a, b))| match a.cmp(b) {
|
|
Ordering::Less => Some(i + 1),
|
|
Ordering::Equal => panic!("oh no"),
|
|
_ => None,
|
|
})
|
|
.sum()
|
|
}
|
|
|
|
pub fn process_part_2(input: &str) -> usize {
|
|
let packets = parse_input(input).unwrap().1;
|
|
let div_2 = Packet::List(vec![Packet::List(vec![Packet::Value(2)])]);
|
|
let div_6 = Packet::List(vec![Packet::List(vec![Packet::Value(6)])]);
|
|
|
|
packets
|
|
.iter()
|
|
.chain([&div_2, &div_6])
|
|
.sorted()
|
|
.enumerate()
|
|
.filter(|&(_i, item)| item == &div_2 || item == &div_6)
|
|
.map(|(i, _)| i + 1)
|
|
.product()
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
enum Packet {
|
|
List(Vec<Packet>),
|
|
Value(u32),
|
|
}
|
|
|
|
impl Debug for Packet {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Packet::List(v) => write!(f, "{v:?}"),
|
|
Packet::Value(v) => write!(f, "{v}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Eq for Packet {}
|
|
|
|
impl PartialEq<Self> for Packet {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
match (self, other) {
|
|
(Packet::Value(a), Packet::Value(b)) => a == b,
|
|
(Packet::List(a), Packet::List(b)) => a == b,
|
|
(Packet::Value(a), Packet::List(b)) => &vec![Packet::Value(*a)] == b,
|
|
(Packet::List(a), Packet::Value(b)) => a == &vec![Packet::Value(*b)],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd<Self> for Packet {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for Packet {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
match (self, other) {
|
|
(Packet::Value(a), Packet::Value(b)) => a.cmp(b),
|
|
(Packet::List(a), Packet::List(b)) => a.cmp(b),
|
|
(Packet::Value(a), Packet::List(b)) => vec![Packet::Value(*a)].cmp(b),
|
|
(Packet::List(a), Packet::Value(b)) => a.cmp(&vec![Packet::Value(*b)]),
|
|
}
|
|
}
|
|
}
|
|
|
|
// I originally parsed tuples but for part 2 I needed the full list without tuples.
|
|
// so lets just parse without. We'll just tuple-ify the iterator afterwards.
|
|
fn parse_input(input: &str) -> IResult<&str, Vec<Packet>> {
|
|
separated_list1(many1(newline), parse_packet)(input)
|
|
}
|
|
|
|
fn parse_packet(input: &str) -> IResult<&str, Packet> {
|
|
alt((
|
|
complete::u32.map(Packet::Value),
|
|
delimited(tag("["), separated_list0(tag(","), parse_packet), tag("]")).map(Packet::List),
|
|
))(input)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const INPUT: &str = "[1,1,3,1,1]
|
|
[1,1,5,1,1]
|
|
|
|
[[1],[2,3,4]]
|
|
[[1],4]
|
|
|
|
[9]
|
|
[[8,7,6]]
|
|
|
|
[[4,4],4,4]
|
|
[[4,4],4,4,4]
|
|
|
|
[7,7,7,7]
|
|
[7,7,7]
|
|
|
|
[]
|
|
[3]
|
|
|
|
[[[]]]
|
|
[[]]
|
|
|
|
[1,[2,[3,[4,[5,6,7]]]],8,9]
|
|
[1,[2,[3,[4,[5,6,0]]]],8,9]";
|
|
|
|
#[test]
|
|
fn day1() {
|
|
assert_eq!(process_part_1(INPUT), 13);
|
|
}
|
|
|
|
#[test]
|
|
fn day2() {
|
|
assert_eq!(process_part_2(INPUT), 140);
|
|
}
|
|
}
|