I'm so happy you are doing AoC. I decided to try it in Rust this year (previous years I used Python) and it's great for me to see your videos after I've done my solution so I can refactor my code to be more Rust-idiomatic. Thanks for taking the time. I'll be learning a lot from following along.
As already said on the first video. Same for me, keep on with the series. It helps so much when I'm finished coding, and your video pops up short after. I can reflect on my own implementation while watching your videos 👍🤗
The docs. for PartialOrd state that an implementation must satisfy a transitivity rule: a < b and b < c implies that a < c. As such, it is not really correct to use PartialOrd for Rock, Paper, Scissors.
mm yeah, I think you're right. This would be a trait implementation that was surprising if it were shipped in a production application. Perhaps I should've used an iter.cycle in the video instead, or just left it to match 🤔
I'm doing these in TS each morning and am really enjoying watching these in rust. I know nothing of rust (yet) but I hope you keep going all through advent!
Technically, you are required to ensure that the ordering of PartialOrd is transitive. The compiler doesn't check it for you, though, which is nice for hacky solutions on a type that will never be used in another project.
lol this is so much more graceful (even if complex) than my "death by if-statements" solution. I'm using AoC to learn Rust better, and you've been a big part of that journey as well. Good luck!
@@gareth2021 See the problem is that I'm still so new at Rust that I'm writing it like Python. I didn't use any of the great features Rust has, like the match syntax that Chris shows later in the video, just the if(a == b) type syntax. AoC is teaching me some super powerful lessons on a nightly basis. Today's lessons were: - Read the prompt carefully (got X = loss, Z = win mixed up) - Write Rust code, not Python code in Rust - Use templated code (reading from a text file and splitting on lines, messed up the syntax on that several times costing me precious minutes)
@@gareth2021 Yes, this was definitely a good use case for match. I did a split(' ') on the lines, and then matched on a 2-tuple of &str. If I were to do it over again, I think I'd probably just match on the entire line since there are only 9 possibilities (I ended up listing all 9 possibilities for part 2 to figure out why my shape had to be).
I appreciate you actually implementing the partial order trait. This is actually a great series for learning Rust. I have finished the Rust book and am going a few other books/projects. But going through advent of code is one of the best ways to learn. Thanks!
it's actually a great demo of the AoC conundrum -- you're rarely sure which tradeoffs to make relative to developer efficiency, complexity, performance, learning value, etc. do you use enums, will the input be interpreted the same way in part 2, is part 2 going to involve shooting for a specific score, etc. You choose a set of tradeoffs and part2 sometimes goes your way and sometimes not. Sometimes i go back and rewrite the solutions for simplicity or clarity with full hindsight.
Thanks again for these videos, they are such a huge help! As I was doing it last night I _wanted_ to use some kind of Comparison based approach but wasn't sure how to do it. So I ended up just doing matches inline. It's great to see how this way works. Looking forward to seeing how our solutions differ as the problems get more difficult as they always do.
I meant to do AoC (first year) but it turns out my daily life poses enough significant programming challenges that I haven't had a chance even to start. Fortunately I bookmarked this vid on day 1 and I guess I'll learn some more Rust vicariously. Thanks Chris!
I really enjoy this series of videos. Though I'm late to the party, I really enjoy working through these problems. I solved this one by calculating a table for the scores, storing it in a HashMap and use it to map over the input.
It's so interesting to see different ways to solve the challenge as I'm not focusing so much on speed but more on learning in my solutions, and I'm learning a lot here, thanks for sharing.
Another possibly way to maybe "complete the loop" on the ordering is possibly adding 1 to the second value (other) when you coerce it into a u8, then mod 4 it. This will cause a "Rock" to have a value of 0 when it's the 2nd (other). It's probably harder to read, but less control flow blocks etc.
Using an Outcome enum made things a lot simpler to me when I did it. Great vid tho, I'll keep watching your videos after completion, always interesting to see how other people are tackling the problem
I find matching on moves easier. And you can also use the powerful match syntax to simplify the work : match (their_move, your_move) { (Move::Paper, Move::Scissors) | (Move::Rock, Move::Paper) | (Move::Scissors, Move::Rock) => 6, // Win ! _ if their_move == your_move => 3, // Draw ! _ => 0, // Loose ! }
When rushing to finish this one, I too made the rock beats scissors, etc. more complex than it should have been but I didn't know how to do it any better.
Thank you for your awesome videos! Your channel really helps on my path of learning Rust. I was wondering how do you organize your windows this way? Is this some tool which helps you conveniently adjust multiple windows at once?
I started almost exactly the same as you with a Move enum and a from impl. Then I got lazy and just did a match on the 9 possible line strings, returning the score lol.
My solution is quite similar, I did use a dedicated RoundResult enum though instead of reusing Ordering. Also didn't know that you can do "as " on enum variants, I think I remember trying that in the past and it didn't work, could be wrong though.
yeah the as cast is a neat trick but in hindsight I probably should've used an impl like this instead, which feels less "tricky". Hard to think of everything when I'm recording live though 😅 ``` impl Move { fn score(&self) -> u32 { match self { Rock => 1, Paper => 2, Scissors => 3 } } } ```
meh, so many possible ways to implement this, but I like how you did it, even if it was technically a tiny bit more convoluted because I feel like in a real/larger program you would want to separate the logic into the trait like you did
It's validating that my implementation (which is very similar to yours, again, using traits) wasn't totally out there, although I still feel I was overly complicating the solution, compared to the simplicity of the challenge.
Yeah definitely. Although Advent of code for me is more about experimentation and pushing the corners of the language I use less often. I match all the time, and I implement partialord manually basically never. The questions can always be done in a competitive style, (less code/structure, more tricks, pattern matching solutions) but that's not how I find it fun.
@@chrisbiscardi Absolutely, I dont want to code golf it. My goal with these exercises is to become more familiar with Rust's language features and higher level abstractions.
It's funny because while the implementation demonstrating traits and stuff is neat, in this case, you would probably have been better off just writing a single match statement on strings with 9 arms lmao
Thanks for the video! One thing I'd change is add Itertools and use `collect_tuple()` instead of indexing moves, like so: ``` let (opponents, yours) = line .split(' ') .map(|s| s.parse().unwrap()) .collect_tuple() .unwrap(); ``` Functionally, it changes nothing, and perhaps it's even a tad slower, _and_ the extra `unwrap()` call is unwieldy, but indexing feels so inelegant to me. In Part 2, I created an `Outcome` enum and matched it with the opponent's hand to decide my own: ``` let (opponents, outcome) = line.split(' ').collect_tuple().unwrap(); let outcome: Outcome = outcome.parse().unwrap(); let opponents: Hand = opponents.parse().unwrap(); let yours = match (outcome, opponents) { (Outcome::Defeat, Hand::Scissors) | (Outcome::Draw, Hand::Paper) | (Outcome::Victory, Hand::Rock) => Hand::Paper, (Outcome::Defeat, Hand::Rock) | (Outcome::Draw, Hand::Scissors) | (Outcome::Victory, Hand::Paper) => Hand::Scissors, (Outcome::Defeat, Hand::Paper) | (Outcome::Draw, Hand::Rock) | (Outcome::Victory, Hand::Scissors) => Hand::Rock, }; ``` IMO, it's a bit simpler this way, though the flood of unwraps is painful.
Just wanted to mention that you can also easily calculate the winning like this: fn get_winning(m: Move) -> Move { ((m as u32 % 3) + 1).into() } and then you implement From for Move!