Skip to content

Commit 74f82aa

Browse files
committed
feat(2024): add day 10 in rust
1 parent 431059e commit 74f82aa

File tree

6 files changed

+404
-5
lines changed

6 files changed

+404
-5
lines changed

rust/2024/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ Main solutions are in [Go](https://github.com/believer/advent-of-code/tree/maste
77
| Day | #1 | #1 Answer | #2 | #2 Answer |
88
| ----------------------------------------------------------------------------------------------------------- | --- | --------: | --- | --------: |
99
| [Day 1: Historian Hysteria](https://github.com/believer/advent-of-code/blob/master/rust/2024/src/day_01.rs) | 🌟 | 1666427 | 🌟 | 24316233 |
10+
| [Day 10: Hoof It](https://github.com/believer/advent-of-code/blob/master/rust/2024/src/day_10.rs) | 🌟 | 652 | 🌟 | 1432 |
1011

1112
## Performance
1213

13-
| Day | #1 | #2 |
14-
| --- | -------: | -------: |
15-
| 1 | 13.29 µs | 32.42 µs |
14+
| Day | #1 | #2 |
15+
| --- | --------: | --------: |
16+
| 1 | 13.29 µs | 32.42 µs |
17+
| 2 | 402.46 µs | 536.40 µs |

rust/2024/src/day_01.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// Day 1: Trebuchet?!
2-
31
use std::collections::HashMap;
42

53
#[aoc_generator(day1)]

rust/2024/src/day_10.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use crate::{
2+
grid::Grid,
3+
point::{Point, CARDINALS},
4+
};
5+
use std::collections::{HashSet, VecDeque};
6+
7+
pub struct Input {
8+
grid: Grid<u8>,
9+
starts: Vec<Point>,
10+
}
11+
12+
// I really enjoy the VecDeque API so I wanted to do this day here as well.
13+
// This together with my Grid/Point helpers from last year makes for some
14+
// really nice code :D
15+
#[aoc_generator(day10)]
16+
pub fn input_generator(input: &str) -> Input {
17+
let grid: Grid<u8> = Grid::from(input);
18+
let trailheads = grid.find_all(b'0');
19+
20+
Input {
21+
grid,
22+
starts: trailheads,
23+
}
24+
}
25+
26+
fn calculate_trail_score(grid: &Grid<u8>, start: &Point, is_distinct: bool) -> usize {
27+
let mut trail_score = 0;
28+
let mut visited: HashSet<Point> = HashSet::new();
29+
let mut queue = VecDeque::new();
30+
31+
queue.push_back(*start);
32+
33+
while let Some(location) = queue.pop_front() {
34+
let current_point = grid[location];
35+
36+
if visited.contains(&location) && is_distinct {
37+
continue;
38+
}
39+
40+
visited.insert(location);
41+
42+
if current_point == b'9' {
43+
trail_score += 1;
44+
continue;
45+
}
46+
47+
for direction in CARDINALS {
48+
let next = location + direction;
49+
50+
if grid.contains(next) && grid[next] == current_point + 1 {
51+
queue.push_back(next);
52+
}
53+
}
54+
}
55+
56+
trail_score
57+
}
58+
59+
#[aoc(day10, part1)]
60+
pub fn solve_part_01(input: &Input) -> usize {
61+
let Input { grid, starts } = input;
62+
63+
starts
64+
.iter()
65+
.map(|start| calculate_trail_score(grid, start, true))
66+
.sum()
67+
}
68+
69+
#[aoc(day10, part2)]
70+
pub fn solve_part_02(input: &Input) -> usize {
71+
let Input { grid, starts } = input;
72+
73+
starts
74+
.iter()
75+
.map(|start| calculate_trail_score(grid, start, false))
76+
.sum()
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::*;
82+
83+
const DATA: &str = "89010123
84+
78121874
85+
87430965
86+
96549874
87+
45678903
88+
32019012
89+
01329801
90+
10456732";
91+
92+
#[test]
93+
fn sample_01() {
94+
assert_eq!(solve_part_01(&input_generator(DATA)), 36)
95+
}
96+
97+
#[test]
98+
fn sample_02() {
99+
assert_eq!(solve_part_02(&input_generator(DATA)), 81)
100+
}
101+
}

rust/2024/src/grid.rs

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use std::{
2+
fmt::{Display, Formatter},
3+
ops::{Index, IndexMut},
4+
};
5+
6+
use crate::point::Point;
7+
8+
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
9+
pub struct Grid<T> {
10+
pub width: i32,
11+
pub height: i32,
12+
pub data: Vec<T>,
13+
}
14+
15+
impl<T: From<u8> + Copy> From<&str> for Grid<T> {
16+
fn from(value: &str) -> Self {
17+
let raw_data = value
18+
.lines()
19+
.flat_map(|line| line.bytes().filter_map(|b| T::from(b).into()))
20+
.collect::<Vec<_>>();
21+
let width = value.lines().next().unwrap_or_default().len() as i32;
22+
let height = value.lines().count() as i32;
23+
24+
Grid {
25+
width,
26+
height,
27+
data: raw_data,
28+
}
29+
}
30+
}
31+
32+
impl<T: Copy + PartialEq> Grid<T> {
33+
pub fn new(width: i32, height: i32, data: Vec<T>) -> Self {
34+
Grid {
35+
width,
36+
height,
37+
data,
38+
}
39+
}
40+
41+
/// Find _one_ point that has the given value in the grid.
42+
///
43+
/// # Example
44+
///
45+
/// ```
46+
/// use advent_of_code_2023::grid::Grid;
47+
/// use advent_of_code_2023::point::Point;
48+
///
49+
/// let data = "...#
50+
/// .#S.
51+
/// ..#.";
52+
///
53+
/// let grid: Grid<u8> = Grid::from(data);
54+
/// let point = grid.find(b'S');
55+
///
56+
/// assert_eq!(point, Some(Point { x: 2, y: 1 }))
57+
/// ```
58+
pub fn find(&self, value: T) -> Option<Point> {
59+
self.data
60+
.iter()
61+
.position(|&x| x == value)
62+
.map(|i| Point::new((i as i32) % self.width, (i as i32) / self.width))
63+
}
64+
65+
/// Find all points that contain the given value in the grid.
66+
///
67+
/// # Example
68+
///
69+
/// ```
70+
/// use advent_of_code_2023::grid::Grid;
71+
///
72+
/// let data = "...#
73+
/// .#..
74+
/// ..#.";
75+
///
76+
/// let grid: Grid<u8> = Grid::from(data);
77+
/// let points = grid.find_all(b'#');
78+
///
79+
/// assert_eq!(points.len(), 3);
80+
/// ```
81+
pub fn find_all(&self, value: T) -> Vec<Point> {
82+
self.data
83+
.iter()
84+
.enumerate()
85+
.filter_map(|(i, &x)| if x == value { Some(i) } else { None })
86+
.map(|i| Point::new((i as i32) % self.width, (i as i32) / self.width))
87+
.collect()
88+
}
89+
90+
/// Find if the grid contains a point
91+
///
92+
/// # Example
93+
///
94+
/// ```
95+
/// use advent_of_code_2023::grid::Grid;
96+
/// use advent_of_code_2023::point::Point;
97+
///
98+
/// let data = "...#
99+
/// .#..
100+
/// ..#.";
101+
///
102+
/// let grid: Grid<u8> = Grid::from(data);
103+
///
104+
/// let existing_point = Point::new(2, 1);
105+
/// let non_existing_point = Point::new(3, 3);
106+
///
107+
/// assert!(grid.contains(existing_point));
108+
/// assert!(!grid.contains(non_existing_point));
109+
/// ```
110+
pub fn contains(&self, point: Point) -> bool {
111+
point.x >= 0 && point.x < self.width && point.y >= 0 && point.y < self.height
112+
}
113+
114+
/// Swap the values of two points in the grid
115+
///
116+
/// # Example
117+
///
118+
/// ```
119+
/// use advent_of_code_2023::grid::Grid;
120+
/// use advent_of_code_2023::point::Point;
121+
///
122+
/// let data = "...#
123+
/// .#..
124+
/// ..#.";
125+
///
126+
/// let mut grid: Grid<u8> = Grid::from(data);
127+
///
128+
/// let point_a = Point::new(3, 0);
129+
/// let point_b = Point::new(2, 0);
130+
///
131+
/// assert_eq!(grid[point_a], b'#');
132+
/// assert_eq!(grid[point_b], b'.');
133+
///
134+
/// grid.swap(point_a, point_b);
135+
///
136+
/// assert_eq!(grid[point_a], b'.');
137+
/// assert_eq!(grid[point_b], b'#');
138+
/// ```
139+
pub fn swap(&mut self, a: Point, b: Point) {
140+
let a = (self.width * a.y + a.x) as usize;
141+
let b = (self.width * b.y + b.x) as usize;
142+
143+
self.data.swap(a, b);
144+
}
145+
}
146+
147+
/// Used to get a value using a Point as index from the grid
148+
///
149+
/// ```
150+
/// use advent_of_code_2023::grid::Grid;
151+
/// use advent_of_code_2023::point::Point;
152+
///
153+
/// let grid: Grid<u8> = Grid::from(".#..");
154+
/// assert_eq!(grid[Point::new(1, 0)], b'#');
155+
/// ```
156+
impl<T> Index<Point> for Grid<T> {
157+
type Output = T;
158+
159+
#[inline]
160+
fn index(&self, point: Point) -> &Self::Output {
161+
&self.data[(self.width * point.y + point.x) as usize]
162+
}
163+
}
164+
165+
/// Used to set a value using a Point as index on the grid
166+
///
167+
/// ```
168+
/// use advent_of_code_2023::grid::Grid;
169+
/// use advent_of_code_2023::point::Point;
170+
///
171+
/// let mut grid: Grid<u8> = Grid::from("....");
172+
/// let point = Point::new(1,0);
173+
///
174+
/// grid[point] = b'#';
175+
///
176+
/// assert_eq!(grid[point], b'#');
177+
/// ```
178+
impl<T> IndexMut<Point> for Grid<T> {
179+
fn index_mut(&mut self, point: Point) -> &mut Self::Output {
180+
&mut self.data[(self.width * point.y + point.x) as usize]
181+
}
182+
}
183+
184+
/// Used for debugging a grid visually.
185+
impl Display for Grid<u8> {
186+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187+
for y in 0..self.height {
188+
for x in 0..self.width {
189+
write!(f, "{}", self[Point::new(x, y)] as char)?;
190+
}
191+
writeln!(f)?;
192+
}
193+
194+
Ok(())
195+
}
196+
}

rust/2024/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ extern crate aoc_runner;
33
#[macro_use]
44
extern crate aoc_runner_derive;
55

6+
pub mod grid;
7+
pub mod point;
8+
69
pub mod day_01;
10+
pub mod day_10;
711

812
aoc_lib! { year = 2024 }

0 commit comments

Comments
 (0)