Add solutions for day20, parts one and two

This commit is contained in:
Tobias Marschner 2024-09-21 13:43:23 +02:00
parent 19d5613bd9
commit b9e2079de2
4 changed files with 326 additions and 0 deletions

View File

@ -0,0 +1,11 @@
[package]
name = "day20-part1"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rayon = "1"
itertools = "0"
id-arena = { version = "2", features = ["rayon"] }

View File

@ -0,0 +1,146 @@
use id_arena::{Arena, Id};
use itertools::Itertools;
type NodeId = Id<Node>;
#[derive(Debug, Eq, PartialEq)]
struct Node {
value: i32,
prev: Option<NodeId>,
next: Option<NodeId>,
}
fn main() {
// Use command line arguments to specify the input filename.
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
panic!("Usage: ./main <input-file>\nNo input file provided. Exiting.");
}
// Next, read the contents of the input file into a string for easier processing.
let input = std::fs::read_to_string(&args[1]).expect("Error opening file");
// The actual set of initial numbers, completely immutable.
let numbers = input
.lines()
.map(|e| e.parse::<i32>().unwrap())
.collect_vec();
// Translate this list into a self-rolled linked-list with the help of the `id-arena` crate.
let mut nodes = Arena::<Node>::with_capacity(numbers.len());
// Keep the original order of references for the mixing operation.
let mut og_order: Vec<NodeId> = Vec::with_capacity(numbers.len());
// Fill the arena with the Nodes.
let mut prev_node = None;
for num in numbers {
// Create the current Node.
let current_node = nodes.alloc(Node {
value: num,
prev: prev_node,
next: None,
});
og_order.push(current_node);
// Fix the next-pointer of the previous node, if it exists.
if let Some(pn) = prev_node {
nodes[pn].next = Some(current_node);
}
// And make the current element the previous one.
prev_node = Some(current_node);
}
// Make the linked list cyclic, as that matches the mixing behvaior of the task.
let first = og_order.first().copied().unwrap();
let last = og_order.last().copied().unwrap();
nodes[first].prev = Some(last);
nodes[last].next = Some(first);
// We'll need it later: Find the element with value 0, it has special significance.
let zero_node = nodes
.iter()
.find_map(|(i, e)| if e.value == 0 { Some(i) } else { None })
.unwrap();
let print_list = |a: &Arena<Node>, s: NodeId| {
print!("[");
let mut c = s;
loop {
print!("{}", a[c].value);
c = a[c].next.unwrap();
if c == s {
break;
}
print!(", ");
}
println!("]");
};
// Now iterate through all nodes in their original order.
for id in og_order {
// print_list(&nodes, zero_node);
// Save the hassle if the value is 0.
if nodes[id].value == 0 {
continue;
}
// Keep track of the element after which to insert the current one.
let mut target = nodes[id].prev.unwrap();
// Move as many steps as the value requires.
for _ in 0..nodes[id].value.abs() {
// Move left or right, depending on sign of value.
if nodes[id].value.is_negative() {
// println!("Moving {} to the left.", nodes[id].value);
target = nodes[target].prev.unwrap();
// Move again if the target is ourselves.
if target == id {
// println!("Skipping self, moving to the left.");
target = nodes[target].prev.unwrap();
}
} else {
// println!("Moving {} to the right.", nodes[id].value);
target = nodes[target].next.unwrap();
// Move again if the target is ourselves.
if target == id {
// println!("Skipping self, moving to the right.");
target = nodes[target].next.unwrap();
}
}
}
// Actually perform the swap.
let new_prev = target;
let new_next = nodes[target].next.unwrap();
let old_prev = nodes[id].prev.unwrap();
let old_next = nodes[id].next.unwrap();
// Rebind the old neighbors to each other.
nodes[old_prev].next = Some(old_next);
nodes[old_next].prev = Some(old_prev);
// Bind the new neighbors to the node.
nodes[new_prev].next = Some(id);
nodes[new_next].prev = Some(id);
// Bind the node itself to the new neighbors.
nodes[id].prev = Some(new_prev);
nodes[id].next = Some(new_next);
}
// print_list(&nodes, zero_node);
// Find the 1000th, 2000th and 3000th element, starting form the zero-element,
// and add them together for the final result.
let mut result = 0i32;
let mut current_node = zero_node;
for i in 1..=3000 {
current_node = nodes[current_node].next.unwrap();
if i % 1000 == 0 {
// println!("Adding {}.", nodes[current_node].value);
result += nodes[current_node].value;
}
}
println!("Result: {}", result);
}

View File

@ -0,0 +1,11 @@
[package]
name = "day20-part2"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rayon = "1"
itertools = "0"
id-arena = { version = "2", features = ["rayon"] }

View File

@ -0,0 +1,158 @@
use id_arena::{Arena, Id};
use itertools::Itertools;
type NodeId = Id<Node>;
#[derive(Debug, Eq, PartialEq)]
struct Node {
value: i64,
prev: Option<NodeId>,
next: Option<NodeId>,
}
fn main() {
// Use command line arguments to specify the input filename.
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
panic!("Usage: ./main <input-file>\nNo input file provided. Exiting.");
}
// Next, read the contents of the input file into a string for easier processing.
let input = std::fs::read_to_string(&args[1]).expect("Error opening file");
// The actual set of initial numbers, completely immutable.
// Here the "decryption key" is multiplied into the list.
let numbers = input
.lines()
// .map(|e| e.parse::<i64>().unwrap())
.map(|e| e.parse::<i64>().unwrap() * 811589153)
.collect_vec();
// Translate this list into a self-rolled linked-list with the help of the `id-arena` crate.
let mut nodes = Arena::<Node>::with_capacity(numbers.len());
// Keep the original order of references for the mixing operation.
let mut og_order: Vec<NodeId> = Vec::with_capacity(numbers.len());
// Fill the arena with the Nodes.
let mut prev_node = None;
for num in numbers {
// Create the current Node.
let current_node = nodes.alloc(Node {
value: num,
prev: prev_node,
next: None,
});
og_order.push(current_node);
// Fix the next-pointer of the previous node, if it exists.
if let Some(pn) = prev_node {
nodes[pn].next = Some(current_node);
}
// And make the current element the previous one.
prev_node = Some(current_node);
}
// Make the linked list cyclic, as that matches the mixing behvaior of the task.
let first = og_order.first().copied().unwrap();
let last = og_order.last().copied().unwrap();
nodes[first].prev = Some(last);
nodes[last].next = Some(first);
// We'll need it later: Find the element with value 0, it has special significance.
let zero_node = nodes
.iter()
.find_map(|(i, e)| if e.value == 0 { Some(i) } else { None })
.unwrap();
let print_list = |a: &Arena<Node>, s: NodeId| {
print!("[");
let mut c = s;
loop {
print!("{}", a[c].value);
c = a[c].next.unwrap();
if c == s {
break;
}
print!(", ");
}
println!("]");
};
// Now iterate through all nodes in their original order - ten times (for some reason).
for (i, id) in (0..10).cartesian_product(og_order.iter().copied()) {
// print!("Iteration {} / ", i);
// print_list(&nodes, zero_node);
// Save the hassle if the value is 0.
if nodes[id].value == 0 {
continue;
}
let move_value = nodes[id].value;
let mut move_amount = move_value.abs();
let move_reverse = move_value.is_negative();
// Automatically wrap moves larger than the entire list.
// Subtract one since the list over which we move does not contain the element we're
// moving - conceptually it's a list with 4999 elements, not 5000.
move_amount %= (nodes.len() as i64) - 1;
// Keep track of the element after which to insert the current one.
let mut target = nodes[id].prev.unwrap();
// Move as many steps as the value requires.
for _ in 0..move_amount {
// Move left or right, depending on sign of value.
if move_reverse {
// println!("Moving {} to the left.", nodes[id].value);
target = nodes[target].prev.unwrap();
// Move again if the target is ourselves.
if target == id {
// println!("Skipping self, moving to the left.");
target = nodes[target].prev.unwrap();
}
} else {
// println!("Moving {} to the right.", nodes[id].value);
target = nodes[target].next.unwrap();
// Move again if the target is ourselves.
if target == id {
// println!("Skipping self, moving to the right.");
target = nodes[target].next.unwrap();
}
}
}
// Actually perform the swap.
let new_prev = target;
let new_next = nodes[target].next.unwrap();
let old_prev = nodes[id].prev.unwrap();
let old_next = nodes[id].next.unwrap();
// Rebind the old neighbors to each other.
nodes[old_prev].next = Some(old_next);
nodes[old_next].prev = Some(old_prev);
// Bind the new neighbors to the node.
nodes[new_prev].next = Some(id);
nodes[new_next].prev = Some(id);
// Bind the node itself to the new neighbors.
nodes[id].prev = Some(new_prev);
nodes[id].next = Some(new_next);
}
// print_list(&nodes, zero_node);
// Find the 1000th, 2000th and 3000th element, starting form the zero-element,
// and add them together for the final result.
let mut result = 0i64;
let mut current_node = zero_node;
for i in 1..=3000 {
current_node = nodes[current_node].next.unwrap();
if i % 1000 == 0 {
// println!("Adding {}.", nodes[current_node].value);
result += nodes[current_node].value;
}
}
println!("Result: {}", result);
}