Skip to content

Commit

Permalink
Year 2016: Day 17
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 21, 2024
1 parent 6167f54 commit 27d5e1a
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ Of note:
- The changelog 2015.5.2 has been rewritten from each commit content.
- This file may be amended entirely in the future to adhere to the [GNU Changelog style](https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html#Style-of-Change-Logs)

## [2016.17.1]
### Added
- Solved [exercice for 2016, day 17](src/year_2016/17.rs).
- First version of [BreadthFirstSearch](src/bfs.rs).

## [2016.16.1]
### Added
- Solved [exercice for 2016, day 16](src/year_2016/day_16.rs).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "advent-rs"
version = "2016.16.1"
version = "2016.17.1"
edition = "2021"
authors = ["Arnaud 'red' Rouyer"]
readme = "README.md"
Expand Down
4 changes: 4 additions & 0 deletions NOTES_2016.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ Finally, something funny to do.
For once, I'm faster than my [rustaceans counterparts](https://docs.rs/advent-of-code/2022.0.66/src/advent_of_code/year2016/day16.rs.html). In that case, manipulating `Vec<bool>` instead of `String` worked better.

Another thing of note is that if you know how to optimize your loop-down, you can win a lot of time.

## Day 17: Two Steps Forward

A big part of this day was bout building a [BreadthFirstSearch library](src/bfs.rs) that would be flexible enough to reuse in other exercises, since there are SO MANY in this year.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ As I said, [Consistency is hard](https://github.com/joshleaves/advent-rb), and I

I'm also adding notes that may be useful if you're (like me) discovering Rust:
- [2015, complete!](NOTES_2015.md)
- [2016, up to day 16](NOTES_2016.md)
- [2016, up to day 17](NOTES_2016.md)

# Regarding style rules
I'm gonna use a mix of what `cargo fmt` does, with some stuff that feels more natural to me.
Expand Down
16 changes: 15 additions & 1 deletion benches/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use advent_rs::year_2016::day_13;
use advent_rs::year_2016::day_14;
use advent_rs::year_2016::day_15;
use advent_rs::year_2016::day_16;
use advent_rs::year_2016::day_17;
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn year_2016_day_01(c: &mut Criterion) {
Expand Down Expand Up @@ -208,6 +209,18 @@ pub fn year_2016_day_16(c: &mut Criterion) {
g2016_day_16.finish();
}

pub fn year_2016_day_17(c: &mut Criterion) {
let mut g2016_day_17 = c.benchmark_group("year_2016::day_17");
let input_year_2016_day_17 = include_str!("../inputs/year_2016/day_17_input");
g2016_day_17.bench_function("year_2016::day_17_v1", |b| {
b.iter(|| day_17::day_17_v1(black_box(input_year_2016_day_17)))
});
g2016_day_17.bench_function("year_2016::day_17_v2", |b| {
b.iter(|| day_17::day_17_v2(black_box(input_year_2016_day_17)))
});
g2016_day_17.finish();
}

criterion_group!(
benches,
year_2016_day_01,
Expand All @@ -225,6 +238,7 @@ criterion_group!(
year_2016_day_13,
year_2016_day_14,
year_2016_day_15,
year_2016_day_16
year_2016_day_16,
year_2016_day_17
);
criterion_main!(benches);
1 change: 1 addition & 0 deletions inputs/year_2016/day_17_input
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mmsxrhfx
82 changes: 82 additions & 0 deletions src/bfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::collections::HashSet;
use std::collections::VecDeque;
use std::hash::Hash;

pub struct BreadthFirstSearch<POS, NM>
where
POS: Eq + Hash + Clone,
NM: Fn(POS) -> Vec<POS>,
{
starting: POS,
pub ending: Option<POS>,
queue: VecDeque<(POS, usize)>,
visited: HashSet<POS>,
pub depth: usize,
next_moves: NM,
}

impl<POS, NM> BreadthFirstSearch<POS, NM>
where
POS: Eq + Hash + Clone,
NM: Fn(POS) -> Vec<POS>,
{
pub fn new(starting: POS, next_moves: NM) -> Self {
BreadthFirstSearch {
starting: starting,
ending: None,
queue: VecDeque::new(),
visited: HashSet::new(),
depth: 0,
next_moves: next_moves,
}
}

fn traverse<TU>(&mut self, traverse_until: TU, stop_on_success: bool) -> &mut Self
where
TU: Fn(&POS, usize) -> bool,
{
self.queue.push_back((self.starting.clone(), 0));
while let Some((position, depth)) = self.queue.pop_front() {
if traverse_until(&position, depth) {
self.ending = Some(position);
self.depth = depth;
if stop_on_success {
return self;
} else {
continue;
}
}
self.visited.insert(position.clone());
for next_move in (self.next_moves)(position) {
if self.visited.contains(&next_move) {
continue;
}
self.queue.push_back((next_move, depth + 1));
}
}

self
}

pub fn traverse_until_depth(&mut self, target_depth: usize) -> &mut Self {
self.traverse(|_position, depth| depth == target_depth, true)
}

pub fn traverse_until_position(&mut self, target_position: POS) -> &mut Self {
self.traverse(|position, _depth| *position == target_position, true)
}

pub fn traverse_until<TU>(&mut self, traverse_until: TU) -> &mut Self
where
TU: Fn(&POS) -> bool,
{
self.traverse(|position, _depth| traverse_until(position), true)
}

pub fn longest_path_until<TU>(&mut self, traverse_until: TU) -> &mut Self
where
TU: Fn(&POS) -> bool,
{
self.traverse(|position, _depth| traverse_until(position), false)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ macro_rules! solvable {
};
}

mod bfs;
pub mod year_2015;
pub mod year_2016;

Expand Down
9 changes: 9 additions & 0 deletions src/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod day_13;
pub mod day_14;
pub mod day_15;
pub mod day_16;
pub mod day_17;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part != 1 && part != 2 {
Expand All @@ -42,6 +43,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
14 => Some(format!("{}", day_14::day_14(part, input))),
15 => Some(format!("{}", day_15::day_15(part, input))),
16 => Some(format!("{}", day_16::day_16(part, input))),
17 => Some(format!("{}", day_17::day_17(part, input))),
_ => None,
}
}
Expand Down Expand Up @@ -170,4 +172,11 @@ mod tests {
assert_eq!(day_16::day_16_v1(input), "10100011010101011");
assert_eq!(day_16::day_16_v2(input), "01010001101011001");
}

#[test]
fn day_17() {
let input = include_str!("../inputs/year_2016/day_17_input");
assert_eq!(day_17::day_17_v1(input), "RLDUDRDDRR");
assert_eq!(day_17::day_17_v2(input), "590");
}
}
154 changes: 154 additions & 0 deletions src/year_2016/day_17.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use crate::bfs::BreadthFirstSearch;
use md5::{digest::core_api::CoreWrapper, Digest, Md5, Md5Core};
use std::hash::{Hash, Hasher};

#[derive(Eq, Clone)]
struct Day17Position {
position: (usize, usize),
path: String,
}

impl Day17Position {
fn new(position: (usize, usize)) -> Self {
Self {
position,
path: String::new(),
}
}

pub fn next_moves_for_hasher(&self, mut hasher: CoreWrapper<Md5Core>) -> Vec<Self> {
hasher.update(self.path.clone());
let positions: &[u8] = &hasher.finalize()[0..=1];
let mut next_moves = vec![];
if ((positions[0] & 0xF0) >> 4) >= 11 {
if let Some(move_up) = self.move_up() {
next_moves.push(move_up);
}
}
if (positions[0] & 0x0F) >= 11 {
if let Some(move_down) = self.move_down() {
next_moves.push(move_down);
}
}
if ((positions[1] & 0xF0) >> 4) >= 11 {
if let Some(move_left) = self.move_left() {
next_moves.push(move_left);
}
}
if (positions[1] & 0x0F) >= 11 {
if let Some(move_right) = self.move_right() {
next_moves.push(move_right);
}
}
next_moves
}

fn move_up(&self) -> Option<Day17Position> {
if self.position.1 == 0 {
return None;
}
Some(Day17Position {
position: (self.position.0, self.position.1 - 1),
path: self.path.clone() + "U",
})
}

fn move_down(&self) -> Option<Day17Position> {
if self.position.1 == 3 {
return None;
}
Some(Day17Position {
position: (self.position.0, self.position.1 + 1),
path: self.path.clone() + "D",
})
}

fn move_left(&self) -> Option<Day17Position> {
if self.position.0 == 0 {
return None;
}
Some(Day17Position {
position: (self.position.0 - 1, self.position.1),
path: self.path.clone() + "L",
})
}

fn move_right(&self) -> Option<Day17Position> {
if self.position.0 == 3 {
return None;
}
Some(Day17Position {
position: (self.position.0 + 1, self.position.1),
path: self.path.clone() + "R",
})
}
}

impl PartialEq for Day17Position {
fn eq(&self, _other: &Self) -> bool {
// self.position == other.position && sel
false
}
}

impl Hash for Day17Position {
fn hash<H: Hasher>(&self, state: &mut H) {
self.position.hash(state);
}
}

pub fn day_17_v1(input: impl Into<String>) -> String {
let mut hasher = Md5::new();
hasher.update(input.into().trim_end());
let starter = Day17Position::new((0, 0));
let mut bfs = BreadthFirstSearch::new(starter, |curpos| {
curpos.next_moves_for_hasher(hasher.clone())
});
bfs.traverse_until(|position| position.position == (3, 3));

bfs.ending.unwrap().path
}

pub fn day_17_v2(input: impl Into<String>) -> String {
let mut hasher = Md5::new();
hasher.update(input.into().trim_end());
let starter = Day17Position::new((0, 0));
let mut bfs = BreadthFirstSearch::new(starter, |curpos| {
curpos.next_moves_for_hasher(hasher.clone())
});
bfs.longest_path_until(|position| position.position == (3, 3));

bfs.depth.to_string()
}

solvable!(day_17, day_17_v1, day_17_v2, String);

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

#[test]
fn works_with_samples_v1() {
let sample_one: [(&str, &str); 3] = [
("ihgpwlah", "DDRRRD"),
("kglvqrro", "DDUDRLRRUDRD"),
("ulqzkmiv", "DRURDRUDDLLDLUURRDULRLDUUDDDRR"),
];
for (sample, result) in sample_one {
println!("TEST => {}", sample);
assert_eq!(day_17_v1(sample), result);
}
}

#[test]
fn works_with_samples_v2() {
let sample_two: [(&str, &str); 3] = [
("ihgpwlah", "370"),
("kglvqrro", "492"),
("ulqzkmiv", "830"),
];
for (sample, result) in sample_two {
assert_eq!(day_17_v2(sample), result);
}
}
}

0 comments on commit 27d5e1a

Please sign in to comment.