mirror of
https://github.com/mx42/aoc2024.rs.git
synced 2026-01-14 05:49:53 +01:00
feat: add day 20 p1
This commit is contained in:
@@ -53,8 +53,9 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.
|
|||||||
| [Day 17](./src/bin/17.rs) | `1.5ms` | `-` |
|
| [Day 17](./src/bin/17.rs) | `1.5ms` | `-` |
|
||||||
| [Day 18](./src/bin/18.rs) | `2.1ms` | `5.0ms` |
|
| [Day 18](./src/bin/18.rs) | `2.1ms` | `5.0ms` |
|
||||||
| [Day 19](./src/bin/19.rs) | `21.1ms` | `707.2ms` |
|
| [Day 19](./src/bin/19.rs) | `21.1ms` | `707.2ms` |
|
||||||
|
| [Day 20](./src/bin/20.rs) | `754.2ms` | `-` |
|
||||||
|
|
||||||
**Total: 14003.90ms**
|
**Total: 14758.10ms**
|
||||||
<!--- benchmarking table --->
|
<!--- benchmarking table --->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
15
data/examples/20.txt
Normal file
15
data/examples/20.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
###############
|
||||||
|
#...#...#.....#
|
||||||
|
#.#.#.#.#.###.#
|
||||||
|
#S#...#.#.#...#
|
||||||
|
#######.#.#.###
|
||||||
|
#######.#.#...#
|
||||||
|
#######.#.###.#
|
||||||
|
###..E#...#...#
|
||||||
|
###.#######.###
|
||||||
|
#...###...#...#
|
||||||
|
#.#####.#.###.#
|
||||||
|
#.#...#.#.#...#
|
||||||
|
#.#.#.#.#.#.###
|
||||||
|
#...#...#...###
|
||||||
|
###############
|
||||||
204
src/bin/20.rs
Normal file
204
src/bin/20.rs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
advent_of_code::solution!(20);
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
struct Pos {
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pos {
|
||||||
|
fn init(x: usize, y: usize) -> Pos {
|
||||||
|
Pos { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn neighbors(&self) -> Vec<Pos> {
|
||||||
|
let mut res: Vec<Pos> = Vec::new();
|
||||||
|
if self.x > 0 {
|
||||||
|
res.push(Pos::init(self.x - 1, self.y));
|
||||||
|
}
|
||||||
|
if self.y > 0 {
|
||||||
|
res.push(Pos::init(self.x, self.y - 1));
|
||||||
|
}
|
||||||
|
res.push(Pos::init(self.x + 1, self.y));
|
||||||
|
res.push(Pos::init(self.x, self.y + 1));
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PosState {
|
||||||
|
c: char,
|
||||||
|
dist: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<char> for PosState {
|
||||||
|
fn from(input: char) -> PosState {
|
||||||
|
PosState {
|
||||||
|
c: input,
|
||||||
|
dist: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct State {
|
||||||
|
m: HashMap<Pos, PosState>,
|
||||||
|
end: Pos,
|
||||||
|
start: Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new() -> State {
|
||||||
|
State {
|
||||||
|
m: HashMap::new(),
|
||||||
|
end: Pos::init(0, 0),
|
||||||
|
start: Pos::init(0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, p: Pos, ps: PosState) -> State {
|
||||||
|
let end = if ps.c == 'E' {
|
||||||
|
p.clone()
|
||||||
|
} else {
|
||||||
|
self.end.clone()
|
||||||
|
};
|
||||||
|
let start = if ps.c == 'S' {
|
||||||
|
p.clone()
|
||||||
|
} else {
|
||||||
|
self.start.clone()
|
||||||
|
};
|
||||||
|
let mut m = self.m.clone();
|
||||||
|
m.insert(p, ps);
|
||||||
|
|
||||||
|
State { m, end, start }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_distances(&mut self, current: Vec<Pos>, dist: usize) {
|
||||||
|
if current.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut next: Vec<Pos> = Vec::new();
|
||||||
|
for cur in ¤t {
|
||||||
|
let mut add = false;
|
||||||
|
self.m.entry(cur.clone()).and_modify(|e| {
|
||||||
|
if e.dist.is_none() {
|
||||||
|
e.dist = Some(dist);
|
||||||
|
if e.c != '#' {
|
||||||
|
add = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if add {
|
||||||
|
for n in cur.neighbors() {
|
||||||
|
if self.m.contains_key(&n) {
|
||||||
|
next.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.sort();
|
||||||
|
next.dedup();
|
||||||
|
self.fill_distances(next, dist + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn print(&self) {
|
||||||
|
// for y in 0..15 {
|
||||||
|
// for x in 0..15 {
|
||||||
|
// let p = Pos::init(x, y);
|
||||||
|
// if let Some(e) = self.m.get(&p) {
|
||||||
|
// print!("{}{}{}{}{}{}", e.c, e.c, e.c, e.c, e.c, e.c);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!();
|
||||||
|
// for x in 0..15 {
|
||||||
|
// let p = Pos::init(x, y);
|
||||||
|
// if let Some(e) = self.m.get(&p) {
|
||||||
|
// if let Some(d) = e.dist {
|
||||||
|
// print!("{}{:^4}{}", e.c, d, e.c);
|
||||||
|
// } else {
|
||||||
|
// print!("{}{}{}{}{}{}", e.c, e.c, e.c, e.c, e.c, e.c);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!();
|
||||||
|
// for x in 0..15 {
|
||||||
|
// let p = Pos::init(x, y);
|
||||||
|
// if let Some(e) = self.m.get(&p) {
|
||||||
|
// print!("{}{}{}{}{}{}", e.c, e.c, e.c, e.c, e.c, e.c);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn get_time_saving_cheats(&self) -> HashMap<usize, usize> {
|
||||||
|
let mut cheats: HashMap<usize, usize> = HashMap::new();
|
||||||
|
|
||||||
|
for (p, ps) in self.m.iter() {
|
||||||
|
if ps.c == '#' {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for n in p.neighbors() {
|
||||||
|
if let Some(ns) = self.m.get(&n) {
|
||||||
|
if ns.c == '#' && ns.dist.is_some() && ns.dist < ps.dist {
|
||||||
|
let cheat_save =
|
||||||
|
(ps.dist.unwrap() as isize) - (ns.dist.unwrap() as isize) - 1isize;
|
||||||
|
if cheat_save > 0 {
|
||||||
|
*cheats.entry(cheat_save as usize).or_default() += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cheats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> State {
|
||||||
|
let mut state = input
|
||||||
|
.strip_suffix("\n")
|
||||||
|
.unwrap_or(input)
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(y, l)| {
|
||||||
|
l.chars()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(x, c)| (Pos::init(x, y), PosState::from(c)))
|
||||||
|
.collect::<Vec<(Pos, PosState)>>()
|
||||||
|
})
|
||||||
|
.fold(State::new(), |st, (p, ps)| st.update(p, ps));
|
||||||
|
state.fill_distances(vec![state.end.clone()], 0);
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
parse_input(input)
|
||||||
|
.get_time_saving_cheats()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(k, v)| if *k >= 100 { Some(v) } else { None })
|
||||||
|
.sum::<usize>()
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(_input: &str) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, Some(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
|
||||||
|
assert_eq!(result, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user