use nom::{IResult, multi::{many1, separated_list0}, sequence::{separated_pair, terminated}, character::complete::{self, multispace1, multispace0}, bytes::complete::tag, branch::alt, Parser}; #[derive(Debug, Clone)] enum Item { NestedList(List), Num(u8), } use Item::*; type List = Vec; fn parse_item(input: &str) -> IResult<&str, Item> { let (input, item) = alt(( complete::u8.map(|n| Num(n)), parse_list.map(|l| NestedList(l)) ))(input)?; Ok((input, item)) } fn parse_list(input: &str) -> IResult<&str, List> { let (input, _) = tag("[")(input)?; let (input, list) = separated_list0(tag(","), parse_item)(input)?; let (input, _) = tag("]")(input)?; Ok((input, list)) } fn parse_input(input: &str) -> IResult<&str, Vec<(List, List)>> { let (input, result) = many1( terminated( separated_pair( parse_list, multispace1, parse_list ), multispace0 ) )(input)?; Ok((input, result)) } fn print_list(list: &List) { print!("["); for item in list { match item { NestedList(l) => print_list(l), Num(n) => print!("{n}"), } print!(",") } print!("]") } fn print_list_pair(lists: &(List, List)) { print_list(&lists.0); println!(); print_list(&lists.1); println!(); println!(); } fn check_lists(lists: &(List, List)) -> Option { let mut l1 = lists.0.iter(); let mut l2 = lists.1.iter(); loop { match (l1.next(), l2.next()) { (None, Some(_)) => return Some(true), (Some(_), None) => return Some(false), (None, None) => return None, (Some(Num(n)), Some(Num(m))) => if n < m { return Some(true); } else if n > m { return Some(false); } else { continue; }, (Some(NestedList(left)), Some(NestedList(right))) => match check_lists(&(left.to_vec(), right.to_vec())) { None => continue, result => return result, }, (Some(NestedList(left)), Some(Num(m))) => match check_lists(&(left.to_vec(), vec![Num(*m)])) { None => continue, result => return result, }, (Some(Num(n)), Some(NestedList(right))) => match check_lists(&(vec![Num(*n)], right.to_vec())) { None => continue, result => return result, }, } } } fn main() { let input_text = include_str!("../input.txt"); let (_rest, list_pairs) = parse_input(input_text).unwrap(); // Checking the parsing (which worked first try btw like wtf) // for list_pair in &lists { // print_list_pair(list_pair) // } let mut index = 0; let mut result = 0; for list_pair in &list_pairs { index += 1; match check_lists(list_pair) { Some(true) => result += index, Some(false) => continue, None => unreachable!(), } } println!("Sum of indices is: {result}"); let first_divider_packet = vec![NestedList(vec![Num(2)])]; let second_divider_packet = vec![NestedList(vec![Num(6)])]; let mut list = Vec::new(); for list_pair in list_pairs { list.push(list_pair.0); list.push(list_pair.1); } list.push(first_divider_packet.clone()); list.push(second_divider_packet.clone()); list.sort_by(|l, r| match check_lists(&(l.to_vec(), r.to_vec())) { None => std::cmp::Ordering::Equal, Some(true) => std::cmp::Ordering::Less, Some(false) => std::cmp::Ordering::Greater, }); // Checking the sorting (which works :D) // for l in &list { // print_list(l); // println!(); // } let mut first_index = 0; let mut second_index = 0; index = 1; for l in & list { if check_lists(&(l.to_vec(), first_divider_packet.clone())) == None { first_index = index; } else if check_lists(&(l.to_vec(), second_divider_packet.clone())) == None { second_index = index; } if first_index != 0 && second_index != 0 { break; } index += 1; } println!("Divider packets' indices multiplied: {}", first_index*second_index); }