|
|
@@ -0,0 +1,191 @@
|
|
|
+use std::fs;
|
|
|
+use std::env;
|
|
|
+use std::collections::HashMap;
|
|
|
+use std::collections::HashSet;
|
|
|
+
|
|
|
+use nom::IResult;
|
|
|
+use nom::character::complete::alphanumeric1;
|
|
|
+use nom::bytes::complete::tag;
|
|
|
+use nom::multi::many1;
|
|
|
+use nom::sequence::separated_pair;
|
|
|
+use nom::combinator::map;
|
|
|
+use nom::sequence::preceded;
|
|
|
+use nom::sequence::terminated;
|
|
|
+use nom::branch::alt;
|
|
|
+use nom::character::complete::char;
|
|
|
+
|
|
|
+#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
+struct Node {
|
|
|
+ name: String,
|
|
|
+ left_child_name: String,
|
|
|
+ right_child_name: String,
|
|
|
+ left_child_offset: usize,
|
|
|
+ right_child_offset: usize,
|
|
|
+}
|
|
|
+
|
|
|
+fn assign_links(nodes: &mut Vec<Node>) {
|
|
|
+ let mut offset_map = HashMap::new();
|
|
|
+ for i in 0..nodes.len() {
|
|
|
+ offset_map.insert(nodes[i].name.clone(), i);
|
|
|
+ }
|
|
|
+ for i in 0..nodes.len() {
|
|
|
+ nodes[i].left_child_offset = *offset_map.get(&nodes[i].left_child_name).unwrap();
|
|
|
+ nodes[i].right_child_offset = *offset_map.get(&nodes[i].right_child_name).unwrap();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn find_first_offsets(nodes: &Vec<Node>) -> Vec<usize> {
|
|
|
+ let mut first_offsets = Vec::new();
|
|
|
+ for i in 0..nodes.len() {
|
|
|
+ if nodes[i].name.chars().last().unwrap() == 'A' {
|
|
|
+ first_offsets.push(i)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ first_offsets
|
|
|
+}
|
|
|
+
|
|
|
+fn find_last_offsets(nodes: &Vec<Node>) -> HashSet<usize> {
|
|
|
+ let mut last_offsets = HashSet::new();
|
|
|
+ for i in 0..nodes.len() {
|
|
|
+ if nodes[i].name.chars().last().unwrap() == 'Z' {
|
|
|
+ last_offsets.insert(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ last_offsets
|
|
|
+}
|
|
|
+
|
|
|
+fn follow_directions(directions: &Vec<char>, nodes: &Vec<Node>) -> Vec<i64> {
|
|
|
+ let mut step_count = 0;
|
|
|
+ let mut current_dir_offset = 0;
|
|
|
+
|
|
|
+ let mut offsets = find_first_offsets(&nodes);
|
|
|
+ let final_offsets = find_last_offsets(&nodes);
|
|
|
+
|
|
|
+ let mut steps_to_z:Vec<i64> = Vec::new();
|
|
|
+ for _ in 0..offsets.len() {
|
|
|
+ steps_to_z.push(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ loop {
|
|
|
+ for i in 0..offsets.len() {
|
|
|
+ if final_offsets.contains(&offsets[i]) && steps_to_z[i] == 0 {
|
|
|
+ steps_to_z[i] = step_count;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut is_all_set = true;
|
|
|
+ for step_to_z in &steps_to_z {
|
|
|
+ if *step_to_z == 0 {
|
|
|
+ is_all_set = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if is_all_set {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ for i in 0..offsets.len() {
|
|
|
+ let next_node_offset = if directions[current_dir_offset] == 'L' {
|
|
|
+ nodes[offsets[i]].left_child_offset
|
|
|
+ } else {
|
|
|
+ nodes[offsets[i]].right_child_offset
|
|
|
+ };
|
|
|
+ offsets[i] = next_node_offset;
|
|
|
+ }
|
|
|
+
|
|
|
+ current_dir_offset = (current_dir_offset + 1) % directions.len();
|
|
|
+ step_count += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ steps_to_z
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_node_name(input: &str) -> IResult<&str, &str> {
|
|
|
+ alphanumeric1(input)
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_node_children(input: &str) -> IResult<&str, (&str, &str)> {
|
|
|
+ terminated(
|
|
|
+ preceded(tag("("),
|
|
|
+ separated_pair(parse_node_name, tag(", "), parse_node_name)),
|
|
|
+ tag(")"))(input)
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_node(input: &str) -> IResult<&str, Node> {
|
|
|
+ map(separated_pair(parse_node_name, tag(" = "), parse_node_children),
|
|
|
+ |parsed| Node {
|
|
|
+ name: String::from(parsed.0),
|
|
|
+ left_child_name: String::from(parsed.1.0),
|
|
|
+ right_child_name: String::from(parsed.1.1),
|
|
|
+ left_child_offset: 0,
|
|
|
+ right_child_offset: 0,
|
|
|
+ })(input)
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_direction(input: &str) -> IResult<&str, char> {
|
|
|
+ alt((
|
|
|
+ char('L'),
|
|
|
+ char('R'),
|
|
|
+ ))(input)
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_directions(input: &str) -> IResult<&str, Vec<char>> {
|
|
|
+ many1(parse_direction)(input)
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_first_line(input: &str) -> Result<Vec<char>, String> {
|
|
|
+ match parse_directions(input) {
|
|
|
+ Ok((rest, data)) => if rest == "" {
|
|
|
+ Ok(data)
|
|
|
+ } else {
|
|
|
+ Err(format!("Incomplete parse, remaining: {}", rest))
|
|
|
+ },
|
|
|
+ Err(error) => Err(error.to_string()),
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+fn parse_line(input: &str) -> Result<Node, String> {
|
|
|
+ match parse_node(input) {
|
|
|
+ Ok((rest, data)) => if rest == "" {
|
|
|
+ Ok(data)
|
|
|
+ } else {
|
|
|
+ Err(format!("Incomplete parse, remaining: {}", rest))
|
|
|
+ },
|
|
|
+ Err(error) => Err(error.to_string()),
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn main() {
|
|
|
+ let args: Vec<String> = env::args().collect();
|
|
|
+ let file_path = &args[1];
|
|
|
+ let input_data = fs::read_to_string(file_path).unwrap_or_else(
|
|
|
+ |_|{panic!("Can't read file {}", file_path)});
|
|
|
+
|
|
|
+ let mut nodes:Vec<Node> = Vec::new();
|
|
|
+ let mut directions:Vec<char> = Vec::new();
|
|
|
+
|
|
|
+ let mut is_first = true;
|
|
|
+
|
|
|
+ for line in input_data.lines() {
|
|
|
+ if line.is_empty() {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if is_first {
|
|
|
+ is_first = false;
|
|
|
+ directions = parse_first_line(line).unwrap_or_else(
|
|
|
+ |e|panic!("Failed to parse line: {}", e));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let node = parse_line(line).unwrap_or_else(
|
|
|
+ |e|panic!("Failed to parse line: {}", e));
|
|
|
+ nodes.push(node);
|
|
|
+ }
|
|
|
+ assign_links(&mut nodes);
|
|
|
+
|
|
|
+ let result = follow_directions(&directions, &nodes);
|
|
|
+ println!("{result:?}");
|
|
|
+}
|