Skip to content

Commit

Permalink
Day 17 puzzles
Browse files Browse the repository at this point in the history
  • Loading branch information
jhrcook committed Dec 28, 2023
1 parent 12d2d26 commit 6846024
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ num = "0.4.1"
petgraph = "0.6.4"
ndarray = "0.15.6"
cached = "0.46.1"
strum = { version = "0.25", features = ["derive", "strum_macros"] }
strum_macros = "0.25"
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
| 14 | [src/solutions/day14.rs](src/solutions/day14.rs) | ⭐️⭐️ |
| 15 | [src/solutions/day15.rs](src/solutions/day15.rs) | ⭐️⭐️ |
| 16 | [src/solutions/day16.rs](src/solutions/day16.rs) | ⭐️⭐️ |
<!-- | 17 | [src/solutions/day17.rs](src/solutions/day17.rs) | ⭐️⭐️ | -->
| 17 | [src/solutions/day17.rs](src/solutions/day17.rs) | ⭐️⭐️ |
<!-- | 18 | [src/solutions/day18.rs](src/solutions/day18.rs) | ⭐️⭐️ | -->
<!-- | 19 | [src/solutions/day19.rs](src/solutions/day19.rs) | ⭐️⭐️ | -->
<!-- | 20 | [src/solutions/day20.rs](src/solutions/day20.rs) | ⭐️⭐️ | -->
Expand All @@ -34,8 +34,12 @@
<!-- | 23 | [src/solutions/day23.rs](src/solutions/day23.rs) | ⭐️⭐️ | -->
<!-- | 24 | [src/solutions/day24.rs](src/solutions/day24.rs) | ⭐️⭐️ | -->

## Help

Used this Reddit post for Day 12 part 2: <https://www.reddit.com/r/adventofcode/comments/18hbbxe/2023_day_12python_stepbystep_tutorial_with_bonus/>

I implemented the algorithm described in this Reddit [post](https://www.reddit.com/r/adventofcode/comments/18k9ne5/comment/kdqp7jx/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button) for Day 17 ([Python code](https://topaz.github.io/paste/#XQAAAQCcAgAAAAAAAAAzHIoib6py7i/yVWhl9dSCjkM4GPHrtd8B89i40hrslkM0i9r9hxir+33uzZa01Y7HY/oCJMAoqvX6OLOsC224RKbiSlSU1U6sDn8KTPKvoKhtJCgPq4QDeXZl8oKQCyrAOP0g3rHGLO7gde4rqBUfOeNyypDl5CSgMF0ceJifzUGjB1OliVqXbO/Ydsmg77dYyTbWx89UvPTsZiijfyTYH7ybEz1UtsTx6VHFZ5zcAVDl7ClaQ7+4gn7tShBjy8XQ0s9XR6uWqgo3vPVBjj9Bf3UGCWSP8qYw9N4dZcLcnLbZQgRkBbK2s9a0Cl0XXD0ie4lmMxzz3pLE3i7GFFzUEv9/dNRee0hFwDsxRBK7ERsb8Xt+mYS+fyiltY71gJKfELcn9Eu3TJy2kI/k2o4YHLSGL20gXFLoE4CunCJ2f6iLMbyU/+WeeKUMZn4BQ72S3uZ4SAD9wV+H44NtZEq0I6qYBCrLX98ODOc6lTFAPjJWFmvpIv74Evkb))

## Setup

```bash
Expand Down
141 changes: 141 additions & 0 deletions puzzle-input/17.txt

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ pub fn run_day(data_dir: &str, day: &u32) -> Result<(), Error> {
solutions::day16::main(data_dir);
Ok(())
}
17 => {
solutions::day17::main(data_dir);
Ok(())
}
// <-- INSERT NEW DAY HERE -->
_ => Err(Error::DayNotImplemented(*day)),
}
Expand Down
145 changes: 145 additions & 0 deletions src/solutions/day17.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use crate::data::load;
use num::Complex;
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap, HashSet};
use thiserror::Error;

#[derive(Error, Debug, PartialEq, Eq)]
pub enum PuzzleErr {
#[error("Integer parsing error.")]
ParseIntError(#[from] std::num::ParseIntError),
}

fn parse_grid(input: &str) -> Result<HashMap<Complex<i32>, i32>, PuzzleErr> {
let mut grid = HashMap::new();
for (i, r) in input.trim().lines().enumerate() {
for (j, c) in r.trim().chars().enumerate() {
grid.insert(
Complex::<i32>::new(i as i32, j as i32),
c.to_string().parse::<i32>()?,
);
}
}
Ok(grid)
}

#[derive(Copy, Clone, Eq, PartialEq)]
struct State {
coord: Complex<i32>,
prev_dir: Complex<i32>,
score: i32,
}

impl Ord for State {
fn cmp(&self, other: &Self) -> Ordering {
other
.score
.cmp(&self.score)
.then_with(|| self.coord.re.cmp(&other.coord.re))
.then_with(|| self.coord.im.cmp(&other.coord.im))
}
}

impl PartialOrd for State {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

fn turns(d: Complex<i32>) -> Vec<Complex<i32>> {
Vec::from_iter([Complex { re: 0, im: 1 } / d, Complex { re: 0, im: -1 } / d])
}

fn shortest_path(
start: &Complex<i32>,
end: &Complex<i32>,
grid: &HashMap<Complex<i32>, i32>,
min_steps: i32,
max_steps: i32,
) -> i32 {
let mut queue = BinaryHeap::<State>::from_iter([
State {
coord: *start,
prev_dir: Complex { re: 1, im: 0 },
score: 0,
},
State {
coord: *start,
prev_dir: Complex { re: 0, im: 1 },
score: 0,
},
]);
let mut visited = HashSet::<(Complex<i32>, Complex<i32>)>::new();
while !queue.is_empty() {
let state = queue.pop().unwrap();
if &state.coord == end {
return state.score;
}
if visited.contains(&(state.coord, state.prev_dir)) {
continue;
}
visited.insert((state.coord, state.prev_dir));

for next_dir in turns(state.prev_dir) {
for i in min_steps..=max_steps {
if grid.contains_key(&(state.coord + (next_dir * i))) {
let y: i32 = (1..=i)
.map(|j| grid.get(&(state.coord + next_dir * j)).unwrap())
.sum();
let new_state = State {
coord: state.coord + next_dir * i,
prev_dir: next_dir,
score: state.score + y,
};
queue.push(new_state);
}
}
}
}
unreachable!();
}

pub fn puzzle_1(input: &str) -> Result<i32, PuzzleErr> {
let grid = parse_grid(input)?;
let start = Complex { re: 0, im: 0 };
let end_re = grid.keys().map(|x| x.re).max().unwrap();
let end_im = grid.keys().map(|x| x.im).max().unwrap();
let end = Complex {
re: end_re,
im: end_im,
};
Ok(shortest_path(&start, &end, &grid, 1, 3))
}

pub fn puzzle_2(input: &str) -> Result<i32, PuzzleErr> {
let grid = parse_grid(input)?;
let start = Complex { re: 0, im: 0 };
let end_re = grid.keys().map(|x| x.re).max().unwrap();
let end_im = grid.keys().map(|x| x.im).max().unwrap();
let end = Complex {
re: end_re,
im: end_im,
};
Ok(shortest_path(&start, &end, &grid, 4, 10))
}

pub fn main(data_dir: &str) {
println!("Day 17: Clumsy Crucible");
let data = load(data_dir, 17, None);

// Puzzle 1.
let answer_1 = puzzle_1(&data);
match answer_1 {
Ok(x) => println!(" Puzzle 1: {}", x),
Err(e) => panic!("No solution to puzzle 1: {}.", e),
}
assert_eq!(answer_1, Ok(1076));

// Puzzle 2.
let answer_2 = puzzle_2(&data);
match answer_2 {
Ok(x) => println!(" Puzzle 2: {}", x),
Err(e) => panic!("No solution to puzzle 2: {}", e),
}
assert_eq!(answer_2, Ok(1219))
}
1 change: 1 addition & 0 deletions src/solutions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
pub mod day17;
43 changes: 43 additions & 0 deletions tests/test_day17.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use aoc_2023::solutions::day17::{puzzle_1, puzzle_2};

const EXAMPLE_INPUT_1: &str = "
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533
";

#[test]
fn puzzle_1_example_1() {
let _ = env_logger::try_init();
assert_eq!(puzzle_1(self::EXAMPLE_INPUT_1), Ok(102));
}

#[test]
fn puzzle_2_example_1() {
let _ = env_logger::try_init();
assert_eq!(puzzle_2(self::EXAMPLE_INPUT_1), Ok(94));
}

const EXAMPLE_INPUT_2: &str = "
111111111111
999999999991
999999999991
999999999991
999999999991
";

#[test]
fn puzzle_2_example_2() {
let _ = env_logger::try_init();
assert_eq!(puzzle_2(self::EXAMPLE_INPUT_2), Ok(71));
}

0 comments on commit 6846024

Please sign in to comment.