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