#![feature(iter_array_chunks)]
/// --- Part Two ---
///
/// Now, you just need to put all of the packets in the right order. Disregard the blank lines in
/// your list of received packets.
///
/// The distress signal protocol also requires that you include two additional divider packets:
///
/// [[2]]
/// [[6]]
///
/// Using the same rules as before, organize all packets - the ones in your list of received
/// packets as well as the two divider packets - into the correct order.
///
/// For the example above, the result of putting the packets in the correct order is:
///
/// ```
/// []
/// [[]]
/// [[[]]]
/// [1,1,3,1,1]
/// [1,1,5,1,1]
/// [[1],[2,3,4]]
/// [1,[2,[3,[4,[5,6,0]]]],8,9]
/// [1,[2,[3,[4,[5,6,7]]]],8,9]
/// [[1],4]
/// [[2]]
/// [3]
/// [[4,4],4,4]
/// [[4,4],4,4,4]
/// [[6]]
/// [7,7,7]
/// [7,7,7,7]
/// [[8,7,6]]
/// [9]
/// ```
///
/// Afterward, locate the divider packets. To find the decoder key for this distress signal, you
/// need to determine the indices of the two divider packets and multiply them together. (The
/// first packet is at index 1, the second packet is at index 2, and so on.) In this example, the
/// divider packets are 10th and 14th, and so the decoder key is 140.
///
/// Organize all of the packets into the correct order. What is the decoder key for the distress
/// signal?
use clap::Parser;
use itertools::Itertools;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::i8;
use nom::combinator::{map, opt};
use nom::error::{ContextError, ErrorKind as NomErrorKind, ParseError};
use nom::multi::many1;
use nom::sequence::{delimited, terminated};
use nom::IResult;
use std::cmp::Ordering;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;
const FILEPATH: &'static str = "examples/input.txt";
pub type Input<'a> = &'a str;
pub type Result<'a, T> = IResult, T, Error>>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ErrorKind {
Nom(NomErrorKind),
Context(&'static str),
Custom(String),
}
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long, default_value = FILEPATH)]
file: PathBuf,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
pub errors: Vec<(I, ErrorKind)>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum ListEntry {
Value(Option),
List(Vec),
}
impl Ord for ListEntry {
fn cmp(&self, other: &Self) -> Ordering {
let res = compare(&self, other);
match res {
Some(x) => match x {
true => Ordering::Less,
false => Ordering::Greater,
},
None => Ordering::Equal,
}
}
}
impl PartialOrd for ListEntry {
fn partial_cmp(&self, other: &Self) -> Option {
Some(self.cmp(other))
}
}
impl ParseError for Error {
fn from_error_kind(input: I, kind: NomErrorKind) -> Self {
let errors = vec![(input, ErrorKind::Nom(kind))];
Self { errors }
}
fn append(input: I, kind: NomErrorKind, mut other: Self) -> Self {
other.errors.push((input, ErrorKind::Nom(kind)));
other
}
}
impl ContextError for Error {
fn add_context(input: I, ctx: &'static str, mut other: Self) -> Self {
other.errors.push((input, ErrorKind::Context(ctx)));
other
}
}
fn parse_list(input: &str) -> Result {
alt((
map(i8, |v| ListEntry::Value(Some(v))),
map(tag("[]"), |_| ListEntry::Value(None)),
delimited(
tag("["),
map(many1(terminated(parse_list, opt(tag(",")))), |list| {
ListEntry::List(list)
}),
tag("]"),
),
))(input)
}
fn compare(lhs: &ListEntry, rhs: &ListEntry) -> Option {
use ListEntry::*;
match (lhs, rhs) {
(Value(lv), Value(rv)) => {
if lv == rv {
None
} else {
Some(lv < rv)
}
}
(Value(None), List(_)) => Some(true),
(Value(_), List(_)) => compare(&ListEntry::List(vec![lhs.clone()]), rhs),
(List(_), Value(None)) => Some(false),
(List(_), Value(_)) => compare(lhs, &ListEntry::List(vec![rhs.clone()])),
(List(lv), List(rv)) => lv
.iter()
.zip_longest(rv.iter())
.find_map(|pair| match pair {
itertools::EitherOrBoth::Both(l, r) => compare(l, r),
itertools::EitherOrBoth::Right(_) => Some(true),
itertools::EitherOrBoth::Left(_) => Some(false),
}),
}
}
fn main() {
let divider_packets: [ListEntry; 2] = [
ListEntry::List(vec![ListEntry::Value(Some(2))]),
ListEntry::List(vec![ListEntry::Value(Some(6))]),
];
let args = Cli::parse();
let file = File::open(&args.file).unwrap();
let reader = BufReader::new(file);
let res: usize = reader
.lines()
.filter_map(|line| {
let s = line.unwrap();
if s.is_empty() {
return None;
}
Some(parse_list(s.as_str()).unwrap().1)
})
.chain(divider_packets.clone().into_iter())
.sorted()
.zip(1..)
.filter(|(x, _)| x == ÷r_packets[0] || x == ÷r_packets[1])
.map(|(_, idx)| idx)
.take(2)
.product();
println!("{res}");
}