added day 21
This commit is contained in:
parent
a6dda67238
commit
50c6a34a28
207
src/day21/1.rs
Normal file
207
src/day21/1.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
|
||||||
|
#![feature(test)]
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use fxhash::FxHashSet;
|
||||||
|
|
||||||
|
const INPUTS: [&str; 2] = [
|
||||||
|
"029A
|
||||||
|
980A
|
||||||
|
179A
|
||||||
|
456A
|
||||||
|
379A",
|
||||||
|
include_str!("./input.txt"),
|
||||||
|
];
|
||||||
|
|
||||||
|
const NAV_KEYPAD: [[char; 3]; 4] = [
|
||||||
|
['7', '8', '9'],
|
||||||
|
['4', '5', '6'],
|
||||||
|
['1', '2', '3'],
|
||||||
|
[' ', '0', 'A'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const DIR_KEYPAD: [[char; 3]; 2] = [
|
||||||
|
[' ', '^', 'A'],
|
||||||
|
['<', 'v', '>'],
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
pub fn run(input: &str) -> i64 {
|
||||||
|
let mut seq = input.lines().map(|x| x.chars().collect::<Vec<char>>()).collect::<Vec<Vec<char>>>();
|
||||||
|
|
||||||
|
let mut answer = 0;
|
||||||
|
|
||||||
|
for seq in seq {
|
||||||
|
|
||||||
|
let mut shortest = std::i64::MAX;
|
||||||
|
|
||||||
|
let keypad_path = find_path_on_keypad(&seq);
|
||||||
|
|
||||||
|
let shortest = keypad_path.into_par_iter().map(|keypad_auth| {
|
||||||
|
find_path_on_nav(&keypad_auth).into_par_iter().map(|nav1| {
|
||||||
|
find_path_on_nav(&nav1).into_iter().map(|x| x.len()).map(|x| x as i64).min().unwrap()
|
||||||
|
}).min().unwrap()
|
||||||
|
}).min().unwrap();
|
||||||
|
|
||||||
|
let seq = seq.iter().filter(|x| x.is_digit(10)).collect::<String>().parse::<i64>().unwrap();
|
||||||
|
answer += seq * shortest;
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn find_path_on_nav(seq: &[char]) -> Vec<Vec<char>> {
|
||||||
|
|
||||||
|
let find_coord = |x: char| match x {
|
||||||
|
'^' => (0,1),
|
||||||
|
'A' => (0,2),
|
||||||
|
'<' => (1,0),
|
||||||
|
'v' => (1,1),
|
||||||
|
'>' => (1,2),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut seq = seq.to_vec();
|
||||||
|
seq.insert(0, 'A');
|
||||||
|
|
||||||
|
let seq = seq.iter().map(|&x| find_coord(x)).collect::<Vec<(i64, i64)>>();
|
||||||
|
|
||||||
|
let path = bfs(&seq, (0,0), (2,3), true);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn find_path_on_keypad(seq: &[char]) -> Vec<Vec<char>> {
|
||||||
|
let find_coord = |x: char| match x {
|
||||||
|
'1' => (2,0),
|
||||||
|
'2' => (2,1),
|
||||||
|
'3' => (2,2),
|
||||||
|
'4' => (1,0),
|
||||||
|
'5' => (1,1),
|
||||||
|
'6' => (1,2),
|
||||||
|
'7' => (0,0),
|
||||||
|
'8' => (0,1),
|
||||||
|
'9' => (0,2),
|
||||||
|
'0' => (3,1),
|
||||||
|
'A' => (3,2),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut seq = seq.to_vec();
|
||||||
|
seq.insert(0, 'A');
|
||||||
|
|
||||||
|
let seq = seq.iter().map(|&x| find_coord(x)).collect::<Vec<(i64, i64)>>();
|
||||||
|
let path = bfs(&seq, (3,0), (4,3), false);
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn bfs(nodes: &[(i64, i64)], ignore: (i64, i64), limits: (i64, i64), shortest_only: bool) -> Vec<Vec<char>> {
|
||||||
|
let mut q = VecDeque::new();
|
||||||
|
q.push_back((nodes[0], 1, vec![], Bitmap::new()));
|
||||||
|
|
||||||
|
let mut best = vec![];
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
|
while let Some(((x, y), mut dest_idx, mut path, mut visited)) = q.pop_front() {
|
||||||
|
if !best.is_empty() && ((shortest_only && path.len() >= best.len()) || (!shortest_only && path.len() > best.len())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if dest_idx == nodes.len() - 1 && x == nodes[dest_idx].0 && y == nodes[dest_idx].1 {
|
||||||
|
path.push('A');
|
||||||
|
|
||||||
|
if best.is_empty() || (!shortest_only && best.len() >= path.len()) || (shortest_only && best.len() > path.len()) {
|
||||||
|
best = path.clone();
|
||||||
|
out.push(best.clone());
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while x == nodes[dest_idx].0 && y == nodes[dest_idx].1 {
|
||||||
|
path.push('A');
|
||||||
|
visited.clear();
|
||||||
|
dest_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if visited.get(x,y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited.set(x,y);
|
||||||
|
|
||||||
|
for (i,j) in [(0,1), (1,0), (0,-1), (-1,0)].iter() {
|
||||||
|
let x = i + x;
|
||||||
|
let y = j + y;
|
||||||
|
|
||||||
|
if x < 0 || y < 0 || x >= limits.0 || y >= limits.1 || visited.get(x,y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if x == ignore.0 && y == ignore.1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut path = path.clone();
|
||||||
|
match (i,j) {
|
||||||
|
(0,1) => path.push('>'),
|
||||||
|
(1,0) => path.push('v'),
|
||||||
|
(0,-1) => path.push('<'),
|
||||||
|
(-1,0) => path.push('^'),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
q.push_back(((x,y), dest_idx, path, visited));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone,Copy)]
|
||||||
|
struct Bitmap {
|
||||||
|
inner: u64,
|
||||||
|
size: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { inner: 0, size: 5}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn get(&self, x: i64, y: i64) -> bool {
|
||||||
|
let offset = (x * self.size + y) as usize;
|
||||||
|
|
||||||
|
(self.inner >> offset & 1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, x: i64, y: i64) {
|
||||||
|
let offset = (x * self.size + y) as usize;
|
||||||
|
|
||||||
|
self.inner |= 1 << offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.inner = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for input in INPUTS.iter().skip(1) {
|
||||||
|
println!("answer = {}", run(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn part1(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| {
|
||||||
|
let v = run(INPUTS[1]);
|
||||||
|
test::black_box(v);
|
||||||
|
});
|
||||||
|
}
|
252
src/day21/2.rs
Normal file
252
src/day21/2.rs
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
|
||||||
|
#![feature(test)]
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
|
||||||
|
const INPUTS: [&str; 2] = [
|
||||||
|
"029A
|
||||||
|
980A
|
||||||
|
179A
|
||||||
|
456A
|
||||||
|
379A",
|
||||||
|
include_str!("./input.txt"),
|
||||||
|
];
|
||||||
|
|
||||||
|
trait KeyPad {
|
||||||
|
fn paths(&self) -> &FxHashMap<((i8, i8), (i8, i8)), Vec<Vec<u8>>>;
|
||||||
|
fn coord_from_u8(&self, c: u8) -> (i8, i8);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NavKeyPad {
|
||||||
|
// grid: [[u8; 3]; 4],
|
||||||
|
paths: FxHashMap<((i8, i8), (i8, i8)), Vec<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPad for LazyLock<NavKeyPad> {
|
||||||
|
fn coord_from_u8(&self, c: u8) -> (i8, i8) {
|
||||||
|
match c {
|
||||||
|
b'1' => (2,0),
|
||||||
|
b'2' => (2,1),
|
||||||
|
b'3' => (2,2),
|
||||||
|
b'4' => (1,0),
|
||||||
|
b'5' => (1,1),
|
||||||
|
b'6' => (1,2),
|
||||||
|
b'7' => (0,0),
|
||||||
|
b'8' => (0,1),
|
||||||
|
b'9' => (0,2),
|
||||||
|
b'0' => (3,1),
|
||||||
|
b'A' => (3,2),
|
||||||
|
_ => (0,0) // unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paths(&self) -> &FxHashMap<((i8, i8), (i8, i8)), Vec<Vec<u8>>> {
|
||||||
|
&self.paths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NAV_KEYPAD: LazyLock<NavKeyPad> = LazyLock::new(|| {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
for a in 0..4 {
|
||||||
|
for b in 0..3 {
|
||||||
|
for c in 0..4 {
|
||||||
|
for d in 0..3 {
|
||||||
|
map.insert(((a,b), (c,d)), compute_path((a,b), (c,d), (3,0), (4,3)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavKeyPad {
|
||||||
|
//grid: [
|
||||||
|
// ['7', '8', '9'],
|
||||||
|
// ['4', '5', '6'],
|
||||||
|
// ['1', '2', '3'],
|
||||||
|
// [' ', '0', 'A'],
|
||||||
|
//],
|
||||||
|
paths: map,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
struct DirKeyPad{
|
||||||
|
// grid: [[u8; 3]; 2],
|
||||||
|
paths: FxHashMap<((i8, i8), (i8, i8)), Vec<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPad for LazyLock<DirKeyPad> {
|
||||||
|
fn coord_from_u8(&self, c: u8) -> (i8, i8) {
|
||||||
|
match c {
|
||||||
|
b'^' => (0,1),
|
||||||
|
b'A' => (0,2),
|
||||||
|
b'<' => (1,0),
|
||||||
|
b'v' => (1,1),
|
||||||
|
b'>' => (1,2),
|
||||||
|
_ => (0, 0) // unreachable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paths(&self) -> &FxHashMap<((i8, i8), (i8, i8)), Vec<Vec<u8>>> {
|
||||||
|
&self.paths
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIR_KEYPAD: LazyLock<DirKeyPad> = LazyLock::new(|| {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
for a in 0..4 {
|
||||||
|
for b in 0..3 {
|
||||||
|
for c in 0..4 {
|
||||||
|
for d in 0..3 {
|
||||||
|
map.insert(((a,b), (c,d)), compute_path((a,b), (c,d), (0,0), (2,3)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirKeyPad {
|
||||||
|
// grid: [
|
||||||
|
// [' ', '^', 'A'],
|
||||||
|
// ['<', 'v', '>'],
|
||||||
|
// ],
|
||||||
|
paths: map,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fn compute_path((sx,sy): (i8, i8), (ex, ey): (i8, i8), ignore: (i8, i8), limits: (i8, i8)) -> Vec<Vec<u8>> {
|
||||||
|
let mut q = VecDeque::new();
|
||||||
|
q.push_back((sx,sy, vec![], Bitmap::new()));
|
||||||
|
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
|
while let Some((x, y, path, mut visited)) = q.pop_front() {
|
||||||
|
if path.len() > out.last().map(|x: &Vec<u8>| x.len()).unwrap_or(std::usize::MAX) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if x == ex && y == ey {
|
||||||
|
out.push(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if visited.get(x,y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited.set(x,y);
|
||||||
|
|
||||||
|
for (i,j) in [(0,1), (1,0), (0,-1), (-1,0)].iter() {
|
||||||
|
let x = i + x;
|
||||||
|
let y = j + y;
|
||||||
|
|
||||||
|
if x < 0 || y < 0 || x >= limits.0 || y >= limits.1 || visited.get(x, y) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if x == ignore.0 && y == ignore.1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut path = path.clone();
|
||||||
|
match (i,j) {
|
||||||
|
(0,1) => path.push(b'>'),
|
||||||
|
(1,0) => path.push(b'v'),
|
||||||
|
(0,-1) => path.push(b'<'),
|
||||||
|
(-1,0) => path.push(b'^'),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
q.push_back((x, y, path, visited));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(input: &str) -> i64 {
|
||||||
|
let input = input.as_bytes();
|
||||||
|
let seq = input.split(|&x| x == b'\n').map(|x| x.to_vec());
|
||||||
|
let mut cache = FxHashMap::default();
|
||||||
|
let mut answer = 0;
|
||||||
|
|
||||||
|
for seq in seq {
|
||||||
|
let seq_num = seq.iter().filter(|x| x.is_ascii_digit()).fold(0, |a,x| a * 10 + (*x - b'0') as i64);
|
||||||
|
|
||||||
|
let shortest = compute(NAV_KEYPAD, seq, 1 + 25, &mut cache);
|
||||||
|
|
||||||
|
answer += seq_num * shortest;
|
||||||
|
}
|
||||||
|
|
||||||
|
answer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute(
|
||||||
|
keypad: impl KeyPad,
|
||||||
|
seq: Vec<u8>,
|
||||||
|
robots: i8,
|
||||||
|
cache: &mut FxHashMap<(i8, usize, Vec<u8>), i64>,
|
||||||
|
) -> i64 {
|
||||||
|
if robots == 0 {
|
||||||
|
return seq.len() as i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = cache.get(&(robots, seq.len(), seq.clone())) {
|
||||||
|
return *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_pos = keypad.coord_from_u8(b'A');
|
||||||
|
let mut smallest_length = 0;
|
||||||
|
|
||||||
|
for &c in seq.iter() {
|
||||||
|
let c_pos = keypad.coord_from_u8(c);
|
||||||
|
let sequence = keypad.paths().get(&(current_pos, c_pos)).unwrap();
|
||||||
|
let mut min = std::i64::MAX;
|
||||||
|
|
||||||
|
for s in sequence {
|
||||||
|
let mut s = s.clone();
|
||||||
|
s.push(b'A');
|
||||||
|
min = std::cmp::min(min, compute(DIR_KEYPAD, s, robots - 1, cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
smallest_length += min;
|
||||||
|
current_pos = c_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.insert((robots, seq.len(), seq), smallest_length);
|
||||||
|
smallest_length
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone,Copy)]
|
||||||
|
struct Bitmap {
|
||||||
|
inner: u64,
|
||||||
|
size: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bitmap {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { inner: 0, size: 5}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn get(&self, x: i8, y: i8) -> bool {
|
||||||
|
let offset = (x * self.size + y) as usize;
|
||||||
|
|
||||||
|
(self.inner >> offset & 1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, x: i8, y: i8) {
|
||||||
|
let offset = (x * self.size + y) as usize;
|
||||||
|
|
||||||
|
self.inner |= 1 << offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for input in INPUTS.iter() {
|
||||||
|
println!("answer = {}", run(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn part2(b: &mut test::Bencher) {
|
||||||
|
b.iter(|| {
|
||||||
|
let v = run(INPUTS[1]);
|
||||||
|
test::black_box(v);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user