Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day 4: Camp Cleanup #5

Closed
simonw opened this issue Dec 4, 2022 · 14 comments
Closed

Day 4: Camp Cleanup #5

simonw opened this issue Dec 4, 2022 · 14 comments
Labels

Comments

@simonw
Copy link
Owner

simonw commented Dec 4, 2022

https://adventofcode.com/2022/day/4

Example:

2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8

Diagram:

....567..  5-7
......789  7-9

.2345678.  2-8
..34567..  3-7

.....6...  6-6
...456...  4-6

.23456...  2-6
...45678.  4-8

In how many pairs does one group entirely overlap the other?

@simonw
Copy link
Owner Author

simonw commented Dec 4, 2022

I think a numeric comparison will work well here. Look for cases where either the first range encloses the second or vice-versa.

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

So here's a mystery that stumped me for a while.

I wrote this code, based on my previous answers:

use std::fs;
use std::io;
use std::io::BufRead;

fn main() {
    // Read file line by line
    let file = fs::File::open("input.txt")?;
    let reader = io::BufReader::new(file);
    for line in reader.lines() {
        println!("{}", line.unwrap());
    }
}

That looks fine to me - but the compiler says:

   Compiling day_04 v0.1.0 (/Users/simon/Dropbox/Development/advent-of-code-2022-in-rust/day_04)
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
  --> src/main.rs:7:43
   |
5  | / fn main() {
6  | |     // Read file line by line
7  | |     let file = fs::File::open("input.txt")?;
   | |                                           ^ cannot use the `?` operator in a function that returns `()`
8  | |     let reader = io::BufReader::new(file);
...  |
11 | |     }
12 | | }
   | |_- this function should return `Result` or `Option` to accept `?`
   |
   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `day_04` due to previous error

So I pasted that into ChatGPT and it said:

The error message is telling you that you are using the ? operator in a function that doesn't return a Result or Option type. The ? operator is used to return an error value from a function if the operation being performed returns an error. However, in a function that doesn't return a Result or Option, there is no way to return an error value, so the ? operator can't be used.

To fix this error, you will need to change your function to return a Result or Option type. You can do this by adding a Result or Option return type to your function signature. For example, you could change your function to the following:

fn main() -> Result<(), std::io::Error> {
    // Read file line by line
    let file = fs::File::open("input.txt")?;
    let reader = io::BufReader::new(file);
    // Process the lines
    for line in reader.lines() {
        let line = line?;
        println!("{}", line);
    }
    // Return success
    Ok(())
}

In this example, the main function now has a return type of Result<(), std::io::Error>, which means it will return either an error value or a value indicating success (in this case, an empty tuple ()). This allows the ? operator to be used in the function to return any errors that occur.

It took me a bit to understand why changing the function signature (adding -> Result<(), std::io::Error>) would fix the compile error.

Then I got it: the ?; operator means "do this and return an error if it fails" - but the fn main() function by default didn't have a signature that included the possibility it might return an error.

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

Here's the version of the code that works:

use std::fs;
use std::io;
use std::io::BufRead;

fn main() -> Result<(), std::io::Error> {
    // Read file line by line
    let file = fs::File::open("input.txt")?;
    let reader = io::BufReader::new(file);
    for line in reader.lines() {
        let line = line?;
        println!("{}", line);
    }
    Ok(())
}

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

I asked it which of these was better idiomatic Rust and it had a strong opinion:

image

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

Tried this:

use std::fs;
use std::io;
use std::io::BufRead;
fn main() -> Result<(), std::io::Error> {
// Read file line by line
let file = fs::File::open("input.txt")?;
let reader = io::BufReader::new(file);
let mut score = 0;
for line in reader.lines() {
let line = line?;
// Line is e.g. 41-47,40-80 - parse into a-b,c-d
// first use split_once('-')
let (one, two) = line.split_once(',').unwrap();
let (a, b) = one.split_once('-').unwrap();
let (c, d) = two.split_once('-').unwrap();
// Parse those into numbers
let a = a.parse::<u32>().unwrap();
let b = b.parse::<u32>().unwrap();
let c = c.parse::<u32>().unwrap();
let d = d.parse::<u32>().unwrap();
// Check if range a-b contains c-d or vice versa
if (a <= c && c <= b) || (a <= d && d <= b) {
println!("{}-{} fully overlaps {}-{}", a, b, c, d);
score += 1;
}
}
println!("Score: {}", score);
Ok(())
}

Mostly written by Copilot, prompted by my commits.

Apparently wrong:

That's not the right answer; your answer is too high. If you're stuck, make sure you're using the full input data; there are also some general tips on the about page, or you can ask for hints on the subreddit. Please wait one minute before trying again. (You guessed 831.)

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

Question was: "In how many assignment pairs does one range fully contain the other?"

Maybe exact matches shouldn't count?

No, tried that - still got an answer that was too high.

I used:

        if (a <= c && c <= b) || (a <= d && d <= b) && (a != c && b != d) {

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

I cheated and ran the solution from https://fasterthanli.me/series/advent-of-code-2022/part-4 - it gets 490, so I should change my code until I get that number too.

That example uses .filter(|(a, b)| a.contains_or_is_contained(b)) where that function is defined using fancy traits as:

trait InclusiveRangeExt {
    fn contains_range(&self, other: &Self) -> bool;

    // 👋 new! we can have trait methods with default implementations
    fn contains_or_is_contained(&self, other: &Self) -> bool {
        self.contains_range(other) || other.contains_range(self)
    }
}

impl<T> InclusiveRangeExt for RangeInclusive<T>
where
    T: PartialOrd,
{
    fn contains_range(&self, other: &Self) -> bool {
        self.contains(other.start()) && self.contains(other.end())
    }
}

Which itself uses use itertools::Itertools; which I had to install with cargo add itertools.

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

I tried to figure out how to remove that itertools package. I asked ChatGPT and it clearly didn't know but made stuff up anyway! It told me to run cargo remove itertools - but that's not a command. I kept pasting it error messages and it kept making new guesses, none of which worked.

I won't paste in the full transcript because it got tedious. I eventually decided to edit the Cargo.toml file by hand instead:

image

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

image

CleanShot 2022-12-04 at 21 11 27@2x

This gave me the right result.

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

use std::fs;
use std::io;
use std::io::BufRead;
fn range_contains_range(range: (u32, u32), other: (u32, u32)) -> bool {
range.0 <= other.0 && range.1 >= other.1
}
fn main() -> Result<(), std::io::Error> {
// Read file line by line
let file = fs::File::open("input.txt")?;
let reader = io::BufReader::new(file);
let mut score = 0;
for line in reader.lines() {
let line = line?;
// Line is e.g. 41-47,40-80 - parse into a-b,c-d
// first use split_once('-')
let (one, two) = line.split_once(',').unwrap();
let (a, b) = one.split_once('-').unwrap();
let (c, d) = two.split_once('-').unwrap();
// Parse those into numbers
let a = a.parse::<u32>().unwrap();
let b = b.parse::<u32>().unwrap();
let c = c.parse::<u32>().unwrap();
let d = d.parse::<u32>().unwrap();
let range1 = (a, b);
let range2 = (c, d);
if range_contains_range(range1, range2) || range_contains_range(range2, range1) {
score += 1;
}
}
println!("Score: {}", score);
Ok(())
}

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

Part 2:

It seems like there is still quite a bit of duplicate work planned. Instead, the Elves would like to know the number of pairs that overlap at all.

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

Copilot completed this task for me based just on my new function name:

fn range_overlaps_range(range: (u32, u32), other: (u32, u32)) -> bool {
    range.0 <= other.0 && other.0 <= range.1
        || range.0 <= other.1 && other.1 <= range.1
        || other.0 <= range.0 && range.0 <= other.1
        || other.0 <= range.1 && range.1 <= other.1
}

@simonw simonw closed this as completed in 01110b4 Dec 5, 2022
@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

use std::fs;
use std::io;
use std::io::BufRead;
fn range_overlaps_range(range: (u32, u32), other: (u32, u32)) -> bool {
range.0 <= other.0 && other.0 <= range.1
|| range.0 <= other.1 && other.1 <= range.1
|| other.0 <= range.0 && range.0 <= other.1
|| other.0 <= range.1 && range.1 <= other.1
}
fn main() -> Result<(), std::io::Error> {
// Read file line by line
let file = fs::File::open("input.txt")?;
let reader = io::BufReader::new(file);
let mut score = 0;
for line in reader.lines() {
let line = line?;
// Line is e.g. 41-47,40-80 - parse into a-b,c-d
// first use split_once('-')
let (one, two) = line.split_once(',').unwrap();
let (a, b) = one.split_once('-').unwrap();
let (c, d) = two.split_once('-').unwrap();
// Parse those into numbers
let a = a.parse::<u32>().unwrap();
let b = b.parse::<u32>().unwrap();
let c = c.parse::<u32>().unwrap();
let d = d.parse::<u32>().unwrap();
let range1 = (a, b);
let range2 = (c, d);
if range_overlaps_range(range1, range2) {
score += 1;
}
}
println!("Score: {}", score);
Ok(())
}

@simonw
Copy link
Owner Author

simonw commented Dec 5, 2022

image

image

@simonw simonw added the day label Dec 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant