Add solutions for day20, parts one and two
This commit is contained in:
parent
19d5613bd9
commit
b9e2079de2
11
2022/day20-part1/Cargo.toml
Normal file
11
2022/day20-part1/Cargo.toml
Normal 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"] }
|
||||||
146
2022/day20-part1/src/main.rs
Normal file
146
2022/day20-part1/src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
11
2022/day20-part2/Cargo.toml
Normal file
11
2022/day20-part2/Cargo.toml
Normal 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"] }
|
||||||
158
2022/day20-part2/src/main.rs
Normal file
158
2022/day20-part2/src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user