191 lines
5.2 KiB
Rust
191 lines
5.2 KiB
Rust
use num_derive::FromPrimitive;
|
|
use num_traits::FromPrimitive;
|
|
use std::fs;
|
|
use std::io::{self, BufRead, Read, Seek};
|
|
use std::str::FromStr;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive)]
|
|
enum Rps {
|
|
Rock = 0,
|
|
Paper,
|
|
Scissors,
|
|
}
|
|
|
|
impl FromStr for Rps {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
|
match input {
|
|
"A" | "X" => Ok(Rps::Rock),
|
|
"B" | "Y" => Ok(Rps::Paper),
|
|
"C" | "Z" => Ok(Rps::Scissors),
|
|
_ => Err("Unknown move type!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Rps {
|
|
fn versus(self, other: Self) -> Outcome {
|
|
match (self, other) {
|
|
(x, y) if x == y => Outcome::Draw,
|
|
(Rps::Rock, Rps::Scissors) | (Rps::Paper, Rps::Rock) | (Rps::Scissors, Rps::Paper) => {
|
|
Outcome::Win
|
|
}
|
|
_ => Outcome::Lose,
|
|
}
|
|
}
|
|
|
|
fn score(self, theirs: Rps) -> u32 {
|
|
self as u32
|
|
+ 1
|
|
+ match self.versus(theirs) {
|
|
Outcome::Win => 6,
|
|
Outcome::Draw => 3,
|
|
Outcome::Lose => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
enum Outcome {
|
|
Lose,
|
|
Draw,
|
|
Win,
|
|
}
|
|
|
|
impl FromStr for Outcome {
|
|
type Err = &'static str;
|
|
|
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
|
match input {
|
|
"X" => Ok(Self::Lose),
|
|
"Y" => Ok(Self::Draw),
|
|
"Z" => Ok(Self::Win),
|
|
_ => Err("Unknown outcome!"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Outcome {
|
|
fn against(self, theirs: Rps) -> Rps {
|
|
let inc = match self {
|
|
Outcome::Win => 1,
|
|
Outcome::Draw => 0,
|
|
Outcome::Lose => -1,
|
|
};
|
|
|
|
FromPrimitive::from_i8((theirs as i8 + inc).rem_euclid(3)).unwrap()
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let filename = "etc/p02.txt";
|
|
let file = fs::File::open(filename).expect("Can't open file");
|
|
let mut reader = io::BufReader::new(file);
|
|
|
|
println!("PART ONE ---------------");
|
|
part1(reader.by_ref().lines());
|
|
reader.rewind().expect("Can't rewind");
|
|
|
|
println!("\n PART TWO ---------------");
|
|
part2(reader.lines());
|
|
}
|
|
|
|
fn part1(lines: impl Iterator<Item = std::io::Result<String>>) {
|
|
let mut total_score: u32 = 0;
|
|
for l in lines {
|
|
let line = l.expect("Can't read line.");
|
|
let mut split = line.split_ascii_whitespace();
|
|
let theirs = split
|
|
.next()
|
|
.expect("Want two items per line")
|
|
.parse::<Rps>()
|
|
.expect("Can't get their move");
|
|
let mine = split
|
|
.next()
|
|
.expect("Want two items per line")
|
|
.parse::<Rps>()
|
|
.expect("Can't get my move");
|
|
|
|
let score = mine.score(theirs);
|
|
total_score += score;
|
|
println!(
|
|
"Line \"{}\" - Theirs: {:?}, Mine: {:?}, score {}",
|
|
line, theirs, mine, score,
|
|
);
|
|
}
|
|
|
|
println!("Total score is {}", total_score);
|
|
}
|
|
|
|
fn part2(lines: impl Iterator<Item = std::io::Result<String>>) {
|
|
let mut total_score: u32 = 0;
|
|
for l in lines {
|
|
let line = l.expect("Can't read line.");
|
|
let mut split = line.split_ascii_whitespace();
|
|
let theirs = split
|
|
.next()
|
|
.expect("Want two items per line")
|
|
.parse::<Rps>()
|
|
.expect("Can't get their move");
|
|
let strat = split
|
|
.next()
|
|
.expect("Want two items per line")
|
|
.parse::<Outcome>()
|
|
.expect("Can't get my strategy");
|
|
|
|
let mine = strat.against(theirs);
|
|
|
|
let score = mine.score(theirs);
|
|
total_score += score;
|
|
println!(
|
|
"Line \"{}\" - Theirs: {:?}, Strat: {:?}, Mine: {:?}, score {}",
|
|
line, theirs, strat, mine, score,
|
|
);
|
|
}
|
|
|
|
println!("Total score is {}", total_score);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn rps() {
|
|
assert_eq!(Outcome::Win, Rps::Rock.versus(Rps::Scissors));
|
|
assert_eq!(Outcome::Win, Rps::Paper.versus(Rps::Rock));
|
|
assert_eq!(Outcome::Win, Rps::Scissors.versus(Rps::Paper));
|
|
assert_eq!(Outcome::Lose, Rps::Rock.versus(Rps::Paper));
|
|
assert_eq!(Outcome::Lose, Rps::Paper.versus(Rps::Scissors));
|
|
assert_eq!(Outcome::Lose, Rps::Scissors.versus(Rps::Rock));
|
|
}
|
|
|
|
#[test]
|
|
fn scores() {
|
|
assert_eq!(7, Rps::Rock.score(Rps::Scissors));
|
|
assert_eq!(8, Rps::Paper.score(Rps::Rock));
|
|
assert_eq!(1, Rps::Rock.score(Rps::Paper));
|
|
assert_eq!(6, Rps::Scissors.score(Rps::Scissors));
|
|
}
|
|
|
|
#[test]
|
|
fn strat_to_move() {
|
|
assert_eq!(Rps::Paper, Outcome::Win.against(Rps::Rock));
|
|
assert_eq!(Rps::Scissors, Outcome::Win.against(Rps::Paper));
|
|
assert_eq!(Rps::Rock, Outcome::Win.against(Rps::Scissors));
|
|
|
|
assert_eq!(Rps::Scissors, Outcome::Lose.against(Rps::Rock));
|
|
assert_eq!(Rps::Rock, Outcome::Lose.against(Rps::Paper));
|
|
|
|
assert_eq!(Rps::Rock, Outcome::Draw.against(Rps::Rock));
|
|
}
|
|
|
|
#[test]
|
|
fn part_two_ex() {
|
|
assert_eq!(4, Outcome::Draw.against(Rps::Rock).score(Rps::Rock));
|
|
assert_eq!(1, Outcome::Lose.against(Rps::Paper).score(Rps::Paper));
|
|
assert_eq!(7, Outcome::Win.against(Rps::Scissors).score(Rps::Scissors));
|
|
}
|
|
}
|