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