generated from janezicmatej/aoc-template
Compare commits
2 Commits
000002509e
...
00000270ae
Author | SHA1 | Date |
---|---|---|
Matej Janezic | 00000270ae | |
Matej Janezic | 00000260b8 |
|
@ -0,0 +1,15 @@
|
||||||
|
###############
|
||||||
|
#...#...#.....#
|
||||||
|
#.#.#.#.#.###.#
|
||||||
|
#S#...#.#.#...#
|
||||||
|
#######.#.#.###
|
||||||
|
#######.#.#...#
|
||||||
|
#######.#.###.#
|
||||||
|
###..E#...#...#
|
||||||
|
###.#######.###
|
||||||
|
#...###...#...#
|
||||||
|
#.#####.#.###.#
|
||||||
|
#.#...#.#.#...#
|
||||||
|
#.#.#.#.#.#.###
|
||||||
|
#...#...#...###
|
||||||
|
###############
|
|
@ -0,0 +1,5 @@
|
||||||
|
029A
|
||||||
|
980A
|
||||||
|
179A
|
||||||
|
456A
|
||||||
|
379A
|
|
@ -0,0 +1,148 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use aoc::grid_vec::{Direction, Grid, Point};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> (Grid, Point, Point) {
|
||||||
|
let mut grid: Grid = input.parse().unwrap();
|
||||||
|
let start = grid.find(b'S').unwrap();
|
||||||
|
let end = grid.find(b'E').unwrap();
|
||||||
|
|
||||||
|
grid[start] = b'.';
|
||||||
|
grid[end] = b'.';
|
||||||
|
|
||||||
|
(grid, start, end)
|
||||||
|
}
|
||||||
|
fn bfs(
|
||||||
|
grid: &Grid,
|
||||||
|
start: Point,
|
||||||
|
limit: Option<usize>,
|
||||||
|
cheat: bool,
|
||||||
|
visited: &mut HashMap<Point, usize>,
|
||||||
|
queue: &mut VecDeque<(Point, usize)>,
|
||||||
|
) {
|
||||||
|
visited.clear();
|
||||||
|
queue.clear();
|
||||||
|
|
||||||
|
queue.push_back((start, 0));
|
||||||
|
|
||||||
|
while let Some((p, l)) = queue.pop_front() {
|
||||||
|
if visited.contains_key(&p) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited.insert(p, l);
|
||||||
|
|
||||||
|
for d in Direction::CROSS {
|
||||||
|
let n = p + d;
|
||||||
|
|
||||||
|
let nv = grid.get(&n);
|
||||||
|
let uwnv = *nv.unwrap_or(&b'#');
|
||||||
|
let valid_neigh = (cheat && nv.is_some()) || uwnv == b'.';
|
||||||
|
let valid_length = l < limit.unwrap_or(usize::MAX);
|
||||||
|
let unvisited = !visited.contains_key(&n);
|
||||||
|
|
||||||
|
if valid_neigh && valid_length && unvisited {
|
||||||
|
queue.push_back((n, l + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cheat_neighbours_and_benchmark(
|
||||||
|
grid: &Grid,
|
||||||
|
start: Point,
|
||||||
|
cheat_limit: usize,
|
||||||
|
) -> HashMap<Point, Vec<(Point, usize)>> {
|
||||||
|
let mut map = HashMap::<Point, Vec<(Point, usize)>>::new();
|
||||||
|
|
||||||
|
let mut reuse_queue = VecDeque::new();
|
||||||
|
let mut reuse_visited = HashMap::new();
|
||||||
|
|
||||||
|
let mut valid_points = HashMap::new();
|
||||||
|
|
||||||
|
bfs(
|
||||||
|
grid,
|
||||||
|
start,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
&mut valid_points,
|
||||||
|
&mut reuse_queue,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (&s, _) in valid_points.iter() {
|
||||||
|
bfs(
|
||||||
|
grid,
|
||||||
|
s,
|
||||||
|
Some(cheat_limit),
|
||||||
|
true,
|
||||||
|
&mut reuse_visited,
|
||||||
|
&mut reuse_queue,
|
||||||
|
);
|
||||||
|
for (&point, &length) in reuse_visited.iter().filter(|(&p, _)| grid[p] == b'.') {
|
||||||
|
map.entry(s).or_default().push((point, length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cheated_bfs(
|
||||||
|
grid: &Grid,
|
||||||
|
start: Point,
|
||||||
|
end: Point,
|
||||||
|
cheat_limit: usize,
|
||||||
|
increase_limit: usize,
|
||||||
|
) -> usize {
|
||||||
|
let neighbours = get_cheat_neighbours_and_benchmark(grid, start, cheat_limit);
|
||||||
|
|
||||||
|
let mut reuse_queue = VecDeque::new();
|
||||||
|
|
||||||
|
let mut from_start = HashMap::new();
|
||||||
|
let mut from_end = HashMap::new();
|
||||||
|
|
||||||
|
bfs(grid, start, None, false, &mut from_start, &mut reuse_queue);
|
||||||
|
bfs(grid, end, None, false, &mut from_end, &mut reuse_queue);
|
||||||
|
|
||||||
|
let benchmark = from_start[&end];
|
||||||
|
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
for node in from_start.keys() {
|
||||||
|
for (neigh, cheat_dist) in neighbours[node].iter() {
|
||||||
|
let start_dist = from_start[node];
|
||||||
|
let end_dist = from_end[neigh];
|
||||||
|
|
||||||
|
let dist = start_dist + cheat_dist + end_dist;
|
||||||
|
if dist < benchmark && dist.abs_diff(benchmark) >= increase_limit {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let (grid, start, end) = parse_input(input);
|
||||||
|
cheated_bfs(&grid, start, end, 2, 100).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
let (grid, start, end) = parse_input(input);
|
||||||
|
cheated_bfs(&grid, start, end, 20, 100).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(20);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 20)), None);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 20)), None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,262 @@
|
||||||
|
use std::iter::once;
|
||||||
|
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
const NUMPAD: [(&str, &str); 110] = [
|
||||||
|
// 0 - everywhere
|
||||||
|
("01", "^<"),
|
||||||
|
("02", "^"),
|
||||||
|
("03", "^>"),
|
||||||
|
("04", "^^<"),
|
||||||
|
("05", "^^"),
|
||||||
|
("06", "^^>"),
|
||||||
|
("07", "^^^<"),
|
||||||
|
("08", "^^^"),
|
||||||
|
("09", "^^^>"),
|
||||||
|
("0A", ">"),
|
||||||
|
// 1 - everywhere
|
||||||
|
("10", ">v"),
|
||||||
|
("12", ">"),
|
||||||
|
("13", ">>"),
|
||||||
|
("14", "^"),
|
||||||
|
("15", "^>"),
|
||||||
|
("16", "^>>"),
|
||||||
|
("17", "^^"),
|
||||||
|
("18", "^^>"),
|
||||||
|
("19", "^^>>"),
|
||||||
|
("1A", ">>v"),
|
||||||
|
// 2 - everywhere
|
||||||
|
("20", "v"),
|
||||||
|
("21", "<"),
|
||||||
|
("23", ">"),
|
||||||
|
("24", "^<"),
|
||||||
|
("25", "^"),
|
||||||
|
("26", "^>"),
|
||||||
|
("27", "^^<"),
|
||||||
|
("28", "^^"),
|
||||||
|
("29", "^^>"),
|
||||||
|
("2A", ">v"),
|
||||||
|
// 3 - everywhere
|
||||||
|
("30", "v<"),
|
||||||
|
("31", "<<"),
|
||||||
|
("32", "<"),
|
||||||
|
("34", "^<<"),
|
||||||
|
("35", "^<"),
|
||||||
|
("36", "^"),
|
||||||
|
("37", "^^<<"),
|
||||||
|
("38", "^^<"),
|
||||||
|
("39", "^^"),
|
||||||
|
("3A", "v"),
|
||||||
|
// 4 - everywhere
|
||||||
|
("40", ">vv"),
|
||||||
|
("41", "v"),
|
||||||
|
("42", "v>"),
|
||||||
|
("43", "v>>"),
|
||||||
|
("45", ">"),
|
||||||
|
("46", ">>"),
|
||||||
|
("47", "^"),
|
||||||
|
("48", "^>"),
|
||||||
|
("49", "^>>"),
|
||||||
|
("4A", ">>vv"),
|
||||||
|
// 5 - everywhere
|
||||||
|
("50", "vv"),
|
||||||
|
("51", "v<"),
|
||||||
|
("52", "v"),
|
||||||
|
("53", "v>"),
|
||||||
|
("54", "<"),
|
||||||
|
("56", ">"),
|
||||||
|
("57", "^<"),
|
||||||
|
("58", "^"),
|
||||||
|
("59", "^>"),
|
||||||
|
("5A", ">vv"),
|
||||||
|
// 6 - everywhere
|
||||||
|
("60", "vv<"),
|
||||||
|
("61", "v<<"),
|
||||||
|
("62", "v<"),
|
||||||
|
("63", "v"),
|
||||||
|
("64", "<<"),
|
||||||
|
("65", "<"),
|
||||||
|
("67", "^<<"),
|
||||||
|
("68", "^<"),
|
||||||
|
("69", "^"),
|
||||||
|
("6A", "vv"),
|
||||||
|
// 7 - everywhere
|
||||||
|
("70", ">vvv"),
|
||||||
|
("71", "vv"),
|
||||||
|
("72", "vv>"),
|
||||||
|
("73", "vv>>"),
|
||||||
|
("74", "v"),
|
||||||
|
("75", "v>"),
|
||||||
|
("76", "v>>"),
|
||||||
|
("78", ">"),
|
||||||
|
("79", ">>"),
|
||||||
|
("7A", ">>vvv"),
|
||||||
|
// 8 - everywhere
|
||||||
|
("80", "vvv"),
|
||||||
|
("81", "vv<"),
|
||||||
|
("82", "vv"),
|
||||||
|
("83", "vv>"),
|
||||||
|
("84", "v<"),
|
||||||
|
("85", "v"),
|
||||||
|
("86", "v>"),
|
||||||
|
("87", "<"),
|
||||||
|
("89", ">"),
|
||||||
|
("8A", ">vvv"),
|
||||||
|
// 9 - everywhere
|
||||||
|
("90", "<vvv"),
|
||||||
|
("91", "vv<<"),
|
||||||
|
("92", "vv<"),
|
||||||
|
("93", "vv"),
|
||||||
|
("94", "v<<"),
|
||||||
|
("95", "v<"),
|
||||||
|
("96", "v"),
|
||||||
|
("97", "<<"),
|
||||||
|
("98", "<"),
|
||||||
|
("9A", "vvv"),
|
||||||
|
// A - everywhere
|
||||||
|
("A0", "<"),
|
||||||
|
("A1", "^<<"),
|
||||||
|
("A2", "^<"),
|
||||||
|
("A3", "^"),
|
||||||
|
("A4", "^^<<"),
|
||||||
|
("A5", "^^<"),
|
||||||
|
("A6", "^^"),
|
||||||
|
("A7", "^^^<<"),
|
||||||
|
("A8", "^^^<"),
|
||||||
|
("A9", "^^^"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const KEYPAD: [(&str, &str); 20] = [
|
||||||
|
// < - everywhere
|
||||||
|
("<v", ">"),
|
||||||
|
("<>", ">>"),
|
||||||
|
("<^", ">^"),
|
||||||
|
("<A", ">>^"),
|
||||||
|
// v - everywhere
|
||||||
|
("v<", "<"),
|
||||||
|
("v>", ">"),
|
||||||
|
("v^", "^"),
|
||||||
|
("vA", ">^"),
|
||||||
|
// > - everywhere
|
||||||
|
("><", "<<"),
|
||||||
|
(">v", "<"),
|
||||||
|
(">^", "^<"),
|
||||||
|
(">A", "^"),
|
||||||
|
// ^ - everywhere
|
||||||
|
("^<", "v<"),
|
||||||
|
("^v", "v"),
|
||||||
|
("^>", "v>"),
|
||||||
|
("^A", ">"),
|
||||||
|
// A - everywhere
|
||||||
|
("A<", "v<<"),
|
||||||
|
("Av", "v<"),
|
||||||
|
("A>", "v"),
|
||||||
|
("A^", "<"),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn build_move_options(s: &str) -> Vec<String> {
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
set.insert(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut v = vec![s.to_string()];
|
||||||
|
|
||||||
|
if set.len() == 2 {
|
||||||
|
v.push(s.chars().rev().collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_one(input: &str) -> Option<usize> {
|
||||||
|
let mut keypad = HashMap::new();
|
||||||
|
|
||||||
|
for (k, v) in KEYPAD.iter() {
|
||||||
|
let key = k.as_bytes();
|
||||||
|
keypad.insert((key[0], key[1]), build_move_options(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut numpad = HashMap::new();
|
||||||
|
|
||||||
|
for (k, v) in NUMPAD.iter() {
|
||||||
|
let key = k.as_bytes();
|
||||||
|
numpad.insert((key[0], key[1]), build_move_options(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s1s = Vec::new();
|
||||||
|
let mut s2s = Vec::new();
|
||||||
|
let mut s3s = Vec::new();
|
||||||
|
let mut s4s = Vec::new();
|
||||||
|
|
||||||
|
let mut c = 0;
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
s1s.clear();
|
||||||
|
s2s.clear();
|
||||||
|
s3s.clear();
|
||||||
|
s4s.clear();
|
||||||
|
|
||||||
|
s1s.push(once(&b'A').chain(line.as_bytes()).copied().collect_vec());
|
||||||
|
|
||||||
|
for mapper in [&numpad, &keypad, &keypad] {
|
||||||
|
for s1 in s1s.iter() {
|
||||||
|
s3s.push(vec![b'A']);
|
||||||
|
for i in 1..s1.len() {
|
||||||
|
let k2 = s1[i - 1];
|
||||||
|
let k1 = s1[i];
|
||||||
|
|
||||||
|
if mapper.contains_key(&(k1, k2)) {
|
||||||
|
for path in mapper[&(k1, k2)].iter() {
|
||||||
|
for s3 in s3s.iter() {
|
||||||
|
let mut cs3 = s3.clone();
|
||||||
|
cs3.extend(path.as_bytes());
|
||||||
|
s4s.push(cs3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s3s.clear();
|
||||||
|
s3s.append(&mut s4s);
|
||||||
|
|
||||||
|
for s3 in s3s.iter_mut() {
|
||||||
|
s3.push(b'A');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s2s.append(&mut s3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
s1s.clear();
|
||||||
|
s1s.append(&mut s2s);
|
||||||
|
}
|
||||||
|
|
||||||
|
let min_len = s1s.iter().map(|v| v.len()).min().unwrap();
|
||||||
|
let n: usize = line.strip_suffix("A").unwrap().parse().unwrap();
|
||||||
|
|
||||||
|
println!("{min_len}, {n}");
|
||||||
|
c += n * min_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_two(input: &str) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
aoc::solution!(21);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_part_one() {
|
||||||
|
assert_eq!(part_one(&aoc::template::read_file("examples", 21)), None);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_part_two() {
|
||||||
|
assert_eq!(part_two(&aoc::template::read_file("examples", 21)), None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ impl Display for Grid {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
for (idx, cell) in self.grid.iter().enumerate() {
|
for (idx, cell) in self.grid.iter().enumerate() {
|
||||||
write!(f, "{}", *cell as char)?;
|
write!(f, "{}", *cell as char)?;
|
||||||
if idx > 0 && idx % self.width == 0 {
|
if idx > 0 && (idx + 1) % self.width == 0 {
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,20 @@ impl Grid {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, p: &Point) -> Option<&u8> {
|
pub fn get(&self, p: &Point) -> Option<&u8> {
|
||||||
|
if p.i >= self.grid.len() / self.width || p.j >= self.width {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
self.grid.get(p.i * self.width + p.j)
|
self.grid.get(p.i * self.width + p.j)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, p: &Point) -> Option<&mut u8> {
|
pub fn get_mut(&mut self, p: &Point) -> Option<&mut u8> {
|
||||||
|
if p.i >= self.grid.len() / self.width || p.j >= self.width {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
self.grid.get_mut(p.i * self.width + p.j)
|
self.grid.get_mut(p.i * self.width + p.j)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> (usize, usize) {
|
||||||
|
(self.grid.len() / self.width, self.width)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl Point {
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! pnt {
|
macro_rules! pnt {
|
||||||
($i:literal, $j:literal) => {
|
($i:expr, $j:expr) => {
|
||||||
Point { i: $i, j: $j }
|
Point { i: $i, j: $j }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue