day 8 done had to cheat for part 2 because brute force was not good even though algorithm was
This commit is contained in:
13
2023/day-8/Cargo.toml
Normal file
13
2023/day-8/Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "day-8"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nom.workspace = true
|
||||||
|
itertools.workspace = true
|
||||||
|
rstest = {workspace = true}
|
||||||
4
2023/day-8/src/lib.rs
Normal file
4
2023/day-8/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
pub mod part1;
|
||||||
|
pub use crate::part1::*;
|
||||||
|
pub mod part2;
|
||||||
|
pub use crate::part2::*;
|
||||||
12
2023/day-8/src/main.rs
Normal file
12
2023/day-8/src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use day_8::part1;
|
||||||
|
use day_8::part2;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = include_str!("./input.txt");
|
||||||
|
let part1_result = part1(input);
|
||||||
|
println!("part 1: {part1_result}");
|
||||||
|
let part2_result = part2(input);
|
||||||
|
println!("part 2: {part2_result}");
|
||||||
|
}
|
||||||
134
2023/day-8/src/part1.rs
Normal file
134
2023/day-8/src/part1.rs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete,
|
||||||
|
multi::{many1, separated_list1},
|
||||||
|
sequence::{delimited, pair, separated_pair, tuple},
|
||||||
|
IResult, Parser,
|
||||||
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Branches {
|
||||||
|
pub left: String,
|
||||||
|
pub right: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Branches {
|
||||||
|
fn choose(&self, direction: Direction) -> &str {
|
||||||
|
match direction {
|
||||||
|
Direction::Left => &self.left,
|
||||||
|
Direction::Right => &self.right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// day 4 part 1 of aoc 2023
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// - input the input for today's puzzle
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||||
|
/// usize
|
||||||
|
#[must_use]
|
||||||
|
pub fn part1(input: &str) -> String {
|
||||||
|
let (_, (steps, branches)) = parse_input(input).expect("aoc expects valid input");
|
||||||
|
|
||||||
|
let mut current = "AAA";
|
||||||
|
let mut count = 0_usize;
|
||||||
|
for x in steps.iter().cycle() {
|
||||||
|
if current == "ZZZ" {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = branches.get(current).expect("aoc").choose(*x);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
count.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directions(input: &str) -> IResult<&str, Vec<Direction>> {
|
||||||
|
let (input, directions) = many1(alt((
|
||||||
|
tag("L").map(|_| Direction::Left),
|
||||||
|
tag("R").map(|_| Direction::Right),
|
||||||
|
)))(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
Ok((input, directions))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_branches(input: &str) -> IResult<&str, Branches> {
|
||||||
|
let (input, (left, right)) = delimited(
|
||||||
|
pair(tag("("), complete::space0),
|
||||||
|
separated_pair(
|
||||||
|
complete::alpha1,
|
||||||
|
pair(tag(","), complete::space1),
|
||||||
|
complete::alpha1,
|
||||||
|
),
|
||||||
|
pair(complete::space0, tag(")")),
|
||||||
|
)(input)?;
|
||||||
|
let left = left.to_string();
|
||||||
|
let right = right.to_string();
|
||||||
|
Ok((input, Branches { left, right }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_nodes(input: &str) -> IResult<&str, (String, Branches)> {
|
||||||
|
let (input, (node, branches)) = separated_pair(
|
||||||
|
complete::alpha1,
|
||||||
|
tuple((complete::space1, tag("="), complete::space1)),
|
||||||
|
parse_branches,
|
||||||
|
)(input)?;
|
||||||
|
Ok((input, (node.to_string(), branches)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_node_tree(input: &str) -> IResult<&str, BTreeMap<String, Branches>> {
|
||||||
|
let (input, map) = separated_list1(complete::line_ending, parse_nodes)(input)?;
|
||||||
|
let map = map.into_iter().collect();
|
||||||
|
Ok((input, map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, (Vec<Direction>, BTreeMap<String, Branches>)> {
|
||||||
|
let (input, x) =
|
||||||
|
separated_pair(parse_directions, complete::line_ending, parse_node_tree)(input)?;
|
||||||
|
Ok((input, x))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(
|
||||||
|
"RL
|
||||||
|
|
||||||
|
AAA = (BBB, CCC)
|
||||||
|
BBB = (DDD, EEE)
|
||||||
|
CCC = (ZZZ, GGG)
|
||||||
|
DDD = (DDD, DDD)
|
||||||
|
EEE = (EEE, EEE)
|
||||||
|
GGG = (GGG, GGG)
|
||||||
|
ZZZ = (ZZZ, ZZZ)",
|
||||||
|
"2"
|
||||||
|
)]
|
||||||
|
#[case(
|
||||||
|
"LLR
|
||||||
|
|
||||||
|
AAA = (BBB, BBB)
|
||||||
|
BBB = (AAA, ZZZ)
|
||||||
|
ZZZ = (ZZZ, ZZZ)",
|
||||||
|
"6"
|
||||||
|
)]
|
||||||
|
|
||||||
|
fn part1_works(#[case] input: &str, #[case] expected: &str) {
|
||||||
|
let result = part1(input);
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
160
2023/day-8/src/part2.rs
Normal file
160
2023/day-8/src/part2.rs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete,
|
||||||
|
multi::{many1, separated_list1},
|
||||||
|
sequence::{delimited, pair, separated_pair, tuple},
|
||||||
|
IResult, Parser,
|
||||||
|
};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum Direction {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Branches {
|
||||||
|
pub left: String,
|
||||||
|
pub right: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Branches {
|
||||||
|
fn choose(&self, direction: Direction) -> &str {
|
||||||
|
match direction {
|
||||||
|
Direction::Left => &self.left,
|
||||||
|
Direction::Right => &self.right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// day 4 part 1 of aoc 2023
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// - input the input for today's puzzle
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than
|
||||||
|
/// usize
|
||||||
|
#[must_use]
|
||||||
|
pub fn part2(input: &str) -> String {
|
||||||
|
let (_, (steps, branches)) = parse_input(input).expect("aoc expects valid input");
|
||||||
|
|
||||||
|
let starting_node: Vec<&str> = branches
|
||||||
|
.keys()
|
||||||
|
.map(String::as_str)
|
||||||
|
.filter(|x| x.ends_with('A'))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let cycles = starting_node
|
||||||
|
.iter()
|
||||||
|
.map(|node| {
|
||||||
|
let mut visited_nodes = vec![*node];
|
||||||
|
let mut current = *node;
|
||||||
|
steps
|
||||||
|
.iter()
|
||||||
|
.cycle()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, direction)| {
|
||||||
|
let new = branches.get(current).expect("aoc1").choose(*direction);
|
||||||
|
if new.ends_with('Z') {
|
||||||
|
return Some(i + 1);
|
||||||
|
}
|
||||||
|
visited_nodes.push(new);
|
||||||
|
dbg!(current = new);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.expect("aoc4")
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
lcm(&cycles).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directions(input: &str) -> IResult<&str, Vec<Direction>> {
|
||||||
|
let (input, directions) = many1(alt((
|
||||||
|
tag("L").map(|_| Direction::Left),
|
||||||
|
tag("R").map(|_| Direction::Right),
|
||||||
|
)))(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
Ok((input, directions))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_branches(input: &str) -> IResult<&str, Branches> {
|
||||||
|
let (input, (left, right)) = delimited(
|
||||||
|
pair(tag("("), complete::space0),
|
||||||
|
separated_pair(
|
||||||
|
complete::alphanumeric1,
|
||||||
|
pair(tag(","), complete::space1),
|
||||||
|
complete::alphanumeric1,
|
||||||
|
),
|
||||||
|
pair(complete::space0, tag(")")),
|
||||||
|
)(input)?;
|
||||||
|
let left = left.to_string();
|
||||||
|
let right = right.to_string();
|
||||||
|
Ok((input, Branches { left, right }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_nodes(input: &str) -> IResult<&str, (String, Branches)> {
|
||||||
|
let (input, (node, branches)) = separated_pair(
|
||||||
|
complete::alphanumeric1,
|
||||||
|
tuple((complete::space1, tag("="), complete::space1)),
|
||||||
|
parse_branches,
|
||||||
|
)(input)?;
|
||||||
|
Ok((input, (node.to_string(), branches)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_node_tree(input: &str) -> IResult<&str, BTreeMap<String, Branches>> {
|
||||||
|
let (input, map) = separated_list1(complete::line_ending, parse_nodes)(input)?;
|
||||||
|
let map = map.into_iter().collect();
|
||||||
|
Ok((input, map))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, (Vec<Direction>, BTreeMap<String, Branches>)> {
|
||||||
|
let (input, x) =
|
||||||
|
separated_pair(parse_directions, complete::line_ending, parse_node_tree)(input)?;
|
||||||
|
Ok((input, x))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case(
|
||||||
|
"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)",
|
||||||
|
"6"
|
||||||
|
)]
|
||||||
|
|
||||||
|
fn part2_works(#[case] input: &str, #[case] expected: &str) {
|
||||||
|
let result = part2(input);
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user