Skip to content

Commit

Permalink
solve: day21
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-anders committed Dec 22, 2024
1 parent e5aa40c commit 4383ef0
Showing 1 changed file with 73 additions and 132 deletions.
205 changes: 73 additions & 132 deletions day21/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,75 @@
use std::{
collections::{BTreeMap, HashMap},
iter::{once, repeat, repeat_n},
};
use std::{collections::BTreeMap, iter::once};

use aoc_derive::aoc_main;
use graphs::{WeightedGraph, dijkstra};
use itertools::{Itertools, iproduct};
use itertools::Itertools;
use math::Vec2D;
use utils::*;

#[derive(Debug, Clone)]
struct KeypadRobot {
pos: Vec2D,
num_dirpads: usize,
// Order matters here! e.g., changing >^ to ^> changes the shortest path to the code.
// One could write an algorithm that tries all legal move orders and the permutations for each
// path, but lucklily they all seem to be independent, so I manually tried all legal move orders
// for each path, keeping the one that yielded the lowest answer in the end.
fn move_on_dirpad(from: char, to: char) -> String {
match (from, to) {
(from, to) if from == to => "",

('A', '>') => "v",
('A', '<') => "v<<",
('A', '^') => "<",
('A', 'v') => "<v",

('<', '>') => ">>",
('<', 'v') => ">",
('<', '^') => ">^",
('<', 'A') => ">>^",

('^', 'A') => ">",
('^', '>') => "v>",
('^', 'v') => "v",
('^', '<') => "v<",

('>', 'A') => "^",
('>', '^') => "<^",
('>', 'v') => "<",
('>', '<') => "<<",

('v', 'A') => "^>",
('v', '>') => ">",
('v', '^') => "^",
('v', '<') => "<",

_ => unreachable!("{from} -> {to}"),
}
.to_string()
+ "A"
}

fn expand_moves(moves: &str) -> Vec<String> {
("A".to_string() + moves)
.chars()
.tuple_windows()
.map(|(from, to)| move_on_dirpad(from, to))
.collect()
}

fn check_path(start: Vec2D, path: &[char], forbidden: Vec2D) -> bool {
path.iter()
.try_fold(start, |pos, c| {
(pos != forbidden).then_some(
pos + Vec2D::from(match c {
'<' => (-1, 0),
'>' => (1, 0),
'^' => (0, -1),
'v' => (0, 1),
_ => unreachable!(),
}),
)
fn expand_path(path: BTreeMap<String, usize>) -> BTreeMap<String, usize> {
path.into_iter()
.flat_map(|(m, count)| expand_moves(&m).into_iter().map(move |m| (m, count)))
.fold(BTreeMap::new(), |mut acc, (m, count)| {
*acc.entry(m).or_insert(0) += count;
acc
})
.is_some()
}

#[derive(Debug, Clone)]
struct KeypadRobot {
num_dirpads: usize,
}

impl KeypadRobot {
fn new(num_dirpads: usize) -> Self {
Self { pos: Self::coord('A'), num_dirpads }
Self { num_dirpads }
}

fn coord(c: char) -> Vec2D {
Expand All @@ -56,22 +92,25 @@ impl KeypadRobot {

fn expand_move(&self, from: char, to: char) -> usize {
(0..self.num_dirpads)
.fold([(make_move(from, to), 1)].into_iter().collect(), |path, _| expand_path(path))
.fold([(move_on_dirpad(from, to), 1)].into_iter().collect(), |path, _| {
expand_path(path)
})
.into_iter()
.map(|(path, count)| path.len() * count)
.sum()
}

fn enter_char(&mut self, c: char) -> usize {
let dist =
dijkstra(self, [(self.pos, 'A', false)], |&node| node == (Self::coord(c), 'A', true));

self.pos = Self::coord(c);
dist.unwrap()
}

fn enter_code(&mut self, code: &str) -> usize {
code.chars().map(|c| self.enter_char(c)).sum()
once('A')
.chain(code.chars())
.tuple_windows()
.map(|(from, to)| {
dijkstra(self, [(Self::coord(from), 'A', false)], |&node| {
node == (Self::coord(to), 'A', true)
})
.unwrap()
})
.sum()
}
}

Expand Down Expand Up @@ -104,13 +143,8 @@ impl WeightedGraph for KeypadRobot {
}
}

fn shortest_sequence(code: &str, num_dirpads: usize) -> usize {
KeypadRobot::new(num_dirpads).enter_code(code)
}

fn complexity(code: &str, num_dirpads: usize) -> usize {
println!("check code {code}");
code[..3].parse::<usize>().unwrap() * shortest_sequence(code, num_dirpads)
code[..3].parse::<usize>().unwrap() * KeypadRobot::new(num_dirpads).enter_code(code)
}

#[aoc_main]
Expand All @@ -121,104 +155,11 @@ fn solve(input: Input) -> impl Into<Solution> {
)
}

// 454307058698260
// 1153311057543380

fn make_move(from: char, to: char) -> String {
match (from, to) {
(from, to) if from == to => "",

('A', '>') => "v",
('A', '<') => "v<<",
('A', '^') => "<",
('A', 'v') => "v<",

('<', '>') => ">>",
('<', 'v') => ">",
('<', '^') => ">^",
('<', 'A') => ">>^",

('^', 'A') => ">",
('^', '>') => ">v",
('^', 'v') => "v",
('^', '<') => "v<",

('>', 'A') => "^",
('>', '^') => "<^",
('>', 'v') => "<",
('>', '<') => "<<",

('v', 'A') => ">^",
('v', '>') => ">",
('v', '^') => "^",
('v', '<') => "<",

_ => unreachable!("{from} -> {to}"),
}
.to_string()
+ "A"
}

fn expand_move(m: &str) -> Vec<String> {
("A".to_string() + m).chars().tuple_windows().map(|(from, to)| make_move(from, to)).collect()
}

fn expand_path(path: BTreeMap<String, usize>) -> BTreeMap<String, usize> {
path.into_iter()
.flat_map(|(m, count)| expand_move(&m).into_iter().map(move |m| (m, count)))
.fold(BTreeMap::new(), |mut acc, (m, count)| {
*acc.entry(m).or_insert(0) += count;
acc
})
}

#[cfg(test)]
mod tests {
use std::collections::BTreeSet;

use super::*;
#[test]
fn test_examples() {
dbg!(shortest_sequence("0", 2));
dbg!(shortest_sequence("2", 2));
dbg!(shortest_sequence("3", 2));
dbg!(shortest_sequence("6", 2));
dbg!(shortest_sequence("8", 2));

dbg!(make_move('A', '<'));
dbg!(expand_path([(make_move('A', '<'), 1)].into_iter().collect()));

dbg!(make_move('<', 'A'));
dbg!(expand_path([(make_move('<', 'A'), 1)].into_iter().collect()));

//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 2)])));
//
//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 1), (">A".to_string(), 1)])));
//
//dbg!(expand_path(BTreeMap::from([("<A".to_string(), 2), (">A".to_string(), 1)])));

dbg!(expand_path(BTreeMap::from([("<A".to_string(), 1)])));
dbg!(expand_path(expand_path(BTreeMap::from([("<A".to_string(), 1)]))));
dbg!(expand_path(expand_path(expand_path(BTreeMap::from([("<A".to_string(), 1)])))));

//return;

//assert_eq!(shortest_sequence("0", 0), 1);
//assert_eq!(shortest_sequence("0", 1), 6);
//// <A
//assert_eq!(shortest_sequence("0", 0), 6);
//assert_eq!(shortest_sequence("0", 1), 8);
//
//return;

//assert_eq!(shortest_sequence("029A", 0), 8);
//assert_eq!(shortest_sequence("029A", 1), 6 + 2 + 2 + 2 + 2 + 4 + 4 + 4);
assert_eq!(shortest_sequence("029A", 1), 68);
// assert_eq!(shortest_sequence("980A".to_string(), 0, 3, &mut HashMap::new()), 60);
// assert_eq!(shortest_sequence("179A".to_string(), 0, 3, &mut HashMap::new()), 68);
// assert_eq!(shortest_sequence("456A".to_string(), 0, 3, &mut HashMap::new()), 64);
// assert_eq!(shortest_sequence("379A".to_string(), 0, 3, &mut HashMap::new()), 64);
//
assert_example!(
"
029A
Expand Down

0 comments on commit 4383ef0

Please sign in to comment.