Camp Cleanup
In Day 4 we have to help the elves optimize their numbers with the cleaning assignments for the camp. I leanred about the map
operations in rust.
In the puzzle for today we need to review the work assignments for the ekves and to be able to identify areas that have more than one elf assigned.
Part 1
In the first part we have to count the number of assignments that are completly enclosed within another (e.g. the first row of test input).
We are given the test input which are lines of pairs of number ranges represented like so:
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8
The first step as always is to read in the data from our file.
fn main() {
let input: &str = include_str!("./day3_input.txt");
/* ... */
}
One thing I learned from some googling is that rust actually has a method to iterate over a string, or a string reference in this case, by each line. From that we can call map
and act or each line.
This is very similar to some of the linq expressions in c#.
let part_one = input.lines().map(|line| {
/* ... */
}
Inside the map
method we split the line on the comma ,
to ge the two ranges into a tuple for the left and right sides.
let (left, right) = line.split_once(',').unwrap();
Then we split both of those to get the start and end points of each range, which is stored in a tuple as well.
let ((a,b),(c,d)) = (left.split_once('-').unwrap(), right.split_once('-').unwrap());
}
We then parse those into digits (u8
) for the upcoming comparison.
(
a.parse::<u8>().unwrap(),
b.parse::<u8>().unwrap(),
c.parse::<u8>().unwrap(),
d.parse::<u8>().unwrap(),
)
Now we can apply the filer which will have our logical test to find ranges starting with the values in a
or c
and ending in b
or d
and then count that row. The codnition is that either range one (a
to b
) is completly within range two (c
to d
) or range two is completly within range one.
The logic is
IF
the start or range one is greater than or equal to the start of range two
AND
the end of range one is less than or equal to the end or range two
OR
the start of range one is less than or equal to the start of range two
AND
the end of range one is greater than or equal to the end of range two
In code it looks like this, with the call to count the values that meet the filter criteria:
.filter(|(a,b,c,d)|(a >= c && b <= d)|| (a <= c && b >= d)).count();
Then we jsut print the output of the map function. The whole method looks like this:
fn main() {
let input = include_str!("./day4_input.prod");
let part_one = input.lines().map(|line| {
let (left, right) = line.split_once(',').unwrap();
let ((a,b),(c,d)) = (left.split_once('-').unwrap(), right.split_once('-').unwrap());
(
a.parse::<u8>().unwrap(),
b.parse::<u8>().unwrap(),
c.parse::<u8>().unwrap(),
d.parse::<u8>().unwrap(),
)
})
.filter(|(a,b,c,d)|(a >= c && b <= d)|| (a <= c && b >= d))
.count();
println!("Part one result: {}", part_one);
}
Part 2
Now we are asked to count any intersections of ranges at all. So I duplicated the code for part one and changed the filter to count any lines where the start of range one is less than the end of range two, and the end of range one is greater than the start of range two.
.filter(|(a,b,c,d)|( a <= d && b >= c))
.count();
The Final Solution
fn main() {
let input = include_str!("./day4_input.prod");
let part_one = input.lines().map(|line| {
let (left, right) = line.split_once(',').unwrap();
let ((a,b),(c,d)) = (left.split_once('-').unwrap(), right.split_once('-').unwrap());
(
a.parse::<u8>().unwrap(),
b.parse::<u8>().unwrap(),
c.parse::<u8>().unwrap(),
d.parse::<u8>().unwrap(),
)
})
.filter(|(a,b,c,d)|(a >= c && b <= d)|| (a <= c && b >= d))
.count();
let part_two = input.lines().map(|line| {
let (left, right) = line.split_once(',').unwrap();
let ((a,b),(c,d)) = (left.split_once('-').unwrap(), right.split_once('-').unwrap());
(
a.parse::<u8>().unwrap(),
b.parse::<u8>().unwrap(),
c.parse::<u8>().unwrap(),
d.parse::<u8>().unwrap(),
)
})
.filter(|(a,b,c,d)|( a <= d && b >= c))
.count();
println!("Part one result: {}", part_one);
println!("Part two result: {}", part_two);
}