Skip to content

Commit

Permalink
Day 8
Browse files Browse the repository at this point in the history
  • Loading branch information
kcaffrey committed Dec 8, 2023
1 parent f4fd17e commit 9d5c2d3
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 9 deletions.
20 changes: 20 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 @@ -21,6 +21,7 @@ enum-ordinalize = "4.2.1"
smallvec = "1.11.2"
itertools = "0.12.0"
rayon = "1.8.0"
num-integer = "0.1"

[profile.dhat]
inherits = "release"
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `28.7µs` | `34.6µs` |
| [Day 2](./src/bin/02.rs) | `41.7µs` | `40.8µs` |
| [Day 3](./src/bin/03.rs) | `84.3µs` | `99.7µs` |
| [Day 4](./src/bin/04.rs) | `49.0µs` | `50.7µs` |
| [Day 5](./src/bin/05.rs) | `20.7µs` | `24.3µs` |
| [Day 6](./src/bin/06.rs) | `203.0ns` | `102.0ns` |
| [Day 7](./src/bin/07.rs) | `97.6µs` | `92.8µs` |

**Total: 0.67ms**
| [Day 1](./src/bin/01.rs) | `35.5µs` | `34.9µs` |
| [Day 2](./src/bin/02.rs) | `41.7µs` | `40.9µs` |
| [Day 3](./src/bin/03.rs) | `82.1µs` | `99.4µs` |
| [Day 4](./src/bin/04.rs) | `48.8µs` | `52.0µs` |
| [Day 5](./src/bin/05.rs) | `20.4µs` | `24.2µs` |
| [Day 6](./src/bin/06.rs) | `202.0ns` | `102.0ns` |
| [Day 7](./src/bin/07.rs) | `92.4µs` | `91.9µs` |
| [Day 8](./src/bin/08.rs) | `284.6µs` | `1.9ms` |

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

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

AAA = (AAB, XXX)
AAB = (XXX, AAZ)
AAZ = (AAB, XXX)
BBA = (BBB, XXX)
BBB = (BBC, BBC)
BBC = (BBZ, BBZ)
BBZ = (BBB, BBB)
XXX = (XXX, XXX)
9 changes: 9 additions & 0 deletions data/examples/08.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)
146 changes: 146 additions & 0 deletions src/bin/08.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use std::{collections::HashMap, fmt::Display, str::FromStr};

advent_of_code::solution!(8);

pub fn part_one(input: &str) -> Option<u64> {
let map = parse(input)?;
let start = "AAA".parse().ok()?;
Some(map.steps_to_dest(start, Node::is_zzz))
}

pub fn part_two(input: &str) -> Option<u64> {
let map = parse(input)?;
map.adjacency
.keys()
.copied()
.filter(Node::ends_with_a)
.map(|start| map.steps_to_dest(start, Node::ends_with_z))
.reduce(num_integer::lcm)
}

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

#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
struct Node(u16);

impl Node {
pub fn ends_with_a(&self) -> bool {
self.0 % 26 == 0
}

pub fn ends_with_z(&self) -> bool {
self.0 % 26 == 25
}

pub fn is_zzz(&self) -> bool {
self.0 == 25 * 26 * 26 + 25 * 26 + 25
}
}

#[derive(Clone, Debug)]
struct Map {
instructions: Vec<Direction>,
adjacency: HashMap<Node, (Node, Node)>,
}

impl Map {
pub fn steps_to_dest<F: Fn(&Node) -> bool>(&self, start: Node, is_dest: F) -> u64 {
let mut cur = start;
let mut steps = 0;
let mut instructions = self.instructions.iter().cycle();
while !is_dest(&cur) {
let instruction = instructions.next().unwrap();
let &(left, right) = self
.adjacency
.get(&cur)
.expect("should be in adjacency map");
let next = match instruction {
Direction::Left => left,
Direction::Right => right,
};
cur = next;
steps += 1;
}
steps
}
}

fn parse(input: &str) -> Option<Map> {
let mut lines = input.lines();
let instructions = lines
.next()?
.trim()
.chars()
.filter_map(|ch| match ch {
'L' => Some(Direction::Left),
'R' => Some(Direction::Right),
_ => None,
})
.collect();
lines.next()?;
let adjacency = lines
.filter_map(|line| {
let (source, dest_strs) = line.split_once(" = ")?;
let (left_dest, right_dest) = dest_strs
.trim_matches(|ch| ch == '(' || ch == ')')
.split_once(", ")?;
Some((
source.parse().ok()?,
(left_dest.parse().ok()?, right_dest.parse().ok()?),
))
})
.collect();
Some(Map {
instructions,
adjacency,
})
}

#[derive(Debug)]
struct ParseNodeErr;

impl FromStr for Node {
type Err = ParseNodeErr;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(
s.chars()
.map(|ch| ch as u16 - 'A' as u16)
.reduce(|acc, d| acc * 26 + d)
.ok_or(ParseNodeErr)?,
))
}
}

impl Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s: String = [self.0 / (26 * 26), (self.0 / 26) % 26, self.0 % 26]
.into_iter()
.map(|d| (d + 'A' as u16) as u8 as char)
.collect();
write!(f, "{}", s)
}
}

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

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

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

0 comments on commit 9d5c2d3

Please sign in to comment.