day 23. also, long term nuclear warning messages in day 21.

This commit is contained in:
Shoofle 2024-12-24 00:32:55 -05:00
parent 926fc4f6a8
commit 28f5eb17b0
4 changed files with 658 additions and 6 deletions

View File

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
memoize = "0.4.2"

View File

@ -1,7 +1,22 @@
/*
THIS PLACE IS NOT A PLACE OF HONOR...
NO HIGHLY ESTEEMED DEED IS COMMEMORATED HERE...
NOTHING VALUED IS HERE.
THE DANGER IS PRESENT IN YOUR TIME AS IT WAS IN OURS.
THE DANGER IS UNLEASHED ONLY IF YOU SUBSTANTIALLY DISTURB THIS FILE...
THIS FILE IS BEST LEFT SHUNNED AND UNINHABITED.
*/
use std::collections::HashSet;
use std::collections::HashMap;
use std::env;
use std::fs;
use memoize::memoize;
use crate::Dir::{North, South, East, West, Stay };
@ -118,7 +133,6 @@ fn main() {
&keypad_door,
&door_code,
'A');
println!("{options_door:?}");
for arrow_code_1 in sequences(options_door, &door_map, keypad_door[&'A']) {
print_code(&arrow_code_1);
@ -146,8 +160,519 @@ fn main() {
}
println!("the sum of the complexity scores is {}", sum);
println!("okay let's play around");
let code_0 = "029A";
println!("starting sequence is {code_0}");
let mut seed = 0;
let code_1 = &expand(
&code_0.chars().collect(),
&door_map,
&keypad_door,
'A',
&mut seed);
print_code(code_1);
let code_2 = &expand(
&code_1,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_2);
let code_3 = &expand(
&code_2,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_3);
let code_4 = &expand(
&code_3,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_4);
seed = 0b11111111111111111111111;
let code_1 = &expand(
&code_0.chars().collect(),
&door_map,
&keypad_door,
'A',
&mut seed);
print_code(code_1);
let code_2 = &expand(
&code_1,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_2);
let code_3 = &expand(
&code_2,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_3);
let code_4 = &expand(
&code_3,
&keypad_map,
&keypad_arrows,
Stay,
&mut seed);
print_code(code_4);
println!("\n\n\nokay, i think i got it. now let's do the memoized solution.");
let mut bests: HashMap<(Dir, Dir), Vec<Dir>> = HashMap::new();
for prev in [Stay, East, West, North, South] {
for next in [Stay, East, West, North, South] {
let four_deep = shortest_seq(
keypad_arrows[&prev],
keypad_arrows[&next],
7,
vec![(0,3)]);
let mut result = four_deep;
for _ in 0..6 {
result = execute_code(&result, &keypad_map, keypad_arrows[&Stay]);
}
bests.insert((prev, next), result);
}
}
for (key, value) in &bests {
print!("{key:?}:");
print_code(&value);
}
let mut sum = 0;
for line in file.lines() {
let num: i32 = line[..line.len()-1].parse().expect("failed to parse door code as number");
println!("looking at code {line}, generating:");
print_code(&thru_keypads(line, 2));
print_code(&thru_keypads(line, 3));
print_code(&thru_keypads(line, 4));
let extra = 25;
let mut length = 0;
let mut prev = 'A';
for d in line.chars() {
length += length_calc_numpad(prev, d, 1+extra);
prev = d;
}
println!("length should be {length}");
sum += length * (num as i64);
}
println!("the answer is {sum}");
}
fn length_calc_numpad(prev: char, next: char, depth: i32) -> i64 {
let optimals: HashMap<(char, char), Vec<Dir>> = HashMap::from([
(('A', 'A'), vec![Stay]),
(('A', '0'), vec![West, Stay]),
(('A', '1'), vec![North, West, West, Stay]),
(('A', '2'), vec![West, North, Stay]),
(('A', '3'), vec![North, Stay]),
(('A', '4'), vec![North, North, West, West, Stay]),
(('A', '5'), vec![West, North, North, Stay]),
(('A', '6'), vec![North, North, Stay]),
(('A', '7'), vec![North, North, North, West, West, Stay]),
(('A', '8'), vec![West, North, North, North, Stay]),
(('A', '9'), vec![North, North, North, Stay]),
(('0', 'A'), vec![East, Stay]),
(('0', '0'), vec![Stay]),
(('0', '1'), vec![North, West, Stay]),
(('0', '2'), vec![North, Stay]),
(('0', '3'), vec![North, East, Stay]),
(('0', '4'), vec![North, North, West, Stay]),
(('0', '5'), vec![North, North, Stay]),
(('0', '6'), vec![North, North, East, Stay]),
(('0', '7'), vec![North, North, North, West, Stay]),
(('0', '8'), vec![North, North, North, Stay]),
(('0', '9'), vec![North, North, North, East, Stay]),
(('1', 'A'), vec![East, East, South, Stay]),
(('1', '0'), vec![East, South, Stay]),
(('1', '1'), vec![Stay]),
(('1', '2'), vec![East, Stay]),
(('1', '3'), vec![East, East, Stay]),
(('1', '4'), vec![North, Stay]),
(('1', '5'), vec![North, East, Stay]),
(('1', '6'), vec![North, East, East, Stay]),
(('1', '7'), vec![North, North, Stay]),
(('1', '8'), vec![North, North, East, Stay]),
(('1', '9'), vec![North, North, East, East, Stay]),
(('2', 'A'), vec![South, East, Stay]),
(('2', '0'), vec![South, Stay]),
(('2', '1'), vec![West, Stay]),
(('2', '2'), vec![Stay]),
(('2', '3'), vec![East, Stay]),
(('2', '4'), vec![West, North, Stay]),
(('2', '5'), vec![North, Stay]),
(('2', '6'), vec![East, North, Stay]),
(('2', '7'), vec![West, North, North, Stay]),
(('2', '8'), vec![North, North, Stay]),
(('2', '9'), vec![East, North, North, Stay]),
(('3', 'A'), vec![South, Stay]),
(('3', '0'), vec![West, South, Stay]),
(('3', '1'), vec![West, West, Stay]),
(('3', '2'), vec![West, Stay]),
(('3', '3'), vec![Stay]),
(('3', '4'), vec![West, West, North, Stay]),
(('3', '5'), vec![West, North, Stay]),
(('3', '6'), vec![North, Stay]),
(('3', '7'), vec![West, West, North, North, Stay]),
(('3', '8'), vec![West, North, Stay]),
(('3', '9'), vec![North, North, Stay]),
(('4', 'A'), vec![East, East, South, South, Stay]),
(('4', '0'), vec![East, South, South, Stay]),
(('4', '1'), vec![South, Stay]),
(('4', '2'), vec![South, East, Stay]),
(('4', '3'), vec![South, East, East, Stay]),
(('4', '4'), vec![Stay]),
(('4', '5'), vec![East, Stay]),
(('4', '6'), vec![East, East, Stay]),
(('4', '7'), vec![North, Stay]),
(('4', '8'), vec![North, East, Stay]),
(('4', '9'), vec![North, East, East, Stay]),
(('5', 'A'), vec![South, South, East, Stay]),
(('5', '0'), vec![South, South, Stay]),
(('5', '1'), vec![West, South, Stay]),
(('5', '2'), vec![South, Stay]),
(('5', '3'), vec![South, East, Stay]),
(('5', '4'), vec![West, Stay]),
(('5', '5'), vec![Stay]),
(('5', '6'), vec![East, Stay]),
(('5', '7'), vec![West, North, Stay]),
(('5', '8'), vec![North, Stay]),
(('5', '9'), vec![North, East, Stay]),
(('6', 'A'), vec![South, South, Stay]),
(('6', '0'), vec![West, South, South, Stay]),
(('6', '1'), vec![West, West, South, Stay]),
(('6', '2'), vec![West, South, Stay]),
(('6', '3'), vec![South, Stay]),
(('6', '4'), vec![West, West, Stay]),
(('6', '5'), vec![West, Stay]),
(('6', '6'), vec![Stay]),
(('6', '7'), vec![West, West, North, Stay]),
(('6', '8'), vec![West, North, Stay]),
(('6', '9'), vec![North, Stay]),
(('7', 'A'), vec![East, East, South, South, South, Stay]),
(('7', '0'), vec![East, South, South, South, Stay]),
(('7', '1'), vec![South, South, Stay]),
(('7', '2'), vec![South, South, East, Stay]),
(('7', '3'), vec![South, South, East, East, Stay]),
(('7', '4'), vec![South, Stay]),
(('7', '5'), vec![South, East, Stay]),
(('7', '6'), vec![South, East, East, Stay]),
(('7', '7'), vec![Stay]),
(('7', '8'), vec![East, Stay]),
(('7', '9'), vec![East, East, Stay]),
(('8', 'A'), vec![South, South, South, East, Stay]),
(('8', '0'), vec![South, South, South, Stay]),
(('8', '1'), vec![West, South, South, Stay]),
(('8', '2'), vec![South, South, Stay]),
(('8', '3'), vec![South, South, East, Stay]),
(('8', '4'), vec![West, South, Stay]),
(('8', '5'), vec![South, Stay]),
(('8', '6'), vec![South, East, Stay]),
(('8', '7'), vec![West, West, Stay]),
(('8', '8'), vec![Stay]),
(('8', '9'), vec![East, Stay]),
(('9', 'A'), vec![South, South, South, Stay]),
(('9', '0'), vec![West, South, South, South, Stay]),
(('9', '1'), vec![West, West, South, South, Stay]),
(('9', '2'), vec![West, South, South, Stay]),
(('9', '3'), vec![South, South, Stay]),
(('9', '4'), vec![West, West, South, Stay]),
(('9', '5'), vec![West, South, Stay]),
(('9', '6'), vec![South, Stay]),
(('9', '7'), vec![West, West, Stay]),
(('9', '8'), vec![West, Stay]),
(('9', '9'), vec![Stay]),
]);
if depth == 0 {
return 1;
} else {
let mut p = Stay;
let mut len = 0;
for d in &optimals[&(prev, next)] {
len += length_calc(p, *d, depth - 1);
p = *d;
}
return len;
}
}
#[memoize]
fn length_calc(prev: Dir, next: Dir, depth: i32) -> i64 {
let optimals: HashMap<(Dir, Dir), Vec<Dir>> = HashMap::from([
((Stay, Stay), vec![Stay]),
((Stay, North), vec![West, Stay]),
((Stay, East), vec![South, Stay]),
((Stay, South), vec![West, South, Stay]),
((Stay, West), vec![South, West, West, Stay]),
((North, Stay), vec![East, Stay]),
((North, North), vec![Stay]),
((North, South), vec![South, Stay]),
((North, East), vec![South, East, Stay]),
((North, West), vec![South, West, Stay]),
((South, Stay), vec![North, East, Stay]),
((South, North), vec![North, Stay]),
((South, South), vec![Stay]),
((South, East), vec![East, Stay]),
((South, West), vec![West, Stay]),
((East, Stay), vec![North, Stay]),
((East, North), vec![West, North, Stay]),
((East, South), vec![West, Stay]),
((East, East), vec![Stay]),
((East, West), vec![West, West, Stay]),
((West, Stay), vec![East, East, North, Stay]),
((West, North), vec![East, North, Stay]),
((West, South), vec![East, Stay]),
((West, East), vec![East, East, Stay]),
((West, West), vec![Stay]),
]);
if depth == 0 {
return 1;
} else {
let mut p = Stay;
let mut len = 0;
for d in &optimals[&(prev, next)] {
len += length_calc(p, *d, depth - 1);
p = *d;
}
return len;
}
}
fn expandaband(sequence: Vec<Dir>, optimals: &HashMap<(Dir, Dir), Vec<Dir>>) -> Vec<Dir> {
let mut output = vec![];
let mut prev = Stay;
for dir in sequence {
output.extend(optimals.get(&(prev, dir)).unwrap());
prev = dir;
}
return output;
}
fn thru_keypads(code: &str, keypads: i32) -> Vec<Dir> {
let keypad_door: HashMap<char, Coord> = HashMap::from([
('7', (0,0)), ('8', (1,0)), ('9', (2,0)),
('4', (0,1)), ('5', (1,1)), ('6', (2,1)),
('1', (0,2)), ('2', (1,2)), ('3', (2,2)),
('0', (1,3)), ('A', (2,3)),
]);
let mut prev = 'A';
let mut output = vec![];
for c in code.chars() {
//println!("navigating from {prev} to {c}");
let shortest = shortest_seq(
keypad_door[&prev],
keypad_door[&c],
keypads-1,
vec![(0,3)]);
output.extend(shortest);
prev = c;
}
return output;
}
#[memoize]
fn shortest_seq(
start: Coord,
end: Coord,
keypads: i32,
blacklist: Vec<Coord>,
) -> Vec<Dir> {
// starting at start what is the shortest sequence to navigate to and press end,
// through many keypads?
let keypad: HashMap<Dir, Coord> = HashMap::from([
(North, (1,0)), (Stay, (2,0)),
(West, (0,1)), (South, (1,1)), (East, (2,1)),
]);
let paths: Vec<_> = all_paths(start, end)
.into_iter()
.filter(|p| traversal_avoids(start, p, &blacklist))
.collect();
//println!("with {keypads} left");
if keypads == 1 {
let mut output = vec![];
output.extend(paths[0].clone());
output.push(Stay);
return output;
} else {
let mut next_keypad_paths = vec![];
for path in paths {
let mut longer = vec![];
let mut prev = keypad[&Stay];
for dir in path {
longer.extend(
shortest_seq(
prev,
keypad[&dir],
keypads-1,
vec![(0,0)]
)
);
prev = keypad[&dir];
}
longer.extend(
shortest_seq(
prev,
keypad[&Stay],
keypads-1,
vec![(0,0)]
)
);
next_keypad_paths.push(longer);
}
let mut shortest = &next_keypad_paths[0];
for path in &next_keypad_paths {
if path.len() < shortest.len() {
shortest = path;
}
}
return shortest.to_vec();
}
}
fn traversal_avoids(start: Coord, path: &Vec<Dir>, blacklist: &Vec<Coord>) -> bool {
let mut here = start;
let mut hit = blacklist.contains(&here);
for d in path {
here = step(&here, *d, 1);
hit = hit || blacklist.contains(&here);
}
return !hit;
}
fn all_paths(prev: Coord, next: Coord) -> Vec<Vec<Dir>> {
let mut out = vec![];
match (next.0-prev.0, next.1-prev.1) {
(0, 0) => out.push(vec![]),
(mut x, 0) => {
let mut horizontal = vec![];
while x > 0 { horizontal.push(East); x-=1; }
while x < 0 { horizontal.push(West); x+=1; }
out.push(horizontal);
},
(0, mut y) => {
let mut vertical = vec![];
while y > 0 { vertical.push(South); y-=1; }
while y < 0 { vertical.push(North); y+=1; }
out.push(vertical);
},
(_x, _y) => {
for p1 in all_paths(prev, (next.0, prev.1)) {
for p2 in all_paths((next.0, prev.1), next) {
let mut p12 = vec![];
p12.extend(p1.clone());
p12.extend(p2);
out.push(p12);
}
}
for p1 in all_paths(prev, (prev.0, next.1)) {
for p2 in all_paths((prev.0, next.1), next) {
let mut p12 = vec![];
p12.extend(p1.clone());
p12.extend(p2);
out.push(p12);
}
}
}
}
return out;
}
fn expand<T: std::cmp::Eq + std::hash::Hash>(
sequence: &Vec<T>,
map: &HashMap<Coord, T>,
keypad: &HashMap<T, Coord>,
start: T,
seed: &mut u128
) -> Vec<Dir> {
let mut here = keypad[&start];
let mut output = Vec::new();
for t in sequence {
let there = keypad[&t];
let diff = (there.0 - here.0, there.1 - here.1);
output.extend(build_path(diff, seed));
here = there;
}
return output;
}
fn build_path(diff: Coord, seed: &mut u128) -> Vec<Dir> {
let mut output = vec![];
match diff {
(0, 0) => output.push(Stay),
(x, 0) => {
for _ in 0..x.abs() {
if x < 0 { output.push(West); }
if x > 0 { output.push(East); }
}
output.push(Stay);
},
(0, y) => {
for _ in 0..y.abs() {
if y < 0 { output.push(North); }
if y > 0 { output.push(South); }
}
output.push(Stay);
},
(x, y) => {
if *seed & 1 == 0 {
for _ in 0..y.abs() {
if y < 0 { output.push(North); }
if y > 0 { output.push(South); }
}
for _ in 0..x.abs() {
if x < 0 { output.push(West); }
if x > 0 { output.push(East); }
}
} else {
for _ in 0..x.abs() {
if x < 0 { output.push(West); }
if x > 0 { output.push(East); }
}
for _ in 0..y.abs() {
if y < 0 { output.push(North); }
if y > 0 { output.push(South); }
}
}
output.push(Stay);
*seed = *seed >> 1;
}
}
return output;
}
fn sequences<T>(options: Vec<Vec<(Dir, i32)>>, map: &HashMap<Coord, T>, start: Coord) -> Vec<Vec<Dir>> {
let mut out: Vec<(Vec<Dir>, Coord, Dir)> = vec![(vec![], start, Stay)];
@ -278,8 +803,4 @@ fn execute_code<T: Clone + Copy>(
}
return out;
}
}

6
day23/Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "day23"
version = "0.1.0"
edition = "2021"
[dependencies]

124
day23/src/main.rs Normal file
View File

@ -0,0 +1,124 @@
use std::collections::HashSet;
use std::collections::HashMap;
use std::env;
use std::fs;
fn main() {
println!("Hello, AoC day 23!");
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
println!("wrong number of arguments!");
std::process::exit(1);
}
let file_path = &args[1];
let file = fs::read_to_string(file_path)
.expect("should be able to read the file");
println!("building adjacency matrix");
let mut adjacency = HashMap::new();
let mut edges = HashSet::new();
for line in file.lines() {
let (a, b) = line.split_once("-").expect("badly formatted line");
let a_edges = adjacency.entry(a).or_insert(vec![]);
a_edges.push(b);
let b_edges = adjacency.entry(b).or_insert(vec![]);
b_edges.push(a);
edges.insert((a,b));
edges.insert((b,a));
}
println!("sorting edges");
for (_, edges) in adjacency.iter_mut() {
edges.sort();
}
// part a
println!("looking for 3-cliques");
let mut cliques = HashSet::new();
for (&a, a_connections) in &adjacency {
for &b in a_connections {
for &c in adjacency.get(b).unwrap() {
if edges.contains(&(a,c)) {
let mut clique = vec![a, b, c];
clique.sort();
cliques.insert(clique);
}
}
}
}
let mut sorted_cliques = cliques.iter().collect::<Vec<_>>();
sorted_cliques.sort();
let mut count_t = 0;
for c in sorted_cliques {
//println!("{},{},{}", c[0], c[1], c[2]);
let mut found = false;
for computer in c {
if computer.starts_with("t") {
found = true;
}
}
if found {
count_t += 1;
}
}
println!("found {count_t} cliques with a computer with t in the name");
// part b
let mut done = false;
while !done {
done = true;
let mut new_cliques = HashSet::new();
for old_clique in cliques.iter() {
let mut computers = old_clique.clone();
let me = computers[0];
for you in adjacency.get(me).unwrap() {
if computers.contains(you) {
continue;
}
let mut accepted = true;
for them in &computers {
if *them == me {
continue;
} else if !edges.contains(&(you, *them)) {
accepted = false;
}
}
// grow the clique iff 'you' is connected to all the members of the clique
if accepted {
done = false;
computers.push(you);
break;
}
}
computers.sort();
new_cliques.insert(computers);
}
cliques = new_cliques;
}
let mut clique_size = 0;
for clique in &cliques {
// for comp in clique { print!("{comp},"); }
// print!("\n");
clique_size = clique_size.max(clique.len());
}
println!("the largest clique is {clique_size} computers");
for clique in &cliques {
if clique.len() == clique_size {
for comp in clique { print!("{comp},"); }
print!("\n");
}
}
}