Skip to content

Commit

Permalink
feat(20/2024): solve second part
Browse files Browse the repository at this point in the history
  • Loading branch information
manhunto committed Dec 21, 2024
1 parent 7c44ca0 commit 687d376
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 35 deletions.
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
| [Day 17: Chronospatial Computer](src/solutions/year2024/day17.rs) | - | - | - |
| [Day 18: RAM Run](src/solutions/year2024/day18.rs) | ⭐⭐ | 2.487 | 204.885 |
| [Day 19: Linen Layout](src/solutions/year2024/day19.rs) | ⭐⭐ | 2.923 | 22.751 |
| [Day 20: Race Condition](src/solutions/year2024/day20.rs) | | 7.355 | - |
| [Day 20: Race Condition](src/solutions/year2024/day20.rs) | | 7.355 | 346.481 |

# 2023

Expand Down
2 changes: 1 addition & 1 deletion src/solutions/year2024/day18.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl Default for Day18 {
impl Day18 {
fn new(grid_size: usize, memory_size: usize) -> Self {
Self {
surface: SurfaceRange::from_points(0, grid_size as isize, 0, grid_size as isize),
surface: SurfaceRange::square(grid_size as isize),
memory_size,
}
}
Expand Down
104 changes: 72 additions & 32 deletions src/solutions/year2024/day20.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::solutions::Solution;
use crate::utils::deltoid_surface::DeltoidSurface;
use crate::utils::grid::Grid;
use crate::utils::point::Point;
use itertools::Itertools;
Expand All @@ -9,18 +10,46 @@ pub struct Day20;

impl Solution for Day20 {
fn part_one(&self, input: &str) -> String {
self.cheats_in_range(input, 100..).to_string()
self.part_one_cheats_in_range(input, 100..).to_string()
}

fn part_two(&self, _input: &str) -> String {
String::from('0')
fn part_two(&self, input: &str) -> String {
// extract surface trait and contains method
// grid has function get every position in area
// rename surface range as rectangular area
//
self.part_two_cheats_in_range(input, 100..).to_string()
}
}

impl Day20 {
fn cheats_in_range<R>(&self, input: &str, range: R) -> usize
fn part_one_cheats_in_range(&self, input: &str, range: impl RangeBounds<usize>) -> usize {
let cheat_range_from_current = |current: Point| {
current
.adjacent_vectors()
.map(|v| v.forward().position())
.into_iter()
};

self.cheats_in_range(input, range, &cheat_range_from_current)
}

fn part_two_cheats_in_range(&self, input: &str, range: impl RangeBounds<usize>) -> usize {
let cheat_range_from_current =
|current: Point| DeltoidSurface::new(current, 20).points().into_iter();

self.cheats_in_range(input, range, &cheat_range_from_current)
}

fn cheats_in_range<R, I>(
&self,
input: &str,
range: R,
cheat_positions: &dyn Fn(Point) -> I,
) -> usize
where
R: RangeBounds<usize>,
I: Iterator<Item = Point>,
{
let grid: Grid<char> = Grid::from(input);
let start = grid.get_first_position(&'S').unwrap();
Expand All @@ -32,30 +61,30 @@ impl Day20 {
.path()
.iter()
.flat_map(|(current_time, current)| {
current
.adjacent_vectors()
.iter()
.filter(|v| grid.is_for_point(&v.position(), '#'))
.map(|p| p.forward())
cheat_positions(*current)
.filter(|v| {
grid.get_for_point(&v.position())
grid.get_for_point(v)
.is_some_and(|element| ['.', 'E'].contains(element))
})
.filter_map(|v| {
if let Some(time_after_cheat) =
path_without_cheats.picoseconds_from(v.position())
{
if time_after_cheat > *current_time {
return Some(time_after_cheat - current_time - 2);
// why -2
.filter_map(|cheat_position| {
let time_after_cheat = path_without_cheats
.picoseconds_from(cheat_position)
.unwrap();
let cheat_cost = current.manhattan_distance(&cheat_position) as usize;

if time_after_cheat > *current_time + cheat_cost {
let time = time_after_cheat - current_time - cheat_cost;
if range.contains(&time) {
return Some(time);
}
}

None
None
} else {
None
}
})
.collect_vec()
})
.filter(|time| range.contains(time))
.count()
}

Expand Down Expand Up @@ -126,17 +155,28 @@ mod tests {
###############"#;

#[test]
fn test_solve() {
assert_eq!(14, Day20.cheats_in_range(EXAMPLE, 2..=2));
assert_eq!(14, Day20.cheats_in_range(EXAMPLE, 4..=4));
assert_eq!(2, Day20.cheats_in_range(EXAMPLE, 6..=6));
assert_eq!(4, Day20.cheats_in_range(EXAMPLE, 8..=8));
assert_eq!(2, Day20.cheats_in_range(EXAMPLE, 10..=10));
assert_eq!(3, Day20.cheats_in_range(EXAMPLE, 12..=12));
assert_eq!(1, Day20.cheats_in_range(EXAMPLE, 20..=20));
assert_eq!(1, Day20.cheats_in_range(EXAMPLE, 36..=36));
assert_eq!(1, Day20.cheats_in_range(EXAMPLE, 38..=38));
assert_eq!(1, Day20.cheats_in_range(EXAMPLE, 40..=40));
assert_eq!(1, Day20.cheats_in_range(EXAMPLE, 64..=64));
fn part_one_cheats_in_range() {
assert_eq!(14, Day20.part_one_cheats_in_range(EXAMPLE, 2..=2));
assert_eq!(14, Day20.part_one_cheats_in_range(EXAMPLE, 4..=4));
assert_eq!(2, Day20.part_one_cheats_in_range(EXAMPLE, 6..=6));
assert_eq!(4, Day20.part_one_cheats_in_range(EXAMPLE, 8..=8));
assert_eq!(2, Day20.part_one_cheats_in_range(EXAMPLE, 10..=10));
assert_eq!(3, Day20.part_one_cheats_in_range(EXAMPLE, 12..=12));
assert_eq!(1, Day20.part_one_cheats_in_range(EXAMPLE, 20..=20));
assert_eq!(1, Day20.part_one_cheats_in_range(EXAMPLE, 36..=36));
assert_eq!(1, Day20.part_one_cheats_in_range(EXAMPLE, 38..=38));
assert_eq!(1, Day20.part_one_cheats_in_range(EXAMPLE, 40..=40));
assert_eq!(1, Day20.part_one_cheats_in_range(EXAMPLE, 64..=64));
}

#[test]
fn part_two_cheats_in_range() {
assert_eq!(32, Day20.part_two_cheats_in_range(EXAMPLE, 50..=50));
assert_eq!(31, Day20.part_two_cheats_in_range(EXAMPLE, 52..=52));
assert_eq!(29, Day20.part_two_cheats_in_range(EXAMPLE, 54..=54));
assert_eq!(39, Day20.part_two_cheats_in_range(EXAMPLE, 56..=56));
assert_eq!(25, Day20.part_two_cheats_in_range(EXAMPLE, 58..=58));
assert_eq!(23, Day20.part_two_cheats_in_range(EXAMPLE, 60..=60));
assert_eq!(20, Day20.part_two_cheats_in_range(EXAMPLE, 62..=62));
}
}
102 changes: 102 additions & 0 deletions src/utils/deltoid_surface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use crate::utils::point::Point;
use crate::utils::range::Range;
use std::collections::HashMap;

type Ranges = HashMap<isize, Range>;

pub struct DeltoidSurface {
#[allow(dead_code)]
point: Point,
#[allow(dead_code)]
distance: usize,
#[allow(dead_code)]
ranges: Ranges,
points: Vec<Point>,
}

impl DeltoidSurface {
pub fn new(point: Point, distance: usize) -> Self {
let ranges = Self::build_ranges(point, distance);
let points = Self::build_points(&ranges);

Self {
point,
distance,
ranges,
points,
}
}

pub fn points(&self) -> Vec<Point> {
self.points.clone()
}

fn build_ranges(point: Point, distance: usize) -> Ranges {
let isize_distance = distance as isize;

let mut ranges: Ranges = Ranges::with_capacity(distance * 2 + 1);

for x in -isize_distance..=isize_distance {
let height_diff = isize_distance - x.abs();
let x = point.x + x;
let y_range = Range::new(point.y - height_diff, point.y + height_diff).unwrap();

ranges.insert(x, y_range);
}

ranges
}

fn build_points(ranges: &Ranges) -> Vec<Point> {
ranges
.iter()
.flat_map(|(x, y_range)| {
y_range
.iter()
.map(|y| Point::new(*x, y))
.collect::<Vec<Point>>()
})
.collect()
}
}

#[cfg(test)]
mod tests {
use crate::utils::deltoid_surface::DeltoidSurface;
use crate::utils::grid::Grid;
use crate::utils::point::Point;
use crate::utils::surface_range::SurfaceRange;

#[test]
fn deltoid_surface_visual_test() {
const EXPECTED: &str = r#"........
....#...
...###..
..#####.
...###..
....#...
........
........"#;

let expected_grid: Grid<char> = Grid::from(EXPECTED);
let surface = DeltoidSurface::new(Point::new(4, 3), 2);

let mut current_grid: Grid<char> = Grid::filled(SurfaceRange::square(7), '.');
current_grid.modify_many(surface.points(), '#');

assert_eq!(expected_grid.to_string(), current_grid.to_string());
}

#[test]
fn manhattan_distance_is_at_most_distance() {
let middle = Point::new(4, 3);
let distance = 3;

let surface = DeltoidSurface::new(middle, distance);

assert!(surface
.points
.iter()
.all(|p| p.manhattan_distance(&middle) <= distance as isize));
}
}
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod chain_pattern_finder;
pub mod deltoid_surface;
pub mod direction;
pub mod graphs;
pub mod grid;
Expand Down
6 changes: 5 additions & 1 deletion src/utils/surface_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ impl SurfaceRange {
)
}

pub fn square(size: isize) -> Self {
Self::from_points(0, size, 0, size)
}

pub fn x(&self) -> Range {
self.x_range
}
Expand Down Expand Up @@ -89,7 +93,7 @@ mod tests {

#[test]
fn shrink() {
let surface_range = SurfaceRange::from_points(0, 10, 0, 10);
let surface_range = SurfaceRange::square(10);
let shrunk = surface_range._shrink(1);

let expected = SurfaceRange::from_points(1, 9, 1, 9);
Expand Down

0 comments on commit 687d376

Please sign in to comment.