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 2: Rock Paper Scissors #3

Closed
simonw opened this issue Dec 3, 2022 · 18 comments
Closed

Day 2: Rock Paper Scissors #3

simonw opened this issue Dec 3, 2022 · 18 comments
Labels

Comments

@simonw
Copy link
Owner

simonw commented Dec 3, 2022

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

Puzzle input: https://adventofcode.com/2022/day/2/input

e.g.

B Y
A Z
A Z
C Z
B Y

The first column is what your opponent is going to play: A for Rock, B for Paper, and C for Scissors [...]

The second column, you reason, must be what you should play in response: X for Rock, Y for Paper, and Z for Scissors

The score for a single round is the score for the shape you selected (1 for Rock, 2 for Paper, and 3 for Scissors) plus the score for the outcome of the round (0 if you lost, 3 if the round was a draw, and 6 if you won).

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

What would your total score be if everything goes exactly according to your strategy guide?

So loop through every line, figure out who won to get the score, then add a score based on the second letter.

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Started with this - I copied it in from my previous day, then used the comments to prompt Copilot to write some code:

use std::fs;
use std::io;
use std::io::BufRead;
fn main() -> io::Result<()> {
    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 of form "A B" - split into two variables
        let (a, b) = line.split_at(1);
        // Print those
        println!("{} {}", a, b);
    }
    Ok(())
}

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

I prompted:

// Who won? Opponent A beats me Z, B beats me X, C beats me Y

And got back:

let winner = match (opponent, me) {
    ("A", "Z") | ("B", "X") | ("C", "Y") => "opponent",
    ("A", "X") | ("B", "Y") | ("C", "Z") => "me",
    _ => "draw",
};

I'll rewrite that to use score numbers instead.

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

It suggested this but I'm not going to use it:

image

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

This is better:

image

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Turns out it was splitting it into 'A' and ' Z' - note leading space.

simonw added a commit that referenced this issue Dec 3, 2022
@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Weird, my code is correct against the example data but when I try to run it against the input and submit the result on Avent of Code it says:

That's not the right answer; your answer is too high

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Here's my code so far:

use std::fs;
use std::io;
use std::io::BufRead;
fn main() -> io::Result<()> {
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 of form "opponent me" - split on the space
let mut parts = line.split(" ");
let opponent = parts.next().unwrap();
let me = parts.next().unwrap();
// Who won? Opponent A beats me Z, B beats me X, C beats me Y
let win_score = match (opponent, me) {
("A", "Z") | ("B", "X") | ("C", "Y") => 0,
("A", "X") | ("B", "Y") | ("C", "Z") => 6,
_ => 3,
};
score += win_score;
// And an extra score based on what I picked
// X = 1, Y = 2, Z = 3 - use match for that too:
let extra_score = match me {
"X" => 1,
"Y" => 2,
"Z" => 3,
_ => 0,
};
println!("{} {} {} {}", opponent, me, win_score, extra_score);
score += extra_score;
}
println!("{}", score);
Ok(())
}

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Added a comment and now I can see the problem - it's with the => 6 line:

        /*
        A/X = rock
        B/Y = paper
        C/Z = scissors
        */

        // Who won? Opponent A beats me Z, B beats me X, C beats me Y
        let win_score = match (opponent, me) {
            ("A", "Z") | ("B", "X") | ("C", "Y") => 0,
            ("A", "X") | ("B", "Y") | ("C", "Z") => 6,
            _ => 3,
        };

A, X should not result in a win for me (6 points) - it's a draw.

simonw added a commit that referenced this issue Dec 3, 2022
@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

OK, that fixed it - got through to the next question.

Anyway, the second column says how the round needs to end: X means you need to lose, Y means you need to end the round in a draw, and Z means you need to win. Good luck!

So scoring is still the same, but now the second column tells you how many points you got because we assume you played the right item?

Oh I get it, the columns now just say "opponent picked X and you won/lost/tied" so I have to figure out what I must have picked to get that outcome - so I can calculate that extra_score.

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

Prompt-generated this to make it easier to follow:

        let mut parts = line.split(" ");
        let opponent_code = parts.next().unwrap();
        let outcome_code = parts.next().unwrap();
        
        /*
        A = rock
        B = paper
        C = scissors

        X = I lose
        Y = draw
        Z = I win
        */
        let opponent = match opponent_code {
            "A" => "rock",
            "B" => "paper",
            "C" => "scissors",
            _ => "unknown",
        };
        let outcome = match(outcome_code) {
            "X" => "lose",
            "Y" => "draw",
            "Z" => "win",
            _ => "unknown",
        };

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

  --> rock-paper-scissors2.rs:40:24
   |
40 |         let me = match (opponent, outcome) {
   |                        ^^^^^^^^^^^^^^^^^^^ pattern `(&_, _)` not covered
   |
   = note: the matched value is of type `(&str, &str)`

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

I pasted that into https://chat.openai.com/

        let opponent = match opponent_code {
            "A" => "rock",
            "B" => "paper",
            "C" => "scissors",
            _ => "unknown",
        };
        let outcome = match outcome_code {
            "X" => "lose",
            "Y" => "draw",
            "Z" => "win",
            _ => "unknown",
        };

        // Need to figure out what I picked, based on opponent + outcome
        let me = match (opponent, outcome) {
            ("rock", "win") => "paper",
            ("rock", "draw") => "rock",
            ("rock", "lose") => "scissors",
            ("paper", "win") => "scissors",
            ("paper", "draw") => "paper",
            ("paper", "lose") => "rock",
            ("scissors", "win") => "rock",
            ("scissors", "draw") => "scissors",
            ("scissors", "lose") => "paper",
        };

  --> rock-paper-scissors2.rs:40:24
   |
40 |         let me = match (opponent, outcome) {
   |                        ^^^^^^^^^^^^^^^^^^^ pattern `(&_, _)` not covered
   |
   = note: the matched value is of type `(&str, &str)`

It replied:

This code is trying to figure out what move the player made in a game of rock-paper-scissors based on the opponent's move and the outcome of the game. It appears that the code is using a match statement to check different combinations of the opponent's move and the outcome, and then returning the player's move based on those combinations.

However, the code is currently giving an error because there is no case in the match statement that handles the case where both the opponent's move and the outcome are unknown. This can be fixed by adding a _ => "unknown" case at the end of the match statement, which will handle any combination of values that aren't covered by the other cases. This will ensure that the code always returns a valid move, even if the opponent's move and the outcome are not known.

Here is an example of how the code could be modified to fix this error:

let me = match (opponent, outcome) {
    ("rock", "win") => "paper",
    ("rock", "draw") => "rock",
    ("rock", "lose") => "scissors",
    ("paper", "win") => "scissors",
    ("paper", "draw") => "paper",
    ("paper", "lose") => "rock",
    ("scissors", "win") => "rock",
    ("scissors", "draw") => "scissors",
    ("scissors", "lose") => "paper",
    _ => "unknown", // Add this case to handle unknown combinations
};

simonw added a commit that referenced this issue Dec 3, 2022
@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

use std::fs;
use std::io;
use std::io::BufRead;
fn main() -> io::Result<()> {
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?;
let mut parts = line.split(" ");
let opponent_code = parts.next().unwrap();
let outcome_code = parts.next().unwrap();
/*
A = rock
B = paper
C = scissors
X = I lose
Y = draw
Z = I win
*/
let opponent = match opponent_code {
"A" => "rock",
"B" => "paper",
"C" => "scissors",
_ => "unknown",
};
let outcome = match outcome_code {
"X" => "lose",
"Y" => "draw",
"Z" => "win",
_ => "unknown",
};
// Need to figure out what I picked, based on opponent + outcome
let me = match (opponent, outcome) {
("rock", "win") => "paper",
("rock", "draw") => "rock",
("rock", "lose") => "scissors",
("paper", "win") => "scissors",
("paper", "draw") => "paper",
("paper", "lose") => "rock",
("scissors", "win") => "rock",
("scissors", "draw") => "scissors",
("scissors", "lose") => "paper",
_ => "unknown",
};
let win_score = match outcome {
"win" => 6,
"draw" => 3,
"lose" => 0,
_ => 0,
};
// And an extra score based on what I picked
let extra_score = match me {
"rock" => 1,
"paper" => 2,
"scissors" => 3,
_ => 0,
};
println!("{} {} {} {}", opponent, me, win_score, extra_score);
score += extra_score;
}
println!("{}", score);
Ok(())
}

Tried that, Advent says "That's not the right answer; your answer is too low".

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

I forgot to add win_score to the score.

@simonw
Copy link
Owner Author

simonw commented Dec 3, 2022

That passed.

@simonw simonw closed this as completed Dec 3, 2022
simonw added a commit that referenced this issue Dec 3, 2022
@and-reas-se
Copy link

let me = match (opponent, outcome) {
    ("rock", "win") => "paper",
    ("rock", "draw") => "rock",
    ("rock", "lose") => "scissors",
    ("paper", "win") => "scissors",
    ("paper", "draw") => "paper",
    ("paper", "lose") => "rock",
    ("scissors", "win") => "rock",
    ("scissors", "draw") => "scissors",
    ("scissors", "lose") => "paper",
    _ => "unknown", // Add this case to handle unknown combinations
};

Code review: Instead of writing stringly typed code, I would suggest parsing into enums to leverage Rusts type system and get the guarantees it provides, which is one of rusts big advantages. For example like this.

enum Move {
    Rock,
    Paper,
    Scissors,
}

enum Outcome {
    Win,
    Lose,
    Draw,
}

Which will create new types named Move and Outcome that can only hold those alternatives. The AI can probably tell you how to parse the text into those types. This would also have avoided the error since then the rust compiler would have know that there are no other possible options and not needed an "unknown" case. And if you had missed a potential case in the match the compiler would have told you which one, turning that into a compile-time error instead of a runtime error.

@estebank
Copy link

estebank commented Dec 9, 2022

Out of curiosity, what rustc version are you running? For a while, the full output of a case like the missing fallback in the match looks like this, which hopefully would have been helpful:

error[E0004]: non-exhaustive patterns: `(&_, _)` not covered
 --> src/main.rs:4:11
  |
4 |     match (a, b) {
  |           ^^^^^^ pattern `(&_, _)` not covered
  |
  = note: the matched value is of type `(&str, &str)`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
  |
6 ~         ("c", "d") => {}
7 +         (&_, _) => todo!()
  |

@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

3 participants