generated from janezicmatej/aoc-template
Compare commits
No commits in common. "00000270aea1039e83592a1f1e221c02db0d8ff5" and "000002509e2e400b248a2b86e865c9becd5d4901" have entirely different histories.
00000270ae
...
000002509e
|
@ -1,15 +0,0 @@
|
||||||
###############
|
|
||||||
#...#...#.....#
|
|
||||||
#.#.#.#.#.###.#
|
|
||||||
#S#...#.#.#...#
|
|
||||||
#######.#.#.###
|
|
||||||
#######.#.#...#
|
|
||||||
#######.#.###.#
|
|
||||||
###..E#...#...#
|
|
||||||
###.#######.###
|
|
||||||
#...###...#...#
|
|
||||||
#.#####.#.###.#
|
|
||||||
#.#...#.#.#...#
|
|
||||||
#.#.#.#.#.#.###
|
|
||||||
#...#...#...###
|
|
||||||
###############
|
|
|
@ -1,5 +0,0 @@
|
||||||
029A
|
|
||||||
980A
|
|
||||||
179A
|
|
||||||
456A
|
|
||||||
379A
|
|
148
src/bin/20.rs
148
src/bin/20.rs
|
@ -1,148 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
262
src/bin/21.rs
262
src/bin/21.rs
|
@ -1,262 +0,0 @@
|
||||||
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 + 1) % self.width == 0 {
|
if idx > 0 && idx % self.width == 0 {
|
||||||
writeln!(f)?;
|
writeln!(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,20 +77,10 @@ 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:expr, $j:expr) => {
|
($i:literal, $j:literal) => {
|
||||||
Point { i: $i, j: $j }
|
Point { i: $i, j: $j }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue