Advent of Code 2023 - Day 08
I'ts that time of year again, the Advent of Code! I'm completing the challenges this year using Rust 🦀 The loop on day 8 can really get you!
I'll be posting them here, just to document how I'm solving each challenge, and I highly encourage you to check them out before reading my solutions!
Here's the Day 8 Challenge
BTW: Complete Part 1 to have access to Part 2 😉
Solution for Part 1
use std::fs::File;
use std::io::{Read, Result};
use std::path::Path;
#[derive(Debug, Clone)]
struct NodePath {
id: String,
left: String,
right: String,
}
fn main() {
let input_file_path = "path_to_input_file";
let mut curr_sum = 0;
let file = file_as_string(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let (instructions, path) = file.split_once("\r\n\r\n").unwrap();
let instructions: Vec<char> = instructions.chars().collect();
let mut instruction_idx = 0;
let mut path: Vec<&str> = path.split("\r\n").collect();
path.sort();
let path: Vec<NodePath> = path.iter().map(|str| parse_node_path(str)).collect();
let mut curr_node = path.get(0).unwrap();
while curr_node.id != "ZZZ" {
curr_sum += 1;
curr_node = match instructions.get(instruction_idx).unwrap() {
'L' => path.iter().find(|n| n.id == curr_node.left).unwrap(),
'R' => path.iter().find(|n| n.id == curr_node.right).unwrap(),
_ => panic!("Dead end on directions.")
};
instruction_idx = if instruction_idx + 1 == instructions.len() {
0
} else {
instruction_idx + 1
}
}
println!("Steps to get to ZZZ: {}", curr_sum);
}
fn parse_node_path(raw_node_path: &str) -> NodePath {
let raw_node_path = raw_node_path.replace([' ', '(', ')'], "");
let (id, dirs) = raw_node_path.split_once('=').unwrap();
let (left, right) = dirs.split_once(',').unwrap();
NodePath {
id: id.into(),
left: left.into(),
right: right.into(),
}
}
fn file_as_string<P: AsRef<Path>>(filename: P) -> Result<String> {
let mut file = File::open(filename)?;
let mut file_as_str = String::new();
File::read_to_string(&mut file, &mut file_as_str)?;
Ok(file_as_str)
}
Solution for Part 2
use std::fs::File;
use std::io::{Read, Result};
use std::path::Path;
#[derive(Debug, Clone)]
struct NodePath {
id: String,
left: String,
right: String,
}
fn gcd(a: usize, b: usize) -> usize {
if b == 0 {
a
} else {
gcd(b, a % b)
}
}
fn lcm(a: usize, b: usize) -> usize {
a / gcd(a, b) * b
}
fn main() {
let input_file_path = "path_to_input";
let mut curr_sum = 0;
let file = file_as_string(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let (instructions, path) = file.split_once("\r\n\r\n").unwrap();
let instructions: Vec<char> = instructions.chars().collect();
let mut instruction_idx = 0;
let mut path: Vec<&str> = path.split("\r\n").collect();
path.sort();
let path: Vec<NodePath> = path.iter()
.map(|str| parse_node_path(str))
.collect();
let mut walking_nodes: Vec<usize> = vec![];
for (idx, node) in path.iter().enumerate() {
if node.id.ends_with('A') {
walking_nodes.push(idx);
}
}
let mut finishing_indexes : Vec<usize> = vec![];
while !walking_nodes.iter().all(|n| path.get(*n).unwrap().id.ends_with('Z')) {
curr_sum += 1;
let original_len = walking_nodes.len();
walking_nodes = process_walking_nodes(&path, walking_nodes, instructions[instruction_idx]);
instruction_idx = if instruction_idx + 1 == instructions.len() {
0
} else {
instruction_idx + 1
};
if original_len != walking_nodes.len() {
finishing_indexes.push(curr_sum as usize);
}
}
let lcm = finishing_indexes.iter()
.fold(1,|x, a| lcm(x, *a));
println!("Steps to where all nodes end with Z: {}", lcm);
}
fn process_walking_nodes(
path: &[NodePath],
walking_nodes: Vec<usize>,
instruction: char,
) -> Vec<usize> {
let mut new_walking_nodes: Vec<usize> = vec![];
for node in walking_nodes {
let next_node_id = match instruction {
'L' => { &path[node].left }
'R' => { &path[node].right }
_ => { panic!("Error whilst traversing nodes") }
};
if next_node_id.ends_with('Z') {
continue;
}
let next_node_index = path.iter().position(|n| &n.id == next_node_id).unwrap();
new_walking_nodes.push(next_node_index)
}
new_walking_nodes
}
fn parse_node_path(raw_node_path: &str) -> NodePath {
let raw_node_path = raw_node_path.replace([' ', '(', ')'], "");
let (id, dirs) = raw_node_path.split_once('=').unwrap();
let (left, right) = dirs.split_once(',').unwrap();
NodePath {
id: id.into(),
left: left.into(),
right: right.into(),
}
}
fn file_as_string<P: AsRef<Path>>(filename: P) -> Result<String> {
let mut file = File::open(filename)?;
let mut file_as_str = String::new();
File::read_to_string(&mut file, &mut file_as_str)?;
Ok(file_as_str)
}