Skip to content

Commit

Permalink
Solve day 8
Browse files Browse the repository at this point in the history
  • Loading branch information
ricohageman committed Dec 8, 2023
1 parent 86aad75 commit a13cca3
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 9 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ doctest = false
test_lib = []

[dependencies]
gcd = "2.3.0"
itertools = "0.12.0"
pico-args = "0.5.0"
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `86.2µs` | `1.0ms` |
| [Day 2](./src/bin/02.rs) | `48.9µs` | `63.0µs` |
| [Day 3](./src/bin/03.rs) | `615.7µs` | `605.0µs` |
| [Day 4](./src/bin/04.rs) | `277.2µs` | `281.7µs` |
| [Day 5](./src/bin/05.rs) | `23.1µs` | `93.9µs` |
| [Day 6](./src/bin/06.rs) | `392.0ns` | `333.0ns` |
| [Day 7](./src/bin/07.rs) | `360.4µs` | `372.4µs` |

**Total: 3.83ms**
| [Day 1](./src/bin/01.rs) | `70.9µs` | `848.4µs` |
| [Day 2](./src/bin/02.rs) | `37.7µs` | `48.4µs` |
| [Day 3](./src/bin/03.rs) | `500.7µs` | `487.2µs` |
| [Day 4](./src/bin/04.rs) | `238.1µs` | `239.4µs` |
| [Day 5](./src/bin/05.rs) | `20.5µs` | `74.7µs` |
| [Day 6](./src/bin/06.rs) | `456.0ns` | `385.0ns` |
| [Day 7](./src/bin/07.rs) | `289.8µs` | `291.3µs` |
| [Day 8](./src/bin/08.rs) | `506.5µs` | `3.3ms` |

**Total: 6.95ms**
<!--- benchmarking table --->

---
Expand Down
9 changes: 9 additions & 0 deletions data/examples/08-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
RL

AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)
5 changes: 5 additions & 0 deletions data/examples/08-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
LLR

AAA = (BBB, BBB)
BBB = (AAA, ZZZ)
ZZZ = (ZZZ, ZZZ)
10 changes: 10 additions & 0 deletions data/examples/08-3.txt
Original file line number Diff line number Diff line change
@@ -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)
160 changes: 160 additions & 0 deletions src/bin/08.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use gcd::Gcd;
use std::collections::HashMap;
advent_of_code::solution!(8);

#[derive(Copy, Clone, Debug)]
enum Direction {
Left,
Right,
}

impl From<char> for Direction {
fn from(value: char) -> Self {
match value {
'L' => Self::Left,
'R' => Self::Right,
_ => panic!(),
}
}
}

#[derive(Copy, Clone, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)]
struct Node {
inner: [char; 3],
}

impl Node {
pub fn new(values: [char; 3]) -> Self {
Self { inner: values }
}

pub fn start() -> Self {
Self::new(['A', 'A', 'A'])
}
}

impl From<&str> for Node {
fn from(value: &str) -> Self {
Self::new(
value
.chars()
.filter(|c| *c != '(' && *c != ')')
.collect::<Vec<char>>()
.try_into()
.unwrap(),
)
}
}

fn solve<F>(
from: Node,
map: &HashMap<Node, (Node, Node)>,
sequence: &[Direction],
target_evaluation: F,
) -> (usize, Node)
where
F: Fn(Node) -> bool,
{
let mut current = from;
let mut steps = 0;

for direction in sequence.iter().cycle() {
steps += 1;

let (left, right) = map[&current];

current = match direction {
Direction::Left => left,
Direction::Right => right,
};

if target_evaluation(current) {
break;
}
}

(steps, current)
}

pub fn part_one(input: &str) -> Option<usize> {
let mut lines = input.lines();
let sequence: Vec<Direction> = lines.next().unwrap().chars().map(Direction::from).collect();

let nodes_to_indices: HashMap<Node, (Node, Node)> = lines
.skip(1)
.map(|line| {
let (departure, targets) = line.split_once(" = ").unwrap();
let (left, right) = targets.split_once(", ").unwrap();

(Node::from(departure), (Node::from(left), Node::from(right)))
})
.collect();

Some(
solve(Node::start(), &nodes_to_indices, &sequence, |node| {
node.inner == ['Z', 'Z', 'Z']
})
.0,
)
}

pub fn part_two(input: &str) -> Option<usize> {
let mut lines = input.lines();
let sequence: Vec<Direction> = lines.next().unwrap().chars().map(Direction::from).collect();

let nodes_to_indices: HashMap<Node, (Node, Node)> = lines
.skip(1)
.map(|line| {
let (departure, targets) = line.split_once(" = ").unwrap();
let (left, right) = targets.split_once(", ").unwrap();

(Node::from(departure), (Node::from(left), Node::from(right)))
})
.collect();

let starting_positions: Vec<Node> = nodes_to_indices
.keys()
.filter(|node| node.inner[2] == 'A')
.cloned()
.collect();

Some(
starting_positions
.iter()
.map(|starting_position| {
let (steps_to_first_target, _) =
solve(*starting_position, &nodes_to_indices, &sequence, |node| {
node.inner[2] == 'Z'
});

steps_to_first_target
})
.fold(1, |acc, next| (acc * next) / acc.gcd(next)),
)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file_part(
"examples", DAY, 1,
));
assert_eq!(result, Some(2));

let result = part_one(&advent_of_code::template::read_file_part(
"examples", DAY, 2,
));
assert_eq!(result, Some(6));
}

#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file_part(
"examples", DAY, 3,
));
assert_eq!(result, Some(6));
}
}

0 comments on commit a13cca3

Please sign in to comment.