diff --git a/day19/src/main.rs b/day19/src/main.rs index 0957e3b..25008e8 100644 --- a/day19/src/main.rs +++ b/day19/src/main.rs @@ -1,8 +1,23 @@ +use std::collections::HashSet; use std::collections::HashMap; use std::env; use std::fs; -use regex::Regex; +use crate::Dir::{North, South, East, West }; + + +type Coord = (i32, i32); +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)] +enum Dir { North, South, East, West } + +fn step(start: &Coord, d: Dir, steps: i32) -> Coord { + match d { + North => (start.0, start.1 - steps), + South => (start.0, start.1 + steps), + East => (start.0 + steps, start.1), + West => (start.0 - steps, start.1), + } +} fn main() { println!("Hello, AoC day 13!"); @@ -18,109 +33,69 @@ fn main() { let binding = fs::read_to_string(file_path) .expect("should be able to read the file"); - let (towels, designs) = binding.split_once("\n\n").expect("should have two sections"); + let mut map = HashMap::new(); + let mut distances_to_end: HashMap = HashMap::new(); - let all_towels: Vec<&str> = towels.split(", ").collect(); - - let re = Regex::new( - &format!("^({})+$", all_towels.join("|")) - ).expect("making the regex"); - - let mut count = 0; - for line in designs.lines() { - if re.is_match(line) { - count += 1; + let mut start = (0,0); + let mut exit = (0,0); + let mut y = 0; + for line in binding.lines() { + let mut x = 0; + for c in line.chars() { + match c { + 'S' => { start = (x,y); map.insert((x,y), '.'); }, + 'E' => { exit = (x,y); map.insert((x,y), '.'); }, + _ => { map.insert((x,y), c); } + }; + x += 1; } + y += 1; } - println!("found {count} valid designs"); + distances_to_end.insert(exit, 0); + let visited: HashSet = HashSet::new(); + let mut wavefront: Vec = vec![exit]; + while let Some(here) = wavefront.pop() { + let here_distance = distances_to_end.get(&here).unwrap().clone(); + for x in neighbors(&here) { + if map.get(&x) != Some(&'.') { + continue; + } + if visited.contains(&x) { + continue; + } + if let Some(d) = distances_to_end.get(&x) { + if *d < here_distance { + continue; + } + } + distances_to_end.insert(x, here_distance + 1); + wavefront.push(x); + } - let mut tree = build_tree(&all_towels); - - //println!("{tree:?}"); - println!("{} nodes in the tree", tree.len()); - - let mut count = 0; - - for line in designs.lines() { - count += eat(&mut tree, line); } - println!("found {count} solutions total"); + println!("the distance from {start:?} to {exit:?} is {}", distances_to_end.get(&start).unwrap()); + } -fn eat(tree: &mut Vec, haystack: &str) -> u128 { - let mut heads: HashMap = HashMap::new(); - heads.insert(0, 1); - - //println!("examining {haystack}"); - - for c in haystack.chars() { - // iterating by character - let mut new_heads = HashMap::new(); - - //println!("looking at {c}; the heads we're tracking are {heads:?}"); - - // this first loop branches to the start from valid states - for (node_idx, num_heads) in heads.drain() { - let TreeCell { next: _, valid } = &tree[node_idx]; - if *valid { - let next_idx = 0; - let existing_heads = new_heads.get(&next_idx).unwrap_or(&0); - new_heads.insert(next_idx, existing_heads + num_heads); - } - let existing_heads = new_heads.get(&node_idx).unwrap_or(&0); - new_heads.insert(node_idx, existing_heads + num_heads); - } - - // this second loop advances all the heads - for (node_idx, num_heads) in new_heads.drain() { - let TreeCell { next, valid: _ } = &tree[node_idx]; - if let Some(next_idx) = next.get(&c) { - heads.insert(*next_idx, num_heads); - } - } - } - - let mut count = 0; - - for (node_idx, number) in heads { - if tree[node_idx].valid { count += number; } - } - //println!("there are {count} heads in success states"); - - return count; +fn second_neighbors(start: &Coord) -> Vec { + vec![ + step(start, North, 2), + step(&step(start, North, 1), West, 1), + step(&step(start, North, 1), East, 1), + step(start, West, 2), + step(start, East, 2), + step(&step(start, West, 1), South, 1), + step(&step(start, East, 1), South, 1), + step(start, South, 2) + ] } - - -#[derive(PartialEq, Eq, Clone, Debug)] -struct TreeCell {next: HashMap, valid: bool} - -fn build_tree(patterns: &[&str]) -> Vec { - let mut cells = vec![TreeCell {next: HashMap::new(), valid: false}]; - - for stripes in patterns { - add_to_tree(stripes, &mut cells, 0); - } - - return cells; -} - -fn add_to_tree(pattern: &str, tree: &mut Vec, index: usize) { - match pattern.chars().next() { - None => tree[index].valid = true, - Some(c) => { - if tree[index].next.contains_key(&c) { - add_to_tree(&pattern[1..], tree, *tree[index].next.get(&c).unwrap()); - } else { - let new_index = tree.len(); - let new_treecell = TreeCell { next: HashMap::new(), valid: false }; - tree.push(new_treecell); // now this is at new_index - tree[index].next.insert(c, new_index); - - add_to_tree(&pattern[1..], tree, new_index); - - } - } - } +fn neighbors(start: &Coord) -> Vec { + vec![ + step(start, North, 1), + step(start, West, 1), + step(start, East, 1), + step(start, South, 1) + ] } \ No newline at end of file diff --git a/day20/Cargo.toml b/day20/Cargo.toml new file mode 100644 index 0000000..156d38d --- /dev/null +++ b/day20/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day20" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day20/src/main.rs b/day20/src/main.rs new file mode 100644 index 0000000..de668e1 --- /dev/null +++ b/day20/src/main.rs @@ -0,0 +1,183 @@ +use std::collections::HashSet; +use std::collections::HashMap; +use std::env; +use std::fs; + +use crate::Dir::{North, South, East, West }; + + +type Coord = (i32, i32); +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)] +enum Dir { North, South, East, West } + +fn step(start: &Coord, d: Dir, steps: i32) -> Coord { + match d { + North => (start.0, start.1 - steps), + South => (start.0, start.1 + steps), + East => (start.0 + steps, start.1), + West => (start.0 - steps, start.1), + } +} + +fn main() { + println!("Hello, AoC day 13!"); + + 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 binding = fs::read_to_string(file_path) + .expect("should be able to read the file"); + + let mut map = HashMap::new(); + + let mut start = (0,0); + let mut exit = (0,0); + let mut y = 0; + for line in binding.lines() { + let mut x = 0; + for c in line.chars() { + match c { + 'S' => { start = (x,y); map.insert((x,y), '.'); }, + 'E' => { exit = (x,y); map.insert((x,y), '.'); }, + _ => { map.insert((x,y), c); } + }; + x += 1; + } + y += 1; + } + + let mut distances_to_end: HashMap = HashMap::new(); + distances_to_end.insert(exit, 0); + let mut visited: HashSet = HashSet::new(); + let mut wavefront: Vec = vec![exit]; + while let Some(here) = wavefront.pop() { + let here_distance = distances_to_end.get(&here).unwrap().clone(); + for x in neighbors(&here) { + if map.get(&x) != Some(&'.') { + continue; + } + if visited.contains(&x) { + continue; + } + if let Some(d) = distances_to_end.get(&x) { + if *d < here_distance { + continue; + } + } + distances_to_end.insert(x, here_distance + 1); + wavefront.push(x); + } + visited.insert(here); + } + + println!("the distance from {start:?} to {exit:?} is {}", distances_to_end.get(&start).unwrap()); + + let mut cheats: HashMap<(Coord, Coord), i32> = HashMap::new(); + + for (here, distance) in &distances_to_end { + for there in second_neighbors(&here) { + if let Some(next_distance) = distances_to_end.get(&there) { + cheats.insert((*here, there), distance-next_distance); + } + } + } + + let mut counts: HashMap = HashMap::new(); + + for (_, advantage) in cheats { + counts.insert(advantage, counts.get(&advantage).unwrap_or(&0) + 1); + } + + let mut count = 0; + for (x, c) in counts { + if x > 100 { + count += c; + } + } + + println!("there were {count} cheats worth more than 100"); + + // part b + + let mut cheats: HashMap<(Coord, Coord), i32> = HashMap::new(); + + for (here, distance) in &distances_to_end { + for there in twenty_step_neighbors(&here) { + if let Some(next_distance) = distances_to_end.get(&there) { + let cheat_length = (here.0 - there.0).abs() + (here.1 - there.1).abs(); + cheats.insert((*here, there), distance-next_distance - cheat_length); + } + } + } + + let mut counts: HashMap = HashMap::new(); + + for (_, advantage) in cheats { + counts.insert(advantage, counts.get(&advantage).unwrap_or(&0) + 1); + } + + let mut count = 0; + for (x, c) in counts { + if x >= 100 { + count += c; + } + } + + println!("there were {count} cheats worth more than 100"); +} + +fn build_cheat_offsets() -> Vec { + let mut all = vec![(0,0)]; + + for _ in 0..20 { + all = all.into_iter().flat_map(|x| neighbors(&x)).collect(); + } + + return all +} + +fn offset_neighbors(start: &Coord, offsets: &Vec) -> Vec { + let mut v: Vec = offsets.clone(); + for x in v.iter_mut() { + x.0 = x.0 + start.0; + x.1 = x.1 + start.1; + } + return v; +} + +fn twenty_step_neighbors(start: &Coord) -> Vec { + let mut v: Vec = Vec::with_capacity(400); + for x in -20_i32..21 { + for y in -20_i32..21 { + if x.abs()+y.abs() <= 20 { + v.push(step(&step(start, North, y), East, x)); + } + } + } + return v; +} +fn second_neighbors(start: &Coord) -> Vec { + vec![ + step(start, North, 2), + step(&step(start, North, 1), West, 1), + step(&step(start, North, 1), East, 1), + step(start, West, 2), + step(start, East, 2), + step(&step(start, West, 1), South, 1), + step(&step(start, East, 1), South, 1), + step(start, South, 2) + ] +} +fn neighbors(start: &Coord) -> Vec { + vec![ + step(start, North, 1), + step(start, West, 1), + step(start, East, 1), + step(start, South, 1) + ] +} \ No newline at end of file diff --git a/day21/Cargo.toml b/day21/Cargo.toml new file mode 100644 index 0000000..ed0c318 --- /dev/null +++ b/day21/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day21" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day21/src/main.rs b/day21/src/main.rs new file mode 100644 index 0000000..bdd9fb5 --- /dev/null +++ b/day21/src/main.rs @@ -0,0 +1,349 @@ +use std::collections::HashSet; +use std::collections::HashMap; +use std::env; +use std::fs; + +use crate::Dir::{North, South, East, West, Stay }; + + +type Coord = (i32, i32); +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)] +enum Dir { North, South, East, West, Stay } + +fn step(start: &Coord, d: Dir, steps: i32) -> Coord { + match d { + North => (start.0, start.1 - steps), + South => (start.0, start.1 + steps), + East => (start.0 + steps, start.1), + West => (start.0 - steps, start.1), + Stay => *start, + } +} + +fn walk_path(start: &Coord, path: &[Dir]) -> Coord { + let mut here = *start; + for d in path { + here = step(&here, *d, 1); + } + return here; +} + +fn main() { + println!("Hello, AoC day 21!"); + + 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"); + + 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 door_map: HashMap = HashMap::new(); + for (key, loc) in &keypad_door { door_map.insert(*loc, *key); } + let mut directions_matrix: HashMap<(Coord, Coord), Vec<(Dir, i32)>> = HashMap::new(); + for (_this, here) in &keypad_door { + for (_other, there) in &keypad_door { + let mut v = vec![]; + if there.0 > here.0 { + v.push((East, there.0 - here.0)); + } + if there.0 < here.0 { + v.push((West, here.0 - there.0)); + } + if there.1 > here.1 { + v.push((South, there.1 - here.1)); + } + if there.1 < here.1 { + v.push((North, here.1 - there.1)); + } + if there == here { + v.push((Stay, 0)); + } + directions_matrix.insert((*here, *there), v); + } + } + + + let keypad_arrows: HashMap = HashMap::from([ + (North, (1,0)), (Stay, (2,0)), + (West, (0,1)), (South, (1,1)), (East, (2,1)), + ]); + let mut keypad_map: HashMap = HashMap::new(); + for (key, loc) in &keypad_arrows { keypad_map.insert(*loc, *key); } + let mut keypad_directions_matrix = HashMap::new(); + for (_this, here) in &keypad_arrows { + for (_other, there) in &keypad_arrows { + let mut v = vec![]; + if there.0 > here.0 { + v.push((East, there.0 - here.0)); + } + if there.0 < here.0 { + v.push((West, here.0 - there.0)); + } + if there.1 > here.1 { + v.push((South, there.1 - here.1)); + } + if there.1 < here.1 { + v.push((North, here.1 - there.1)); + } + if there == here { + v.push((Stay, 0)); + } + keypad_directions_matrix.insert((*here, *there), v); + } + } + + let mut sum = 0; + for line in file.lines() { + let door_code: Vec = line.chars().collect(); + let num: i32 = line[..line.len()-1].parse().expect("failed to parse door code as number"); + let mut complexity: i64 = 100000000000; + + for x in &door_code { + print!("{}", x); + } + print!("\n"); + + let options_door = build_options( + &directions_matrix, + &keypad_door, + &door_code, + 'A'); + for arrow_code_1 in shortest_sequences(options_door, &door_map, keypad_door[&'A']) { + print_code(&arrow_code_1); + + let options_arrow_1 = build_options( + &keypad_directions_matrix, + &keypad_arrows, + &arrow_code_1, + Stay); + for arrow_code_2 in shortest_sequences(options_arrow_1, &keypad_map, keypad_arrows[&Stay]) { + print_code(&arrow_code_2); + + let options_arrow_2 = build_options( + &keypad_directions_matrix, + &keypad_arrows, + &arrow_code_2, + Stay); + for arrow_code_3 in shortest_sequences(options_arrow_2, &keypad_map, keypad_arrows[&Stay]) { + complexity = complexity.min(arrow_code_3.len() as i64); + print_code(&arrow_code_3); + } + } + } + println!("shortest sequence is {} long", complexity); + sum += complexity * (num as i64); + } + + println!("the sum of the complexity scores is {}", sum); + + // println!("printing out the anomalous code"); + + // let code = "<A>^AAvA<^A>AvA^A<>^AAvA^A^AAA<A>^AAAvA<^A>A"; + // let code_dirs = code.chars().map(|x| match x { + // '<' => West, '>' => East, '^' => North, 'v' => South, 'A' => Stay, _ => panic!() + // }).collect(); + // print_code(&code_dirs); + // println!("resulting keypresses:"); + // let code_2 = execute_code(&code_dirs, &keypad_map, keypad_arrows[&Stay]); + // print_code(&code_2); + // println!("resulting keypresses:"); + // let code_3 = execute_code(&code_2, &keypad_map, keypad_arrows[&Stay]); + // print_code(&code_3); + // println!("resulting in:"); + // let code_4 = execute_code(&code_3, &door_map, keypad_door[&'A']); + // print_code_door(&code_4); + + +} + +fn first_sequence(options: Vec>, map: &HashMap, start: Coord) -> Vec { + let mut out = vec![]; + let mut here = start; + + for choices in options { + if choices.len() == 1 { + let (direction, times) = choices[0]; + for _ in 0..times { + out.push(direction); + } + here = step(&here, direction, times) + } else if choices.len() == 2 { + let (d1, t1) = choices[0]; + let (d2, t2) = choices[1]; + if map.contains_key(&step(&here, d1, t1)) { + for _ in 0..t1 { + out.push(d1); + } + + for _ in 0..t2 { + out.push(d2); + } + } else { + for _ in 0..t2 { + out.push(d2); + } + + for _ in 0..t1 { + out.push(d1); + } + } + + + } + } + return out; +} +fn sequences(options: Vec>, map: &HashMap, start: Coord) -> Vec> { + let mut out: Vec<(Vec, Coord)> = vec![(vec![], start)]; + + for choices in options { + let mut new_out = vec![]; + + if choices.len() == 1 { + let (direction, times) = choices[0]; + for (mut walk, mut here) in out { + for _ in 0..times { + walk.push(direction); + here = step(&here, direction, 1); + } + new_out.push((walk, here)); + } + } + + else if choices.len() == 2 { + let (d1, t1) = choices[0]; + let (d2, t2) = choices[1]; + + for (ref walk, here) in &mut out { + let end = step(&step(&here, d1, t1), d2, t2); + let candidate_here = step(&here, d1, t1); + if map.contains_key(&candidate_here) { + let mut ab_walk = walk.clone(); + + for _ in 0..t1 { ab_walk.push(d1); } + for _ in 0..t2 { ab_walk.push(d2); } + + new_out.push((ab_walk, end)); + } + + let candidate_there = step(&here, d2, t2); + if map.contains_key(&candidate_there) { + let mut ba_walk = walk.clone(); + + for _ in 0..t2 { ba_walk.push(d2); } + for _ in 0..t1 { ba_walk.push(d1); } + + new_out.push((ba_walk, end)); + } + + } + } else { + panic!("encountered a bad set of choices"); + } + + out = new_out; + } + + let mut final_out = vec![]; + for (walk, _) in out { + final_out.push(walk); + } + return final_out; +} + +fn shortest_sequences( + options: Vec>, + map: &HashMap, + start: Coord +) -> Vec> { + let mut short_length = None; + let mut shorts = vec![]; + + for seq in sequences(options.to_vec(), map, start) { + let my_len = seq.len(); + if let Some(len) = short_length { + if my_len == len { + shorts.push(seq); + } else if my_len < len { + shorts = vec![seq]; + short_length = Some(my_len); + } + } else { + short_length = Some(my_len); + shorts.push(seq); + } + } + + return shorts; +} + +fn build_options( + routes: &HashMap<(Coord, Coord), Vec<(Dir, i32)>>, + map: &HashMap, + sequence: &Vec, + start: T, +) -> Vec> { + let mut here = map[&start]; + let mut options: Vec> = Vec::new(); + for button in sequence { + let there = map[&button]; + options.push(routes[&(here, there)].clone()); + options.push(vec![(Stay, 1)]); + here = there; + } + + return options; +} + + +fn print_code(code: &Vec) { + for x in code { + print!("{}", match x { + North => '^', South => 'v', East => '>', West => '<', Stay => 'A' + }); + } + print!("\n"); +} + +fn print_code_door(code: &Vec) { + for x in code { + print!("{}", x); + } + print!("\n"); +} + + +fn execute_code( + code: &Vec, + map: &HashMap, + start: Coord +) -> Vec { + + let mut out = vec![]; + let mut here = start; + for d in code { + if *d == Stay { + out.push(map[&here]); + } else { + here = step(&here, *d, 1); + } + + } + return out; + +} + + + +