Files
2024-12-02 23:04:39 -08:00

137 lines
3.4 KiB
Markdown

# Advent of code 2024
Write up for day 2. The below snippets exclude some boilerplate
related to reading the input, and the test cases are excluded.
The gist of both of todays problems is that you want to apply some
rules to a sequence of numbers. There are 2 types of rules.
Rules that apply to 'pairs' of numbers (adjacent numbers in
the sequence). These rules are:
- abs(a-b) >= 1
- abs(a-b) <= 3
Rules that apply to the sequence as a whole. The rules are:
- All numbers are 'going to the same direction' (up or down)
The second part made this a little bit more complicated by saying "allow 1
failure".
### d2p1.rs
First, we created a trivial helper to us track up/down and allow comparing directions:
```rust
#[derive(PartialEq)]
enum Direction {
Up,
Down,
}
impl Direction {
fn from(a: usize, b: usize) -> Self {
if a > b {
Direction::Down
} else {
Direction::Up
}
}
}
```
Then the core of the problem. We iterate through each line, convert it to a vector
of numbers.
This helper is responsible for checking the results of a single set of samples.
Rather than using a fancy `for in` loop, I chose to iterate using a while loop.
In each iteration, I validate the rules against `report[i]` and `report[i+1]`.
```rust
// Tests the report, and returns indexes around where a failure is detected.
fn test(reports: &[usize]) -> Result<(), ()> {
let mut i = 0;
let direction = Direction::from(reports[0], reports[1]);
while i + 1 < reports.len() {
let delta = reports[i].abs_diff(reports[i + 1]);
if delta < 1 || delta > 3 || direction != Direction::from(reports[i], reports[i + 1]) {
return Err(())
}
i += 1;
}
Ok(())
}
```
```rust
fn solve(lines: Vec<String>) -> SResult<usize> {
let mut failed = 0;
for line in lines.iter() {
let reports: Vec<usize> = line.split_whitespace().map(|v| v.parse::<usize>().unwrap()).collect();
match test(&reports) {
Ok(_) => (),
Err(_) => failed += 1,
};
}
Ok(lines.len() - failed)
}
```
### d2p2.rs
We enhance the helper above to return impacted indexes rather than nothing
if something fails:
```rust
// fn test
if delta < 1 || delta > 3 || direction != Direction::from(reports[i], reports[i + 1]) {
let mut errs = vec![i, i + 1];
if i > 0 {
errs.push(i - 1);
}
return Err(errs);
}
```
Next, we iterate through all the lines and check if they pass
```rust
fn solve(lines: Vec<String>) -> SResult<usize> {
let mut fail_count = 0;
for line in lines.iter() {
let reports: Vec<usize> = line
.split_whitespace()
.map(|v| v.parse::<usize>().unwrap())
.collect();
let problems = match test(&reports) {
// Cool, nothing to see here
Ok(_) => continue,
Err(e) => e,
};
```
If they don't pass, we try removing one of the elements and see what happens.
```rust
let mut pass = false;
for problem in problems {
let mut l1 = reports.clone();
l1.remove(problem);
if test(&l1).is_ok() {
pass = true;
break;
}
}
if !pass {
fail_count += 1;
}
}
Ok(lines.len() - fail_count)
}
```