From 28f5eb17b0929dcca86f3d551ef8969c362fda7f Mon Sep 17 00:00:00 2001 From: shoofle Date: Tue, 24 Dec 2024 00:32:55 -0500 Subject: [PATCH] day 23. also, long term nuclear warning messages in day 21. --- day21/Cargo.toml | 1 + day21/src/main.rs | 533 +++++++++++++++++++++++++++++++++++++++++++++- day23/Cargo.toml | 6 + day23/src/main.rs | 124 +++++++++++ 4 files changed, 658 insertions(+), 6 deletions(-) create mode 100644 day23/Cargo.toml create mode 100644 day23/src/main.rs diff --git a/day21/Cargo.toml b/day21/Cargo.toml index ed0c318..879c9cd 100644 --- a/day21/Cargo.toml +++ b/day21/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +memoize = "0.4.2" diff --git a/day21/src/main.rs b/day21/src/main.rs index 63d3252..5bde746 100644 --- a/day21/src/main.rs +++ b/day21/src/main.rs @@ -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> = 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> = 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> = 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, optimals: &HashMap<(Dir, Dir), Vec>) -> Vec { + 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 { + let keypad_door: HashMap = 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, +) -> Vec { + // starting at start what is the shortest sequence to navigate to and press end, + // through many keypads? + let keypad: HashMap = 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, blacklist: &Vec) -> 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> { + 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( + sequence: &Vec, + map: &HashMap, + keypad: &HashMap, + start: T, + seed: &mut u128 +) -> Vec { + 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 { + 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(options: Vec>, map: &HashMap, start: Coord) -> Vec> { let mut out: Vec<(Vec, Coord, Dir)> = vec![(vec![], start, Stay)]; @@ -278,8 +803,4 @@ fn execute_code( } return out; -} - - - - +} \ No newline at end of file diff --git a/day23/Cargo.toml b/day23/Cargo.toml new file mode 100644 index 0000000..2f4996b --- /dev/null +++ b/day23/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day23" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day23/src/main.rs b/day23/src/main.rs new file mode 100644 index 0000000..09b8915 --- /dev/null +++ b/day23/src/main.rs @@ -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 = 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::>(); + 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"); + } + } +} \ No newline at end of file