added day 21

This commit is contained in:
Ishan Jain 2024-12-22 03:25:20 +05:30
parent a6dda67238
commit 50c6a34a28
2 changed files with 459 additions and 0 deletions

207
src/day21/1.rs Normal file
View 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
View 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);
});
}