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 {
|
||||
for (idx, cell) in self.grid.iter().enumerate() {
|
||||
write!(f, "{}", *cell as char)?;
|
||||
if idx > 0 && idx % self.width == 0 {
|
||||
if idx > 0 && (idx + 1) % self.width == 0 {
|
||||
writeln!(f)?;
|
||||
}
|
||||
}
|
||||
|
@ -77,10 +77,20 @@ impl Grid {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn size(&self) -> (usize, usize) {
|
||||
(self.grid.len() / self.width, self.width)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ impl Point {
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! pnt {
|
||||
($i:literal, $j:literal) => {
|
||||
($i:expr, $j:expr) => {
|
||||
Point { i: $i, j: $j }
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue