Merge
This commit is contained in:
commit
5531e8aa7a
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -12,11 +12,17 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.plobos.xyz/projects/PuzzleUtils.git#49b0f24c1bdc2c04df237634607df15f19fb3ead"
|
||||
|
||||
[[package]]
|
||||
name = "y2015"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"md5",
|
||||
"utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -17,3 +17,6 @@ members = [
|
||||
"y2023",
|
||||
"y2024",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
utils = { git = "https://git.plobos.xyz/projects/PuzzleUtils.git" }
|
||||
|
@ -5,3 +5,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
md5 = "0.7.0"
|
||||
utils = { workspace = true }
|
||||
|
@ -1,5 +1,7 @@
|
||||
use core::panic;
|
||||
use std::{collections::HashMap, error::Error};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use utils::{math::factorial, permutation};
|
||||
|
||||
pub fn process_part1(input: &str) -> i32 {
|
||||
let mut happiness_table = HashMap::new();
|
||||
@ -22,12 +24,12 @@ pub fn process_part1(input: &str) -> i32 {
|
||||
|
||||
let num_people = people.len();
|
||||
|
||||
let possible_permutations = factorial(num_people);
|
||||
let possible_permutations = factorial(num_people.try_into().unwrap());
|
||||
|
||||
let mut highest_happiness = 0;
|
||||
for idx in 1..possible_permutations {
|
||||
let mut happiness = 0;
|
||||
let permutation = permutation(people.clone(), idx).unwrap();
|
||||
let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap();
|
||||
|
||||
for (pos, person) in permutation.clone().iter().enumerate() {
|
||||
let left_neighbor = {
|
||||
@ -61,35 +63,6 @@ pub fn process_part1(input: &str) -> i32 {
|
||||
|
||||
highest_happiness
|
||||
}
|
||||
|
||||
fn permutation(mut people: Vec<String>, nth: usize) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
people.sort();
|
||||
if nth == 1 {
|
||||
return Ok(people);
|
||||
}
|
||||
if nth > factorial(people.len()) || nth == 0 {
|
||||
return Err(Box::from("Out of bounds"));
|
||||
}
|
||||
let mut perm = Vec::new();
|
||||
let num_unique_people = people.len();
|
||||
let mut remainder = nth - 1;
|
||||
for idx in 1..=people.len() {
|
||||
let permutations = remainder / factorial(num_unique_people - idx);
|
||||
remainder %= factorial(num_unique_people - idx);
|
||||
perm.push(people[permutations].clone());
|
||||
people.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) -> i32 {
|
||||
let mut happiness_table = HashMap::new();
|
||||
let mut people = Vec::new();
|
||||
@ -118,12 +91,12 @@ pub fn process_part2(input: &str) -> i32 {
|
||||
|
||||
let num_people = people.len();
|
||||
|
||||
let possible_permutations = factorial(num_people);
|
||||
let possible_permutations = factorial(num_people.try_into().unwrap());
|
||||
|
||||
let mut highest_happiness = 0;
|
||||
for idx in 1..possible_permutations {
|
||||
let mut happiness = 0;
|
||||
let permutation = permutation(people.clone(), idx).unwrap();
|
||||
let permutation = permutation::nth_lex(people.clone(), idx.try_into().unwrap()).unwrap();
|
||||
|
||||
for (pos, person) in permutation.clone().iter().enumerate() {
|
||||
let left_neighbor = {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{error::Error, fmt::Debug};
|
||||
use utils::combination;
|
||||
use utils::math::binomial;
|
||||
|
||||
pub fn process_part1(input: &str, litres: u32) -> u32 {
|
||||
let containers = input
|
||||
@ -7,9 +8,10 @@ pub fn process_part1(input: &str, litres: u32) -> u32 {
|
||||
.collect::<Vec<u32>>();
|
||||
let mut correct_combinations = Vec::new();
|
||||
for k in 1..=containers.len() {
|
||||
let num_combinations = binomial(containers.len(), k);
|
||||
let num_combinations =
|
||||
binomial(containers.len().try_into().unwrap(), k.try_into().unwrap());
|
||||
for i in 1..=num_combinations {
|
||||
let res = combination(containers.clone(), k, i).unwrap();
|
||||
let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap();
|
||||
if res.iter().sum::<u32>() == litres {
|
||||
correct_combinations.push(res);
|
||||
}
|
||||
@ -25,9 +27,10 @@ pub fn process_part2(input: &str, litres: u32) -> u32 {
|
||||
.collect::<Vec<u32>>();
|
||||
let mut correct_combinations = Vec::new();
|
||||
for k in 1..=containers.len() {
|
||||
let num_combinations = binomial(containers.len(), k);
|
||||
let num_combinations =
|
||||
binomial(containers.len().try_into().unwrap(), k.try_into().unwrap());
|
||||
for i in 1..=num_combinations {
|
||||
let res = combination(containers.clone(), k, i).unwrap();
|
||||
let res = combination::nth_lex(containers.clone(), k, i.try_into().unwrap()).unwrap();
|
||||
if res.iter().sum::<u32>() == litres {
|
||||
correct_combinations.push(res);
|
||||
}
|
||||
@ -39,56 +42,6 @@ pub fn process_part2(input: &str, litres: u32) -> u32 {
|
||||
correct_combinations.len() as u32
|
||||
}
|
||||
|
||||
fn combination<T: Clone + Debug>(
|
||||
elements: Vec<T>,
|
||||
k: usize,
|
||||
nth: usize,
|
||||
) -> Result<Vec<T>, Box<dyn Error>> {
|
||||
let num_elements = elements.len();
|
||||
let num_combinations = binomial(num_elements, k);
|
||||
if nth > num_combinations || k > num_elements || nth == 0 || k == 0 {
|
||||
return Err(Box::from("Out of bounds"));
|
||||
}
|
||||
let mut i = 0;
|
||||
let mut remaining_k = k;
|
||||
let mut comb = Vec::new();
|
||||
let mut remainder = nth - 1;
|
||||
while remaining_k > 0 {
|
||||
// Count the number of combinations that start with elements[i]
|
||||
// example with n = 5, k = 2
|
||||
// nth <= 4 select first
|
||||
// nth <= 7 select second
|
||||
// nth <= 9 select third
|
||||
// nth == 10 select fourth
|
||||
let count = binomial(num_elements - i - 1, remaining_k - 1);
|
||||
if remainder < count {
|
||||
// If the nth combination is within the count, pick this element
|
||||
comb.push(elements[i].clone());
|
||||
remaining_k -= 1;
|
||||
} else {
|
||||
remainder -= count;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Ok(comb)
|
||||
}
|
||||
|
||||
fn factorial(num: usize) -> usize {
|
||||
let mut fact = 1;
|
||||
for n in 1..=num {
|
||||
fact *= n;
|
||||
}
|
||||
fact
|
||||
}
|
||||
|
||||
fn binomial(n: usize, k: usize) -> usize {
|
||||
if k > n {
|
||||
0
|
||||
} else {
|
||||
factorial(n) / (factorial(k) * factorial(n - k))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1,3 +1,5 @@
|
||||
use utils::math::get_divisors;
|
||||
|
||||
pub fn process_part1(input: u32) -> u32 {
|
||||
// slow
|
||||
//let mut house = 1;
|
||||
@ -20,38 +22,21 @@ pub fn process_part1(input: u32) -> u32 {
|
||||
house += 1;
|
||||
let mut divisors = get_divisors(house);
|
||||
divisors.push(house);
|
||||
if divisors.iter().sum::<usize>() * 10 >= input as usize {
|
||||
if divisors.iter().sum::<u64>() * 10 >= input.into() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
house as u32
|
||||
}
|
||||
|
||||
fn get_divisors(n: usize) -> Vec<usize> {
|
||||
let mut divisors = vec![1];
|
||||
let mut potential_divisor = 2;
|
||||
while (potential_divisor * potential_divisor) < n {
|
||||
if n % potential_divisor == 0 {
|
||||
divisors.push(potential_divisor);
|
||||
divisors.push(n / potential_divisor);
|
||||
}
|
||||
potential_divisor += 1;
|
||||
}
|
||||
// This almost made me go mad
|
||||
if potential_divisor * potential_divisor == n {
|
||||
divisors.push(potential_divisor)
|
||||
}
|
||||
divisors
|
||||
}
|
||||
|
||||
pub fn process_part2(input: u32) -> u32 {
|
||||
let mut house = 0;
|
||||
loop {
|
||||
house += 1;
|
||||
let mut divisors = get_divisors(house);
|
||||
divisors.push(house);
|
||||
let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::<usize>();
|
||||
if sum * 11 >= input as usize {
|
||||
let sum = divisors.iter().filter(|&&d| house / d <= 50).sum::<u64>();
|
||||
if sum * 11 >= input.into() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -134,8 +134,26 @@ impl RoundNode {
|
||||
let mut player = self.player.clone();
|
||||
let mut boss = self.boss.clone();
|
||||
// Player turn
|
||||
player.do_status_effects();
|
||||
boss.do_status_effects();
|
||||
for effect in player.status_effects.iter_mut() {
|
||||
if effect.name == SpellID::Recharge {
|
||||
player.mana += effect.mana;
|
||||
}
|
||||
effect.duration -= 1;
|
||||
if effect.duration > 0 && effect.name == SpellID::Shield {
|
||||
player.armor = effect.armor;
|
||||
} else if effect.name == SpellID::Shield {
|
||||
player.armor = 0;
|
||||
}
|
||||
}
|
||||
for effect in boss.status_effects.iter_mut() {
|
||||
boss.hp = boss.hp.saturating_sub(effect.damage);
|
||||
effect.duration -= 1;
|
||||
}
|
||||
boss.status_effects = boss
|
||||
.status_effects
|
||||
.into_iter()
|
||||
.filter(|&effect| effect.duration > 0)
|
||||
.collect::<Vec<Spell>>();
|
||||
if boss.hp == 0 {
|
||||
return RoundNode {
|
||||
player,
|
||||
@ -144,6 +162,11 @@ impl RoundNode {
|
||||
state: State::Win,
|
||||
};
|
||||
}
|
||||
player.status_effects = player
|
||||
.status_effects
|
||||
.into_iter()
|
||||
.filter(|&effect| effect.duration > 0)
|
||||
.collect::<Vec<Spell>>();
|
||||
let used_mana = spell.cost;
|
||||
let mut dmg = 0;
|
||||
match spell.name {
|
||||
@ -189,8 +212,26 @@ impl RoundNode {
|
||||
}
|
||||
|
||||
// Boss turn
|
||||
player.do_status_effects();
|
||||
boss.do_status_effects();
|
||||
for effect in player.status_effects.iter_mut() {
|
||||
if effect.name == SpellID::Recharge {
|
||||
player.mana += effect.mana;
|
||||
}
|
||||
effect.duration -= 1;
|
||||
if effect.duration > 0 && effect.name == SpellID::Shield {
|
||||
player.armor = effect.armor;
|
||||
} else if effect.name == SpellID::Shield {
|
||||
player.armor = 0;
|
||||
}
|
||||
}
|
||||
player.status_effects = player
|
||||
.status_effects
|
||||
.into_iter()
|
||||
.filter(|&effect| effect.duration > 0)
|
||||
.collect::<Vec<Spell>>();
|
||||
for effect in boss.status_effects.iter_mut() {
|
||||
boss.hp = boss.hp.saturating_sub(effect.damage);
|
||||
effect.duration -= 1;
|
||||
}
|
||||
if boss.hp == 0 {
|
||||
return RoundNode {
|
||||
player,
|
||||
@ -199,6 +240,11 @@ impl RoundNode {
|
||||
state: State::Win,
|
||||
};
|
||||
}
|
||||
boss.status_effects = boss
|
||||
.status_effects
|
||||
.into_iter()
|
||||
.filter(|&effect| effect.duration > 0)
|
||||
.collect::<Vec<Spell>>();
|
||||
let dmg = boss.hit_damage(&player);
|
||||
player.hp = player.hp.saturating_sub(dmg);
|
||||
|
||||
@ -328,29 +374,6 @@ impl Character {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fn do_status_effects(&mut self) {
|
||||
self.status_effects.iter_mut().for_each(|effect| {
|
||||
if effect.name == SpellID::Recharge {
|
||||
self.mana += effect.mana;
|
||||
}
|
||||
if effect.name == SpellID::Poison {
|
||||
self.hp -= effect.damage;
|
||||
}
|
||||
effect.duration -= 1;
|
||||
if effect.duration > 0 && effect.name == SpellID::Shield {
|
||||
self.armor = effect.armor;
|
||||
} else if effect.name == SpellID::Shield {
|
||||
self.armor = 0;
|
||||
}
|
||||
});
|
||||
self.status_effects = self
|
||||
.status_effects
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|&effect| effect.duration > 0)
|
||||
.collect::<Vec<Spell>>();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,8 +1,7 @@
|
||||
use core::panic;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
error::Error,
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use utils::{math::factorial, permutation::nth_lex};
|
||||
|
||||
pub fn process_part1(input: &str) -> u32 {
|
||||
let mut distances = HashMap::new();
|
||||
@ -19,10 +18,10 @@ pub fn process_part1(input: &str) -> u32 {
|
||||
.iter()
|
||||
.map(|place| place.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let num_permutations = factorial(locations.len());
|
||||
let num_permutations = factorial(locations.len().try_into().unwrap());
|
||||
let mut shortest = u32::MAX;
|
||||
for idx in 1..=num_permutations {
|
||||
let perm = permutation(locations.clone(), idx).unwrap();
|
||||
let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap();
|
||||
let route_length = get_route_length(perm, &distances);
|
||||
if route_length < shortest {
|
||||
shortest = route_length;
|
||||
@ -48,34 +47,6 @@ fn get_route_length(route: Vec<String>, distances: &HashMap<(String, String), u3
|
||||
.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();
|
||||
@ -91,10 +62,10 @@ pub fn process_part2(input: &str) -> u32 {
|
||||
.iter()
|
||||
.map(|place| place.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
let num_permutations = factorial(locations.len());
|
||||
let num_permutations = factorial(locations.len().try_into().unwrap());
|
||||
let mut longest = 0;
|
||||
for idx in 1..=num_permutations {
|
||||
let perm = permutation(locations.clone(), idx).unwrap();
|
||||
let perm = nth_lex(locations.clone(), idx.try_into().unwrap()).unwrap();
|
||||
let route_length = get_route_length(perm, &distances);
|
||||
if route_length > longest {
|
||||
longest = route_length;
|
||||
|
Loading…
Reference in New Issue
Block a user