This is a solution to Advent of Code 2024 day 1, written in Raku and in Rust.
https://adventofcode.com/2024/day/1
Part One
To find the total distance between the left list and the right list, add up the distances between all of the pairs you found. In the example above, this is 2 + 1 + 0 + 1 + 2 + 5, a total distance of 11!
Your actual left and right lists contain many location IDs. What is the total distance between your lists?
Raku
Half of the challenge is taking the input which has two values per line and pivoting it to group by column instead. Then it is sorted before a zip merge and reduction to a sum of differences.
my @pairs = 'input-1.txt'.IO.lines>>.split(/\s+/)>>.Int;
my @a = @pairs.map(-> ($a, $b) { $a }).sort;
my @b = @pairs.map(-> ($a, $b) { $b }).sort;
say [+] (@a Z @b).map(-> ($a, $b) { abs($a - $b) });
1580061
Rust
Here is part 1 implemented in Rust using the same approach as the Raku code, albeit with a lot
more saying exactly what I mean. Not sure if this is idomatic Rust, or even half-sane Rust, but
there we go. A big surprise for me is that sort()
can't be used in a fluent style.
use std::error::Error;
use std::fs;
fn main() -> Result<(), Box<dyn Error>> {
let input = fs::read_to_string("/Users/donaldh/git/advent-of-code/2024/input-1.txt")?;
let pairs: Vec<Vec<i32>> = input.lines()
.map(|x| x.split(" ").map(|x| x.parse::<i32>().unwrap()).collect())
.collect();
let mut left: Vec<i32> = pairs.iter().map(|x| x[0]).collect();
left.sort();
let mut right: Vec<i32> = pairs.iter().map(|x| x[1]).collect();
right.sort();
let distances: Vec<i32> = left.iter().zip(right.iter())
.map(|(a, b)| { (a - b).abs() })
.collect();
let total: i32 = distances.iter().sum();
println!("{:#?}", total);
Ok(())
}
1580061
Part Two
This time, you'll need to figure out exactly how often each number from the left list appears in the right list. Calculate a total similarity score by adding up each number in the left list after multiplying it by the number of times that number appears in the right list.
Once again consider your left and right lists. What is their similarity score?
Raku
Roughly the same first step, grouping the data by column, but then converting the second column
to a Bag
so we can efficiently look up the occurences for each item in the first column.
my @pairs = 'input-1.txt'.IO.lines>>.split(/\s+/)>>.Int;
my @a = @pairs.map(-> ($a, $b) { $a });
my $b = @pairs.map(-> ($a, $b) { $b }).Bag;
say [+] @a.map(-> $n { $n * $b{$n} // 0 });
23046913
Rust
There isn't a bag data structure in the Rust std
crate and I'm not going to reach for other
crates so I need to roll my own. It's surprisingly easy to fill a map with counted occurrences.
Again I don't know if this is idiomatic.
use std::error::Error;
use std::fs;
use std::collections::BTreeMap;
fn main() -> Result<(), Box<dyn Error>> {
let input = fs::read_to_string("/Users/donaldh/git/advent-of-code/2024/input-1.txt")?;
let pairs: Vec<Vec<i32>> = input.lines()
.map(|x| x.split(" ").map(|x| x.parse::<i32>().unwrap()).collect())
.collect();
let left: Vec<i32> = pairs.iter().map(|x| x[0]).collect();
let right: Vec<i32> = pairs.iter().map(|x| x[1]).collect();
let mut right_bag = BTreeMap::new();
for value in right.iter() {
if let Some(n) = right_bag.get_mut(value) {
*n += 1;
} else {
right_bag.insert(value, 1);
}
}
let mut total: i32 = 0;
for value in left.iter() {
if let Some(n) = right_bag.get(value) {
total += value * n;
}
}
println!("{:#?}", total);
Ok(())
}
23046913