Alexey Dorokhov 2 роки тому
батько
коміт
01472221f6
8 змінених файлів з 428 додано та 0 видалено
  1. 1 0
      day_8a/.gitignore
  2. 32 0
      day_8a/Cargo.lock
  3. 9 0
      day_8a/Cargo.toml
  4. 153 0
      day_8a/src/main.rs
  5. 1 0
      day_8b/.gitignore
  6. 32 0
      day_8b/Cargo.lock
  7. 9 0
      day_8b/Cargo.toml
  8. 191 0
      day_8b/src/main.rs

+ 1 - 0
day_8a/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_8a/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_8a"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_8a/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_8a"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 153 - 0
day_8a/src/main.rs

@@ -0,0 +1,153 @@
+use std::fs;
+use std::env;
+use std::collections::HashMap;
+
+use nom::IResult;
+use nom::character::complete::alpha1;
+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_offset(nodes: &Vec<Node>) -> usize {
+    for i in 0..nodes.len() {
+        if nodes[i].name == "AAA" {
+            return i;
+        }
+    }
+    nodes.len()
+}
+
+fn follow_directions(directions: &Vec<char>, nodes: &Vec<Node>) -> i64 {
+    let mut step_count = 0;
+    let mut current_dir_offset = 0;
+    let mut current_node_offset = find_first_offset(&nodes);
+    loop {
+        if nodes[current_node_offset].name == "ZZZ" {
+            break;
+        }
+        let next_node_offset = if directions[current_dir_offset] == 'L' {
+            nodes[current_node_offset].left_child_offset
+        } else {
+            nodes[current_node_offset].right_child_offset
+        };
+        current_node_offset = next_node_offset;
+        current_dir_offset = (current_dir_offset + 1) % directions.len();
+        step_count += 1;
+    }
+
+    step_count
+}
+
+fn parse_node_name(input: &str) -> IResult<&str, &str> {
+    alpha1(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:i64 = follow_directions(&directions, &nodes);
+    println!("{result}");
+}

+ 1 - 0
day_8b/.gitignore

@@ -0,0 +1 @@
+target

+ 32 - 0
day_8b/Cargo.lock

@@ -0,0 +1,32 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day_8b"
+version = "0.1.0"
+dependencies = [
+ "nom",
+]
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]

+ 9 - 0
day_8b/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "day_8b"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+nom = "7"

+ 191 - 0
day_8b/src/main.rs

@@ -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:?}");
+}