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