Skip to content

Commit

Permalink
Year 2016: Day 24
Browse files Browse the repository at this point in the history
  • Loading branch information
joshleaves committed Mar 22, 2024
1 parent e77e9ba commit cf19c2c
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 3 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ 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.24.1]
### Added
- Solved [exercice for 2016, day 24](src/year_2016/24.rs).

## [2016.23.1]
### Added
- Solved [exercice for 2016, day 23](src/year_2016/23.rs).


## [2016.22.1]
### Added
- Solved [exercice for 2016, day 22](src/year_2016/22.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.23.1"
version = "2016.24.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 @@ -106,3 +106,7 @@ So far, my [BreadthFirstSearch library](src/bfs.rs) has saved me from rewriting
## Day 23: Safe Cracking

I had forgotten [the original was taking ten minutes to run](https://github.com/joshleaves/advent-rb/NOTES_2016.md#day-23-safe-cracking). This one takes only seven seconds, and I'll be happy with it.

## Day 24: Air Duct Spelunking

Look, [BreadthFirstSearch library](src/bfs.rs) is coming to our rescue again!
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 23](NOTES_2016.md)
- [2016, up to day 24](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
39 changes: 39 additions & 0 deletions inputs/year_2016/day_24_input
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#####################################################################################################################################################################################
#.....#.#.....#.#.#...#.....#.#.#.#.....#3......#...........#.#.....#.....#.............#.#...#...#.....#...#.........#.#...............#.....#.....#.........................#.....#
#.#.#.#.#.#.#.#.#.#.#.#.###.#.#.#.#.#.#####.#.#.#.#####.#.###.#.#.###.#.#####.###.#.#.#.#.#####.#.#.#.#####.#.#.#####.#.#.#####.###.#.#.###.###.###.#.###.#.###.###.#.#######.#######
#.....#...#.#.....#...#.........#...........#.......#...#.#.....#.....#.#.#.......#...#.#...#...#...#.#...#...#.#...#.#.#.#.......#...#...#.....#.....#.....#...#...#.......#.#...#.#
###.#####.#.#######.###.#.#.#.#.#.#####.#.#.#.#####.#.###.#.#####.#.#.#.#.#####.#.#.#.#.#.###.#.###.#.#####.#.#.#.#.###.#.#.#.###.#.#.#.#.###.#.#.###.#.#.#.#.#.#.#.#.#.#####.#.#.#.#
#...#1......#.....#.#.#.#.....#.#.#.....#...#.#...#.......#...........#.............#...#.....#.......#.....#.....#.#.......#.#...#.#.#.#.......#...#.#...........#.#.....#...#.#...#
#.#.###.#.###.#.#.#.#.#.#.#######.#.#.###.#.#.#.###.#####.###.#.#.#.#.#.###.#.#####.#.#.#####.#####.#.#.#.#.#.#.#.###.###.#.#####.#.#.#####.#.#.#.#.#.#.#.###.#####.###.#.#.#.#.###.#
#.....#.#.....#.#...#.#.....#.#.#.#.........#.#.#.......#.#.#.......#...#...#...#.....#.#...#.......#.....#.......#...#.....#...#...#.......#...#.#.....#...#.#.....#.#...#.#...#...#
#.#.#.#.#####.#.###.#.#.#.###.#.###.#.#.#####.#.#####.#.#.#.#.#####.#.#.#.###.#######.#.###.#.###.#.#.#.#.#.#.#####.###.#####.#.#.#.#.#####.#####.#.###.#.#.#.#.#.#.#.#####.###.#.###
#...#.#...#.........#.#...#.#...#.....#.#.....#.......#.........#.....#.....#.........#.....#...#.#.#.#.....#.#.................#.#.#.......#.......#.......#...#...#.......#.#...#.#
#.#.#.#.#.#.#.###.#.###.###.#.#.#.###.###.#.#.#.#.#.#.#########.#.###.#.#.#####.#.#.#.###.#######.#.###.#.#.#.#.#.#.#.###.#.#.###.#######.#.###.#.#.#.#.###.#.#.#.#####.###.#.#.###.#
#...#.#.#.#.#...#...#...#.............#.....#.....#...#...#.#.....#...#...#.....#.#.....#...#...........#.#.#.#.......#...#.............#...#.#...#...........#...#2#...#.....#.#.#.#
###.###.#.#####.#.#.#.#.#.#.#.#.#.#.###.###.#.#.#.###.###.#.#.#.#.#.#.###.###.#.#.#.#.###.#.#.#.###.#####.###.###.#.#.#.###.#######.###.###.#.#.#.#####.#####.###.#.#.#####.#.#.#.###
#.......#...#...........#.......#...#.#.......#.....#.....#...#...#.#.........#.......#...#.#...#...........#.#...#.#...............#.#.#.....#.......#.#.#.#.....#.........#.#.#.#.#
###.#.#.#.#.#.#.#####.###.#.#.#.#.#.#.#.#.#.#.#.#.#.#.#####.#.###.#.###.#.#.#.#########.###.#.###.#.###.#.#.#.#.#######.#####.###.#.#.#.#.#####.#####.#.#.#.#.#.#######.#.###.#.#.#.#
#...#.....#.#.............#...#.#.....#.#...#.......#.........#...#...#.#...#...........#...#...........#...#...#.................#...#.#.#.....#.......#.#.#.......#...#...#...#...#
###.#.#######.#.#.#.###.#.#.#.###.#.#.#.#.#.###.#####.#.###.#.#.#.###.#.#.#.#.#.#.#.###.#.#.#.#.#.#.#####.#.#####.#.#.#.#.#.#######.#.#.#.###.#.#.#.#.#.###.#.###.#########.###.#.#.#
#.....#...#.....#.....#...#.........#.#...#.....#...#.................#...#.#.#.......#.....#...#.#.................#.#.#.........#.....#...#.#...#.....#.................#.#.....#.#
#.#####.#.#.#####.###.#.#.#.###.###.#.#########.#.#######.###.###.#.#.#.#.#.#.#.###.#.#.#####.#.###.#.#########.#.###.###.#.###.###.#.#.#.#.#.###.#####.#.#.#######.#.###.#.#.###.###
#0#...#.#...#...........#.#.............#.#.......#.....#.#.#.....#.#.#...#.....#.#.#.......#.#...#.................#.#.#.....#...#.#.........#.......#.#...#.......#.......#.....#.#
#####.#.###.#.#.###.#####.###.#######.###.#.#.###.#.#.#.#.#.#.#.#.###.#.#.#.#.#.#.#.#.#####.###.#.#####.#.#.#.###.#.#.#.#########.#.#.#.###.#.#.###.#.#####.#.#.#.#.#.###.###.#.#.#.#
#...#.#.....#.#.#...#.......#.#.#...#.........#.......#...#.#.....#.............#...........#...#.#.......#.#.....#.#...........#...#.........#.#...#.#.....#.......#...#.......#...#
###.###.###.#.###.###.#.###.###.#.#.#.#########.#####.###.#######.#.###.#.#.#.###.#.#.#.#.#.#.#.#####.###.#.#.#.#.#.#.#####.###.#.#.###.#####.#.#.#####.#.#.###.#.#####.#.#.#.#.#.#.#
#.......#...............#.....#...#...#.#...#.........#...........#.....#.....#.......#...#.....#.......#.#.#.#...#...............#.....#.....#...#.#...#...#.#.#.#.#.....#...#.#.#4#
#.#.#.#.#.#####.###.#.#####.#.###.#.###.#.#.#.###.###.#.#####.#.#####.#.#######.#.#####.#.#######.#.###.#.#.#.###.###.###.#####.#.###.#.#.#.#####.#.#.#.###.#.#.###.###.#.#.#.#.#.###
#...#...........#.......#...#.....#.#.......#.....#.........#.#.......#.#...#...#...........#.......#.....#...#...#...#.#...#.....#...#.........#...#...#.....#.....#...#...#...#...#
###.#.###.#############.#.###.###.#.###.#.#.#.#.#.#.#.#.###.#####.#.#.#.#.#.#####.###.#.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.#.###.###.#.#.###.#.#.#.#.#.#.#####.#.#.#.#.#.#.###.#.###
#...#.....#.#...#.#.#.#...........#.......#...#.....#...#...................#...#...#.#.#.#...#.......#...............#...#...#...#...#.#.#5#...#...#.#...#...#.#...#.#...#...#.#...#
#.###.###.#.#.#.#.#.#.###.#.#######.#.#.#.###.#.#.#.#.#.#.###.#.###.###.#.#.#.#.#.###.#.#.#.#.#.###.#.#.###.#.#.###.#.#.#.#.#.#.#.#.#####.#.#.#######.#.#.#.#.#.#####.#.#######.#.#.#
#.........#.#...#.#...#...#.............#.....#...#.#.#.#.#.#.....#.#...#.....#.#.#.........#.....#.........#.....#...........#...#.........#.....#...#.#.#.#...#...........#...#...#
#.#.#.#######.#.#####.#.#.#.###.#.#######.#####.#.#.###.#.#.#.###.###.#.###.#.#.#.###.#.#.#.#####.#.###.#.#.#.#.#.#.#####.#.#.###.#.#.#.#.#####.###.#####.#.#.#.#.###.#.#.#####.#####
#...#...............#.#.......#.......#.......#.........#.#.#.#...#...#...#.......#.#.....#.#...........#...#.#.#...........#.#...#.......#.........#...#...#.....#...#.#.....#.#...#
###.#.#.#.#.#########.#.#.#.#.#.#####.#.#######.#.###.#.#.#.###.###.#.#.#.#.#.###.#.#.###.#.#####.#.###########.#.#.#####.#####.#####.#####.#.#.#.#.#.#.#####.#.#.#########.#.###.#.#
#.#...#.....#.......#.....#...#.......#.#.#...#...#...#.........#...#...#.#...#.#.........#.#.......#.#.#...#...#.........#.#.........#.....#.#.#...#.......#.#.....#.......#.#.....#
#.#.###.#.###.#.#.#.#.###.#########.#.#.#.#.#.#.###.#.#.#####.#.#.#####.#####.#.#.#########.#.#.#.###.#.#####.#.#.###.###.#.#.#.#.#.#####.###.#.###########.#.#.#.#.#.#######.#####.#
#........7#.....#.#...#.#.#.#.........#...#.#...........#.....#.......#.........#.........#.....#.......#.#.......#.#.#...#...#.#.#....6#.#.........................#...#.#.......#.#
#.###.#.###.###.###.#.#.###.###.#####.###.#.#.###.#####.###.#.###.#.#.#.#.#.#.#.#.#####.#.#.#####.#######.#.###.#.#.#####.#####.#.#.#.###.#.#.#.#.#####.#.#.###.#####.#.#.#.#.#.#.#.#
#.#...#.#...#.#.....#.#.......#...#.....#...#...#...#...#.#.....#...#.#...#...#...#.#.............#.............#.#...#.............#.#.....#.....#.......#.#.#.#.........#...#...#.#
#####################################################################################################################################################################################
5 changes: 5 additions & 0 deletions src/bfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ where
self
}

pub fn shortest_path_to(&mut self, target_position: POS) -> usize {
self.traverse_until_position(target_position.clone());
self.depth
}

pub fn traverse_until_depth(&mut self, target_depth: usize) -> &mut Self {
self.traverse(|_position, depth| depth > target_depth, true)
}
Expand Down
9 changes: 9 additions & 0 deletions src/year_2016.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod day_20;
pub mod day_21;
pub mod day_22;
pub mod day_23;
pub mod day_24;

pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
if part != 1 && part != 2 {
Expand Down Expand Up @@ -56,6 +57,7 @@ pub fn solve(day: u8, part: u8, input: impl Into<String>) -> Option<String> {
21 => Some(format!("{}", day_21::day_21(part, input))),
22 => Some(format!("{}", day_22::day_22(part, input))),
23 => Some(format!("{}", day_23::day_23(part, input))),
24 => Some(format!("{}", day_24::day_24(part, input))),
_ => None,
}
}
Expand Down Expand Up @@ -234,4 +236,11 @@ mod tests {
assert_eq!(day_23::day_23_v1(input), 13_776);
assert_eq!(day_23::day_23_v2(input), 479_010_336);
}

#[test]
fn day_24() {
let input = include_str!("../inputs/year_2016/day_24_input");
assert_eq!(day_24::day_24_v1(input), 430);
assert_eq!(day_24::day_24_v2(input), 700);
}
}
190 changes: 190 additions & 0 deletions src/year_2016/day_24.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
use crate::bfs::BreadthFirstSearch;
use itertools::Itertools;
use std::collections::HashMap;

struct Labyrinth {
width: usize,
height: usize,
cells: Vec<bool>,
numbers: Vec<(usize, usize)>,
}

impl Labyrinth {
fn new(input: &str) -> Self {
let height = input.lines().count();
let width = input.lines().next().unwrap().len();
let mut nums = vec![];
let mut cells = vec![true; height * width];
for (y, line) in input.lines().enumerate() {
for (x, character) in line.chars().enumerate() {
match character {
'.' => (),
'#' => cells[y * width + x] = false,
'0'..='9' => nums.push((character as u8 - 48, (x, y))),
_ => panic!("Invalid character: {}", character),
}
}
}
nums.sort_by_key(|elt| elt.0);
let numbers = nums.iter().map(|elt| elt.1).collect();
Labyrinth {
width,
height,
cells,
numbers,
}
}

pub fn next_moves(&self, position: (usize, usize)) -> Vec<(usize, usize)> {
let mut results = vec![];
if position.0 > 0 && self.cells[position.1 * self.width + (position.0 - 1)] {
results.push((position.0 - 1, position.1));
}
if position.0 < self.width && self.cells[position.1 * self.width + (position.0 + 1)] {
results.push((position.0 + 1, position.1));
}

if position.1 > 0 && self.cells[(position.1 - 1) * self.width + position.0] {
results.push((position.0, position.1 - 1));
}
if position.1 < self.height && self.cells[(position.1 + 1) * self.width + position.0] {
results.push((position.0, position.1 + 1));
}
results
}
}

#[inline]
fn get_distance(distances: &HashMap<(&usize, &usize), usize>, from: usize, to: usize) -> usize {
match distances.get(&(&from, &to)) {
Some(distance) => *distance,
None => panic!("No distance between {from} and {to}!"),
}
}

#[inline]
fn remove_position_from_vec(mut positions: Vec<usize>, needle: &usize) -> Vec<usize> {
positions.retain(|elt| elt != needle);
return positions;
}

fn best_path_v1(
distances: &HashMap<(&usize, &usize), usize>,
position: usize,
positions_left: Vec<usize>,
path: usize,
mut best_path: usize,
) -> usize {
if path > best_path {
return path;
}
if positions_left.is_empty() {
return path;
}
for next_position in positions_left.iter() {
let next_positions_left = remove_position_from_vec(positions_left.clone(), next_position);
let next_path = path + get_distance(&distances, position, *next_position);
let next_distance = best_path_v1(
&distances,
*next_position,
next_positions_left,
next_path,
best_path,
);
best_path = std::cmp::min(best_path, next_distance);
}

best_path
}

pub fn day_24_v1(input: impl Into<String>) -> usize {
let labyrinth = Labyrinth::new(&input.into());
let numbers: Vec<_> = (0..labyrinth.numbers.len()).collect();
let mut distances = HashMap::new();
let combos: Vec<_> = numbers.iter().combinations(2).collect();
combos.iter().for_each(|pair| {
let starter = labyrinth.numbers[*pair[0]];
let ending = labyrinth.numbers[*pair[1]];
let mut bfs = BreadthFirstSearch::new(starter, |position| labyrinth.next_moves(position));
let distance = bfs.shortest_path_to(ending);
distances.insert((pair[0], pair[1]), distance);
distances.insert((pair[1], pair[0]), distance);
});

best_path_v1(
&distances,
0,
(1..labyrinth.numbers.len()).collect(),
0,
usize::MAX,
)
}

fn best_path_v2(
distances: &HashMap<(&usize, &usize), usize>,
position: usize,
positions_left: Vec<usize>,
path: usize,
mut best_path: usize,
) -> usize {
if path > best_path {
return path;
}
if positions_left.is_empty() {
return path + get_distance(&distances, position, 0);
}
for next_position in positions_left.iter() {
let next_positions_left = remove_position_from_vec(positions_left.clone(), next_position);
let next_path = path + get_distance(&distances, position, *next_position);
let next_distance = best_path_v2(
&distances,
*next_position,
next_positions_left,
next_path,
best_path,
);
best_path = std::cmp::min(best_path, next_distance);
}

best_path
}

pub fn day_24_v2(input: impl Into<String>) -> usize {
let labyrinth = Labyrinth::new(&input.into());
let numbers: Vec<_> = (0..labyrinth.numbers.len()).collect();
let mut distances = HashMap::new();
let combos: Vec<_> = numbers.iter().combinations(2).collect();
combos.iter().for_each(|pair| {
let starter = labyrinth.numbers[*pair[0]];
let ending = labyrinth.numbers[*pair[1]];
let mut bfs = BreadthFirstSearch::new(starter, |position| labyrinth.next_moves(position));
let distance = bfs.shortest_path_to(ending);
distances.insert((pair[0], pair[1]), distance);
distances.insert((pair[1], pair[0]), distance);
});

best_path_v2(
&distances,
0,
(1..labyrinth.numbers.len()).collect(),
0,
usize::MAX,
)
}

solvable!(day_24, day_24_v1, day_24_v2, usize);

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

#[test]
fn works_with_samples_v1() {
let sample = "###########\n\
#0.1.....2#\n\
#.#######.#\n\
#4.......3#\n\
###########";
assert_eq!(day_24_v1(sample), 14);
}
}

0 comments on commit cf19c2c

Please sign in to comment.