2024-10-29 13:13:50 +01:00
|
|
|
use core::panic;
|
|
|
|
use std::{
|
|
|
|
collections::{HashMap, HashSet},
|
|
|
|
error::Error,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn process_part1(input: &str) -> u32 {
|
|
|
|
let mut distances = HashMap::new();
|
|
|
|
let mut locations = HashSet::new();
|
|
|
|
input.lines().for_each(|line| {
|
|
|
|
let (direction, distance) = line.split_once(" = ").unwrap();
|
|
|
|
let (from, to) = direction.split_once(" to ").unwrap();
|
|
|
|
let distance = distance.parse::<u32>().unwrap();
|
|
|
|
distances.insert((from.to_string(), to.to_string()), distance);
|
|
|
|
locations.insert(from.to_string());
|
|
|
|
locations.insert(to.to_string());
|
|
|
|
});
|
|
|
|
let locations = locations
|
|
|
|
.iter()
|
|
|
|
.map(|place| place.to_string())
|
|
|
|
.collect::<Vec<String>>();
|
|
|
|
let num_permutations = factorial(locations.len());
|
|
|
|
let mut shortest = u32::MAX;
|
|
|
|
for idx in 1..=num_permutations {
|
|
|
|
let perm = permutation(locations.clone(), idx).unwrap();
|
|
|
|
let route_length = get_route_length(perm, &distances);
|
|
|
|
if route_length < shortest {
|
|
|
|
shortest = route_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
shortest
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_route_length(route: Vec<String>, distances: &HashMap<(String, String), u32>) -> u32 {
|
|
|
|
route
|
|
|
|
.windows(2)
|
|
|
|
.map(|pair| {
|
|
|
|
let distance = distances.get(&(pair[0].clone(), pair[1].clone()));
|
|
|
|
if let Some(distance) = distance {
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
let distance = distances.get(&(pair[1].clone(), pair[0].clone()));
|
|
|
|
if let Some(distance) = distance {
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
panic!("Should not happen");
|
|
|
|
})
|
|
|
|
.sum()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn permutation(mut locations: Vec<String>, nth: usize) -> Result<Vec<String>, Box<dyn Error>> {
|
|
|
|
locations.sort();
|
|
|
|
if nth == 1 {
|
|
|
|
return Ok(locations);
|
|
|
|
}
|
|
|
|
if nth > factorial(locations.len()) || nth == 0 {
|
|
|
|
return Err(Box::from("Out of bounds"));
|
|
|
|
}
|
|
|
|
let mut perm = Vec::new();
|
|
|
|
let num_unique_locations = locations.len();
|
|
|
|
let mut remainder = nth - 1;
|
|
|
|
for idx in 1..=locations.len() {
|
|
|
|
let permutations = remainder / factorial(num_unique_locations - idx);
|
|
|
|
remainder %= factorial(num_unique_locations - idx);
|
|
|
|
perm.push(locations[permutations].clone());
|
|
|
|
locations.remove(permutations);
|
|
|
|
}
|
|
|
|
Ok(perm)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn factorial(num: usize) -> usize {
|
|
|
|
let mut fact = 1;
|
|
|
|
for n in 1..=num {
|
|
|
|
fact *= n;
|
|
|
|
}
|
|
|
|
fact
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn process_part2(input: &str) -> u32 {
|
|
|
|
let mut distances = HashMap::new();
|
|
|
|
let mut locations = HashSet::new();
|
|
|
|
input.lines().for_each(|line| {
|
|
|
|
let (direction, distance) = line.split_once(" = ").unwrap();
|
|
|
|
let (from, to) = direction.split_once(" to ").unwrap();
|
|
|
|
let distance = distance.parse::<u32>().unwrap();
|
|
|
|
distances.insert((from.to_string(), to.to_string()), distance);
|
|
|
|
locations.insert(from.to_string());
|
|
|
|
locations.insert(to.to_string());
|
|
|
|
});
|
|
|
|
let locations = locations
|
|
|
|
.iter()
|
|
|
|
.map(|place| place.to_string())
|
|
|
|
.collect::<Vec<String>>();
|
|
|
|
let num_permutations = factorial(locations.len());
|
|
|
|
let mut longest = 0;
|
|
|
|
for idx in 1..=num_permutations {
|
|
|
|
let perm = permutation(locations.clone(), idx).unwrap();
|
|
|
|
let route_length = get_route_length(perm, &distances);
|
|
|
|
if route_length > longest {
|
|
|
|
longest = route_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
longest
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2024-10-30 11:34:40 +01:00
|
|
|
mod tests {
|
2024-10-29 13:13:50 +01:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
const INPUT: &str = "London to Dublin = 464
|
|
|
|
London to Belfast = 518
|
|
|
|
Dublin to Belfast = 141";
|
|
|
|
|
|
|
|
#[test]
|
2024-10-30 11:34:40 +01:00
|
|
|
fn part1() {
|
2024-10-29 13:13:50 +01:00
|
|
|
let result = process_part1(INPUT);
|
|
|
|
assert_eq!(result, 605);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn part2() {
|
|
|
|
let result = process_part2(INPUT);
|
|
|
|
assert_eq!(result, 982);
|
|
|
|
}
|
|
|
|
}
|