-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
78bc780
commit e5aa40c
Showing
9 changed files
with
409 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "day20" | ||
version = "0.1.0" | ||
edition = "2024" | ||
|
||
[dependencies] | ||
aoc_derive.path = '../aoc_derive' | ||
utils.path = '../utils' | ||
derive_more.workspace = true | ||
itertools.workspace = true | ||
lazy-regex.workspace = true | ||
parse-display.workspace = true | ||
rayon.workspace = true | ||
regex.workspace = true | ||
num.workspace = true | ||
|
||
[dev-dependencies] | ||
pretty_assertions.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
use aoc_derive::aoc_main; | ||
use derive_more::derive::{Deref, DerefMut, From}; | ||
use graphs::{UnweightedGraph, bfs, floodfill}; | ||
use grid::Grid; | ||
use math::Vec2D; | ||
use utils::*; | ||
|
||
#[derive(Debug, Clone, From, Deref, DerefMut)] | ||
struct Racetrack(Grid<char>); | ||
|
||
impl UnweightedGraph for Racetrack { | ||
type Node = Vec2D; | ||
|
||
fn neighbors<'a, 'b: 'a>(&'a self, node: &'b Vec2D) -> impl Iterator<Item = Vec2D> + 'a { | ||
self.orthogonal_neighbors(node).filter(|&n| self[n] != '#') | ||
} | ||
} | ||
|
||
#[aoc_main(100)] | ||
fn solve(input: Input, min_save: usize) -> impl Into<Solution> { | ||
let track: Racetrack = input.char_grid().into(); | ||
let start = track.find_position(&'S').unwrap(); | ||
let end = track.find_position(&'E').unwrap(); | ||
|
||
let best_without_cheat = bfs(&track, start, end).distance.unwrap(); | ||
|
||
let distance_from_start = floodfill(&track, start); | ||
let distance_from_end = floodfill(&track, end); | ||
|
||
let find_cheats = |pos: Vec2D, max_cheat: usize| { | ||
track | ||
.iter() | ||
.filter_map(|(pos_after_cheat, &c)| { | ||
let dist = (pos - pos_after_cheat).manhattan_dist(); | ||
(c != '#' && dist <= max_cheat).then_some((pos_after_cheat, dist)) | ||
}) | ||
.filter(|&(pos_after_cheat, dist)| { | ||
let total_dist = | ||
distance_from_start[&pos] + dist + distance_from_end[&pos_after_cheat]; | ||
total_dist < best_without_cheat && best_without_cheat - total_dist >= min_save | ||
}) | ||
.count() | ||
}; | ||
|
||
( | ||
track.iter().filter(|&(_, &c)| c != '#').map(|(pos, _)| find_cheats(pos, 2)).sum_usize(), | ||
track.iter().filter(|&(_, &c)| c != '#').map(|(pos, _)| find_cheats(pos, 20)).sum_usize(), | ||
) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
#[test] | ||
fn test_examples() { | ||
assert_eq!( | ||
solve( | ||
r#"############### | ||
#...#...#.....# | ||
#.#.#.#.#.###.# | ||
#S#...#.#.#...# | ||
#######.#.#.### | ||
#######.#.#...# | ||
#######.#.###.# | ||
###..E#...#...# | ||
###.#######.### | ||
#...###...#...# | ||
#.#####.#.###.# | ||
#.#...#.#.#...# | ||
#.#.#.#.#.#.### | ||
#...#...#...### | ||
###############"# | ||
.into(), | ||
0 | ||
) | ||
.into() | ||
.part1, | ||
Some((14 + 14 + 2 + 4 + 2 + 3 + 5).to_string()) | ||
); | ||
|
||
assert_eq!( | ||
solve( | ||
r#"############### | ||
#...#...#.....# | ||
#.#.#.#.#.###.# | ||
#S#...#.#.#...# | ||
#######.#.#.### | ||
#######.#.#...# | ||
#######.#.###.# | ||
###..E#...#...# | ||
###.#######.### | ||
#...###...#...# | ||
#.#####.#.###.# | ||
#.#...#.#.#...# | ||
#.#.#.#.#.#.### | ||
#...#...#...### | ||
###############"# | ||
.into(), | ||
50 | ||
) | ||
.into() | ||
.part2, | ||
Some((32 + 31 + 29 + 39 + 25 + 23 + 20 + 19 + 12 + 14 + 12 + 22 + 4 + 3).to_string()) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "day21" | ||
version = "0.1.0" | ||
edition = "2024" | ||
|
||
[dependencies] | ||
aoc_derive.path = '../aoc_derive' | ||
utils.path = '../utils' | ||
derive_more.workspace = true | ||
itertools.workspace = true | ||
lazy-regex.workspace = true | ||
parse-display.workspace = true | ||
rayon.workspace = true | ||
regex.workspace = true | ||
num.workspace = true | ||
|
||
[dev-dependencies] | ||
pretty_assertions.workspace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
use std::{ | ||
collections::{BTreeMap, HashMap}, | ||
iter::{once, repeat, repeat_n}, | ||
}; | ||
|
||
use aoc_derive::aoc_main; | ||
use graphs::{WeightedGraph, dijkstra}; | ||
use itertools::{Itertools, iproduct}; | ||
use math::Vec2D; | ||
use utils::*; | ||
|
||
#[derive(Debug, Clone)] | ||
struct KeypadRobot { | ||
pos: Vec2D, | ||
num_dirpads: usize, | ||
} | ||
|
||
fn check_path(start: Vec2D, path: &[char], forbidden: Vec2D) -> bool { | ||
path.iter() | ||
.try_fold(start, |pos, c| { | ||
(pos != forbidden).then_some( | ||
pos + Vec2D::from(match c { | ||
'<' => (-1, 0), | ||
'>' => (1, 0), | ||
'^' => (0, -1), | ||
'v' => (0, 1), | ||
_ => unreachable!(), | ||
}), | ||
) | ||
}) | ||
.is_some() | ||
} | ||
|
||
impl KeypadRobot { | ||
fn new(num_dirpads: usize) -> Self { | ||
Self { pos: Self::coord('A'), num_dirpads } | ||
} | ||
|
||
fn coord(c: char) -> Vec2D { | ||
match c { | ||
'7' => (0, 0), | ||
'8' => (1, 0), | ||
'9' => (2, 0), | ||
'4' => (0, 1), | ||
'5' => (1, 1), | ||
'6' => (2, 1), | ||
'1' => (0, 2), | ||
'2' => (1, 2), | ||
'3' => (2, 2), | ||
'0' => (1, 3), | ||
'A' => (2, 3), | ||
_ => unreachable!(), | ||
} | ||
.into() | ||
} | ||
|
||
fn expand_move(&self, from: char, to: char) -> usize { | ||
(0..self.num_dirpads) | ||
.fold([(make_move(from, to), 1)].into_iter().collect(), |path, _| expand_path(path)) | ||
.into_iter() | ||
.map(|(path, count)| path.len() * count) | ||
.sum() | ||
} | ||
|
||
fn enter_char(&mut self, c: char) -> usize { | ||
let dist = | ||
dijkstra(self, [(self.pos, 'A', false)], |&node| node == (Self::coord(c), 'A', true)); | ||
|
||
self.pos = Self::coord(c); | ||
dist.unwrap() | ||
} | ||
|
||
fn enter_code(&mut self, code: &str) -> usize { | ||
code.chars().map(|c| self.enter_char(c)).sum() | ||
} | ||
} | ||
|
||
impl WeightedGraph for KeypadRobot { | ||
type Node = (Vec2D, char, bool); | ||
|
||
fn neighbors<'a, 'b: 'a>( | ||
&'a self, | ||
&(pos, keypad_pos, has_pushed): &'b Self::Node, | ||
) -> impl Iterator<Item = (Self::Node, graphs::Cost)> + 'a { | ||
if has_pushed { | ||
vec![].into_iter() | ||
} else { | ||
[ | ||
Some(((pos, 'A', true), self.expand_move(keypad_pos, 'A'))), | ||
(pos.x < 2) | ||
.then_some(((pos + (1, 0), '>', false), self.expand_move(keypad_pos, '>'))), | ||
(pos.x > 0 && pos != (1, 3)) | ||
.then_some(((pos + (-1, 0), '<', false), self.expand_move(keypad_pos, '<'))), | ||
(pos.y < 3 && pos != (0, 2)) | ||
.then_some(((pos + (0, 1), 'v', false), self.expand_move(keypad_pos, 'v'))), | ||
(pos.y > 0) | ||
.then_some(((pos + (0, -1), '^', false), self.expand_move(keypad_pos, '^'))), | ||
] | ||
.into_iter() | ||
.flatten() | ||
.collect_vec() | ||
.into_iter() | ||
} | ||
} | ||
} | ||
|
||
fn shortest_sequence(code: &str, num_dirpads: usize) -> usize { | ||
KeypadRobot::new(num_dirpads).enter_code(code) | ||
} | ||
|
||
fn complexity(code: &str, num_dirpads: usize) -> usize { | ||
println!("check code {code}"); | ||
code[..3].parse::<usize>().unwrap() * shortest_sequence(code, num_dirpads) | ||
} | ||
|
||
#[aoc_main] | ||
fn solve(input: Input) -> impl Into<Solution> { | ||
( | ||
input.lines().map(|code| complexity(code, 1)).sum_usize(), | ||
input.lines().map(|code| complexity(code, 24)).sum_usize(), | ||
) | ||
} | ||
|
||
// 454307058698260 | ||
// 1153311057543380 | ||
|
||
fn make_move(from: char, to: char) -> String { | ||
match (from, to) { | ||
(from, to) if from == to => "", | ||
|
||
('A', '>') => "v", | ||
('A', '<') => "v<<", | ||
('A', '^') => "<", | ||
('A', 'v') => "v<", | ||
|
||
('<', '>') => ">>", | ||
('<', 'v') => ">", | ||
('<', '^') => ">^", | ||
('<', 'A') => ">>^", | ||
|
||
('^', 'A') => ">", | ||
('^', '>') => ">v", | ||
('^', 'v') => "v", | ||
('^', '<') => "v<", | ||
|
||
('>', 'A') => "^", | ||
('>', '^') => "<^", | ||
('>', 'v') => "<", | ||
('>', '<') => "<<", | ||
|
||
('v', 'A') => ">^", | ||
('v', '>') => ">", | ||
('v', '^') => "^", | ||
('v', '<') => "<", | ||
|
||
_ => unreachable!("{from} -> {to}"), | ||
} | ||
.to_string() | ||
+ "A" | ||
} | ||
|
||
fn expand_move(m: &str) -> Vec<String> { | ||
("A".to_string() + m).chars().tuple_windows().map(|(from, to)| make_move(from, to)).collect() | ||
} | ||
|
||
fn expand_path(path: BTreeMap<String, usize>) -> BTreeMap<String, usize> { | ||
path.into_iter() | ||
.flat_map(|(m, count)| expand_move(&m).into_iter().map(move |m| (m, count))) | ||
.fold(BTreeMap::new(), |mut acc, (m, count)| { | ||
*acc.entry(m).or_insert(0) += count; | ||
acc | ||
}) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::collections::BTreeSet; | ||
|
||
use super::*; | ||
#[test] | ||
fn test_examples() { | ||
dbg!(shortest_sequence("0", 2)); | ||
dbg!(shortest_sequence("2", 2)); | ||
dbg!(shortest_sequence("3", 2)); | ||
dbg!(shortest_sequence("6", 2)); | ||
dbg!(shortest_sequence("8", 2)); | ||
|
||
dbg!(make_move('A', '<')); | ||
dbg!(expand_path([(make_move('A', '<'), 1)].into_iter().collect())); | ||
|
||
dbg!(make_move('<', 'A')); | ||
dbg!(expand_path([(make_move('<', 'A'), 1)].into_iter().collect())); | ||
|
||
//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 2)]))); | ||
// | ||
//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 1), (">A".to_string(), 1)]))); | ||
// | ||
//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 2), (">A".to_string(), 1)]))); | ||
|
||
dbg!(expand_path(BTreeMap::from([("<A".to_string(), 1)]))); | ||
dbg!(expand_path(expand_path(BTreeMap::from([("<A".to_string(), 1)])))); | ||
dbg!(expand_path(expand_path(expand_path(BTreeMap::from([("<A".to_string(), 1)]))))); | ||
|
||
//return; | ||
|
||
//assert_eq!(shortest_sequence("0", 0), 1); | ||
//assert_eq!(shortest_sequence("0", 1), 6); | ||
//// <A | ||
//assert_eq!(shortest_sequence("0", 0), 6); | ||
//assert_eq!(shortest_sequence("0", 1), 8); | ||
// | ||
//return; | ||
|
||
//assert_eq!(shortest_sequence("029A", 0), 8); | ||
//assert_eq!(shortest_sequence("029A", 1), 6 + 2 + 2 + 2 + 2 + 4 + 4 + 4); | ||
assert_eq!(shortest_sequence("029A", 1), 68); | ||
// assert_eq!(shortest_sequence("980A".to_string(), 0, 3, &mut HashMap::new()), 60); | ||
// assert_eq!(shortest_sequence("179A".to_string(), 0, 3, &mut HashMap::new()), 68); | ||
// assert_eq!(shortest_sequence("456A".to_string(), 0, 3, &mut HashMap::new()), 64); | ||
// assert_eq!(shortest_sequence("379A".to_string(), 0, 3, &mut HashMap::new()), 64); | ||
// | ||
assert_example!( | ||
" | ||
029A | ||
980A | ||
179A | ||
456A | ||
379A", | ||
126384 | ||
); | ||
} | ||
} |
Oops, something went wrong.