feat: solve day 12 part 1 & 2
This commit is contained in:
parent
b0914ebb2b
commit
d27f25036a
24
input/12.txt
Normal file
24
input/12.txt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
pf-pk
|
||||||
|
ZQ-iz
|
||||||
|
iz-NY
|
||||||
|
ZQ-end
|
||||||
|
pf-gx
|
||||||
|
pk-ZQ
|
||||||
|
ZQ-dc
|
||||||
|
NY-start
|
||||||
|
NY-pf
|
||||||
|
NY-gx
|
||||||
|
ag-ZQ
|
||||||
|
pf-start
|
||||||
|
start-gx
|
||||||
|
BN-ag
|
||||||
|
iz-pf
|
||||||
|
ag-FD
|
||||||
|
pk-NY
|
||||||
|
gx-pk
|
||||||
|
end-BN
|
||||||
|
ag-pf
|
||||||
|
iz-pk
|
||||||
|
pk-ag
|
||||||
|
iz-end
|
||||||
|
iz-BN
|
160
src/day12.rs
Normal file
160
src/day12.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
pub fn parse_input(input: &str) -> Vec<(String, String)> {
|
||||||
|
let mut edges: Vec<(String, String)> = Vec::new();
|
||||||
|
|
||||||
|
for line in input.lines() {
|
||||||
|
let (from, to) = line.split_once('-').unwrap();
|
||||||
|
edges.push((from.into(), to.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edges_to_map(edges: &Vec<(String, String)>) -> HashMap<String, Vec<String>> {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
for line in edges {
|
||||||
|
map.entry(line.1.clone())
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push(line.0.clone());
|
||||||
|
|
||||||
|
map.entry(line.0.clone())
|
||||||
|
.or_insert(Vec::new())
|
||||||
|
.push(line.1.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_path_finished(path: &Vec<&str>) -> bool {
|
||||||
|
return path.contains(&"end")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_be_appended_part1(path: &Vec<&str>, node: &str) -> bool {
|
||||||
|
node.to_uppercase() == node || !path.contains(&node)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part1(edges: &Vec<(String, String)>) -> usize {
|
||||||
|
let map = edges_to_map(edges);
|
||||||
|
|
||||||
|
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
|
||||||
|
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
unfinished_paths.push(vec!["start"]);
|
||||||
|
while unfinished_paths.len() > 0 {
|
||||||
|
let mut new_paths = Vec::new();
|
||||||
|
|
||||||
|
for path in &mut unfinished_paths {
|
||||||
|
for node in map.get(*path.last().unwrap()).unwrap() {
|
||||||
|
if can_be_appended_part1(path, node) {
|
||||||
|
let mut new_path = path.clone();
|
||||||
|
new_path.push(node);
|
||||||
|
|
||||||
|
if is_path_finished(&new_path) {
|
||||||
|
finished_paths.push(new_path);
|
||||||
|
} else {
|
||||||
|
new_paths.push(new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unfinished_paths = new_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
return finished_paths.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_be_appended_part2(path: &Vec<&str>, node: &str) -> bool {
|
||||||
|
if node == "start" { return false; }
|
||||||
|
if node == "end" { return true; }
|
||||||
|
if node.to_uppercase() == node { return true; }
|
||||||
|
|
||||||
|
// If all lowercase nodes only apear once we can be assure that any lowercase
|
||||||
|
// node that will be added will be correct.
|
||||||
|
let mut uniq = HashSet::new();
|
||||||
|
if path.into_iter().all(move |x| x.to_lowercase() != *x || uniq.insert(x)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !path.contains(&node);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part2(edges: &Vec<(String, String)>) -> usize {
|
||||||
|
let map = edges_to_map(edges);
|
||||||
|
|
||||||
|
let mut finished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
|
||||||
|
let mut unfinished_paths: Vec<Vec<&str>> = Vec::new();
|
||||||
|
unfinished_paths.push(vec!["start"]);
|
||||||
|
while unfinished_paths.len() > 0 {
|
||||||
|
let mut new_paths = Vec::new();
|
||||||
|
|
||||||
|
for path in &mut unfinished_paths {
|
||||||
|
for node in map.get(*path.last().unwrap()).unwrap() {
|
||||||
|
if can_be_appended_part2(path, node) {
|
||||||
|
let mut new_path = path.clone();
|
||||||
|
new_path.push(node);
|
||||||
|
|
||||||
|
if is_path_finished(&new_path) {
|
||||||
|
finished_paths.push(new_path);
|
||||||
|
} else {
|
||||||
|
new_paths.push(new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unfinished_paths = new_paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
return finished_paths.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
||||||
|
let result = part1(&cave_system);
|
||||||
|
assert_eq!(result, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_larger_example() {
|
||||||
|
let cave_system = parse_input("dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc");
|
||||||
|
let result = part1(&cave_system);
|
||||||
|
assert_eq!(result, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part1_largest_example() {
|
||||||
|
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
||||||
|
let result = part1(&cave_system);
|
||||||
|
assert_eq!(result, 226);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {
|
||||||
|
let cave_system = parse_input("start-A\nstart-b\nA-c\nA-b\nb-d\nA-end\nb-end");
|
||||||
|
let result = part2(&cave_system);
|
||||||
|
assert_eq!(result, 36);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_larger_example() {
|
||||||
|
let cave_system = parse_input("dc-end\nHN-start\nstart-kj\ndc-start\ndc-HN\nLN-dc\nHN-end\nkj-sa\nkj-HN\nkj-dc");
|
||||||
|
let result = part2(&cave_system);
|
||||||
|
assert_eq!(result, 103);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part2_largest_example() {
|
||||||
|
let cave_system = parse_input("fs-end\nhe-DX\nfs-he\nstart-DX\npj-DX\nend-zg\nzg-sl\nzg-pj\npj-he\nRW-he\nfs-DX\npj-RW\nzg-RW\nstart-pj\nhe-WI\nzg-he\npj-fs\nstart-RW");
|
||||||
|
let result = part2(&cave_system);
|
||||||
|
assert_eq!(result, 3509);
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ mod day8;
|
|||||||
mod day9;
|
mod day9;
|
||||||
mod day10;
|
mod day10;
|
||||||
mod day11;
|
mod day11;
|
||||||
|
mod day12;
|
||||||
|
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -47,6 +48,8 @@ fn run(day: i32, part: i32, input_filename: &str) {
|
|||||||
"10.2" => println!("{}", day10::part2(&day10::parse_input(&contents))),
|
"10.2" => println!("{}", day10::part2(&day10::parse_input(&contents))),
|
||||||
"11.1" => println!("{}", day11::part1(&day11::parse_input(&contents))),
|
"11.1" => println!("{}", day11::part1(&day11::parse_input(&contents))),
|
||||||
"11.2" => println!("{}", day11::part2(&day11::parse_input(&contents))),
|
"11.2" => println!("{}", day11::part2(&day11::parse_input(&contents))),
|
||||||
|
"12.1" => println!("{}", day12::part1(&day12::parse_input(&contents))),
|
||||||
|
"12.2" => println!("{}", day12::part2(&day12::parse_input(&contents))),
|
||||||
_ => println!("Day {} part {} not found", day, part)
|
_ => println!("Day {} part {} not found", day, part)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user