Day 8 - Part 2

Signed-off-by: Anthony Oteri <anthony.oteri@gmail.com>
This commit is contained in:
Anthony Oteri
2023-12-08 16:52:53 -05:00
parent 7301247fa5
commit 1a9d55c3b3
2 changed files with 158 additions and 3 deletions
+148 -3
View File
@@ -1,18 +1,163 @@
use std::collections::BTreeMap;
use nom::{
branch::alt,
character::complete::{self, alphanumeric1, line_ending, multispace0, newline, one_of},
combinator::eof,
multi::many1,
sequence::{delimited, preceded, separated_pair, terminated},
IResult,
};
use crate::error::AocError;
#[derive(Debug, Clone, Eq, PartialEq)]
struct Entry {
left: String,
right: String,
}
fn parse_instructions(input: &str) -> IResult<&str, Vec<char>> {
let (input, instructions) = terminated(many1(one_of("LR")), newline)(input)?;
Ok((input, instructions))
}
fn parse_entry(input: &str) -> IResult<&str, (String, Entry)> {
let (input, node) = alphanumeric1(input)?;
let (input, _) = terminated(preceded(multispace0, complete::char('=')), multispace0)(input)?;
let (input, (left, right)) = delimited(
complete::char('('),
separated_pair(
alphanumeric1,
preceded(multispace0, complete::char(',')),
preceded(multispace0, alphanumeric1),
),
complete::char(')'),
)(input)?;
Ok((
input,
(
node.to_string(),
Entry {
left: left.to_string(),
right: right.to_string(),
},
),
))
}
fn parse(input: &str) -> IResult<&str, (Vec<char>, BTreeMap<String, Entry>)> {
let (input, instructions) = parse_instructions(input)?;
let (input, _) = newline(input)?;
let (input, entries) = many1(terminated(parse_entry, alt((line_ending, eof))))(input)?;
debug_assert!(dbg!(&input).is_empty());
Ok((
input,
(
instructions,
entries.into_iter().collect::<BTreeMap<String, Entry>>(),
),
))
}
#[tracing::instrument]
pub fn process(input: &str) -> miette::Result<u64, AocError> {
Ok(0)
let (_, (instructions, entries)) = parse(input).unwrap();
let current = entries
.keys()
.filter(|k| k.ends_with('Z'))
.cloned()
.collect::<Vec<_>>();
let _count = 0;
let results = current
.iter()
.map(|node| {
let mut visited = vec![node.clone()];
let mut current = node.clone();
instructions
.iter()
.cycle()
.enumerate()
.find_map(|(index, instruction)| {
let entry = entries.get(&current).unwrap();
let next_node = match instruction {
'L' => &entry.left,
'R' => &entry.right,
_ => unreachable!(),
};
if next_node.ends_with('Z') {
Some(index + 1)
} else {
visited.push(next_node.clone());
current = next_node.clone();
None
}
})
.unwrap()
})
.collect::<Vec<usize>>();
Ok(lcm(&results) as u64)
}
fn lcm(nums: &[usize]) -> usize {
if nums.len() == 1 {
return nums[0];
}
let a = nums[0];
let b = lcm(&nums[1..]);
a * b / gcd(a, b)
}
fn gcd(a: usize, b: usize) -> usize {
if b == 0 {
return a;
}
gcd(b, a % b)
}
#[cfg(test)]
mod tests {
use super::*;
#[test_log::test]
fn test_parse_instructions() {
let input = "LRLRLR\n";
assert_eq!(
parse_instructions(input),
Ok(("", vec!['L', 'R', 'L', 'R', 'L', 'R']))
);
}
#[test_log::test]
fn test_parse_entry() {
let input = "AAA = (BBB, CCC)";
assert_eq!(
parse_entry(input),
Ok((
"",
(
"AAA".to_string(),
Entry {
left: "BBB".to_string(),
right: "CCC".to_string(),
}
)
))
);
}
#[test_log::test]
fn test_process() -> miette::Result<()> {
let input = include_str!("../test-input.txt");
assert_eq!(0, process(input)?);
let input = include_str!("../test-input2.txt");
assert_eq!(6, process(input)?);
Ok(())
}
}
+10
View File
@@ -0,0 +1,10 @@
LR
11A = (11B, XXX)
11B = (XXX, 11Z)
11Z = (11B, XXX)
22A = (22B, XXX)
22B = (22C, 22C)
22C = (22Z, 22Z)
22Z = (22B, 22B)
XXX = (XXX, XXX)