#![feature(iter_array_chunks)]
/// --- Day 13: Distress Signal ---
///
/// You climb the hill and again try contacting the Elves. However, you instead receive a signal
/// you weren't expecting: a distress signal.
///
/// Your handheld device must still not be working properly; the packets from the distress signal
/// got decoded out of order. You'll need to re-order the list of received packets (your puzzle
/// input) to decode the message.
///
/// Your list consists of pairs of packets; pairs are separated by a blank line. You need to
/// identify how many pairs of packets are in the right order.
///
/// For example:
///
/// ```
/// [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]
/// ```
///
/// Packet data consists of lists and integers. Each list starts with [, ends with ], and contains
/// zero or more comma-separated values (either integers or other lists). Each packet is always a
/// list and appears on its own line.
///
/// When comparing two values, the first value is called left and the second value is called right.
/// Then:
///
/// If both values are integers, the lower integer should come first. If the left integer is
/// lower than the right integer, the inputs are in the right order. If the left integer is
/// higher than the right integer, the inputs are not in the right order. Otherwise, the
/// inputs are the same integer; continue checking the next part of the input.
/// If both values are lists, compare the first value of each list, then the second value, and
/// so on. If the left list runs out of items first, the inputs are in the right order. If
/// the right list runs out of items first, the inputs are not in the right order. If the
/// lists are the same length and no comparison makes a decision about the order, continue
/// checking the next part of the input.
/// If exactly one value is an integer, convert the integer to a list which contains that
/// integer as its only value, then retry the comparison. For example, if comparing [0,0,0]
/// and 2, convert the right value to [2] (a list containing 2); the result is then found by
/// instead comparing [0,0,0] and [2].
///
/// Using these rules, you can determine which of the pairs in the example are in the right order:
///
/// ```
/// == Pair 1 ==
/// - Compare [1,1,3,1,1] vs [1,1,5,1,1]
/// - Compare 1 vs 1
/// - Compare 1 vs 1
/// - Compare 3 vs 5
/// - Left side is smaller, so inputs are in the right order
///
/// == Pair 2 ==
/// - Compare [[1],[2,3,4]] vs [[1],4]
/// - Compare [1] vs [1]
/// - Compare 1 vs 1
/// - Compare [2,3,4] vs 4
/// - Mixed types; convert right to [4] and retry comparison
/// - Compare [2,3,4] vs [4]
/// - Compare 2 vs 4
/// - Left side is smaller, so inputs are in the right order
///
/// == Pair 3 ==
/// - Compare [9] vs [[8,7,6]]
/// - Compare 9 vs [8,7,6]
/// - Mixed types; convert left to [9] and retry comparison
/// - Compare [9] vs [8,7,6]
/// - Compare 9 vs 8
/// - Right side is smaller, so inputs are not in the right order
///
/// == Pair 4 ==
/// - Compare [[4,4],4,4] vs [[4,4],4,4,4]
/// - Compare [4,4] vs [4,4]
/// - Compare 4 vs 4
/// - Compare 4 vs 4
/// - Compare 4 vs 4
/// - Compare 4 vs 4
/// - Left side ran out of items, so inputs are in the right order
///
/// == Pair 5 ==
/// - Compare [7,7,7,7] vs [7,7,7]
/// - Compare 7 vs 7
/// - Compare 7 vs 7
/// - Compare 7 vs 7
/// - Right side ran out of items, so inputs are not in the right order
///
/// == Pair 6 ==
/// - Compare [] vs [3]
/// - Left side ran out of items, so inputs are in the right order
///
/// == Pair 7 ==
/// - Compare [[[]]] vs [[]]
/// - Compare [[]] vs []
/// - Right side ran out of items, so inputs are not in the right order
///
/// == Pair 8 ==
/// - Compare [1,[2,[3,[4,[5,6,7]]]],8,9] vs [1,[2,[3,[4,[5,6,0]]]],8,9]
/// - Compare 1 vs 1
/// - Compare [2,[3,[4,[5,6,7]]]] vs [2,[3,[4,[5,6,0]]]]
/// - Compare 2 vs 2
/// - Compare [3,[4,[5,6,7]]] vs [3,[4,[5,6,0]]]
/// - Compare 3 vs 3
/// - Compare [4,[5,6,7]] vs [4,[5,6,0]]
/// - Compare 4 vs 4
/// - Compare [5,6,7] vs [5,6,0]
/// - Compare 5 vs 5
/// - Compare 6 vs 6
/// - Compare 7 vs 0
/// - Right side is smaller, so inputs are not in the right order
/// ```
///
/// What are the indices of the pairs that are already in the right order? (The first pair has
/// index 1, the second pair has index 2, and so on.) In the above example, the pairs in the right
/// order are 1, 2, 4, and 6; the sum of these indices is 13.
///
/// Determine which pairs of packets are already in the right order. What is the sum of the
/// indices of those pairs?
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::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)]
enum ListEntry {
Value(Option),
List(Vec),
}
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 args = Cli::parse();
let file = File::open(&args.file).unwrap();
let reader = BufReader::new(file);
let res: i32 = reader
.lines()
.filter_map(|line| {
let s = line.unwrap();
if s.is_empty() {
return None;
}
Some(parse_list(s.as_str()).unwrap().1)
})
.array_chunks::<2>()
.zip(1..)
.filter_map(|(chunk, idx)| {
if compare(&chunk[0], &chunk[1]).unwrap() {
Some(idx)
} else {
None
}
})
.sum();
println!("{res}");
}