advent-2022-rust/src/days/day02/mod.rs

224 lines
5.8 KiB
Rust

use std::cmp::Ordering;
use crate::common::file::split_lines;
use super::template::{DayTrait, ResultType};
use thiserror::Error;
const DAY_NUMBER: usize = 2;
pub struct Day;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = split_lines(lines)
.map(|line| Rps::parse_line(line))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(first, second)| second.asses_pair(&first))
.sum();
Ok(ResultType::Integer(sum))
}
fn part2(&self, lines: &str) -> anyhow::Result<ResultType> {
let sum = split_lines(lines)
.map(|line| Strategy::parse_line(line))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|(first, second)| second.fullfill(&first).asses_pair(&first))
.sum();
Ok(ResultType::Integer(sum))
}
}
#[derive(Debug, Error)]
enum RPSError {
#[error("No a valid RPS: {0}")]
ParseError(String),
#[error("Not a logal RPS line: {0}")]
IllegalLine(String),
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
enum Rps {
Rock,
Paper,
Scissors,
}
impl Rps {
pub fn parse_line(line: &str) -> Result<(Self, Self), RPSError> {
let mut parts = line.split(' ');
let (Some(first), Some(second)) = (parts.next(), parts.next()) else {
Err(RPSError::IllegalLine(line.to_owned()))?
};
let first = Rps::try_from(first)?;
let second = Rps::try_from(second)?;
Ok((first, second))
}
pub fn value(&self) -> i64 {
match self {
Rps::Rock => 1,
Rps::Paper => 2,
Rps::Scissors => 3,
}
}
pub fn asses_pair(&self, other: &Self) -> i64 {
let outcome = match self.partial_cmp(other) {
Some(Ordering::Less) => 0,
Some(Ordering::Equal) => 3,
Some(Ordering::Greater) => 6,
None => unreachable!(),
};
outcome + self.value()
}
}
impl PartialOrd for Rps {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Rps::Rock, Rps::Paper) | (Rps::Paper, Rps::Scissors) | (Rps::Scissors, Rps::Rock) => {
Some(Ordering::Less)
}
(Rps::Rock, Rps::Scissors) | (Rps::Paper, Rps::Rock) | (Rps::Scissors, Rps::Paper) => {
Some(Ordering::Greater)
}
_ => Some(Ordering::Equal),
}
}
}
impl TryFrom<&str> for Rps {
type Error = RPSError;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value {
"A" | "X" => Ok(Rps::Rock),
"B" | "Y" => Ok(Rps::Paper),
"C" | "Z" => Ok(Rps::Scissors),
_ => Err(RPSError::ParseError(value.to_owned())),
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
enum Strategy {
Loose,
Draw,
Win,
}
impl Strategy {
pub fn parse_line(line: &str) -> Result<(Rps, Self), RPSError> {
let mut parts = line.split(' ');
let (Some(first), Some(second)) = (parts.next(), parts.next()) else {
Err(RPSError::IllegalLine(line.to_owned()))?
};
let first = Rps::try_from(first)?;
let second = Strategy::try_from(second)?;
Ok((first, second))
}
pub fn fullfill(&self, other: &Rps) -> Rps {
match (other, self) {
(_, Strategy::Draw) => *other,
(Rps::Rock, Strategy::Win) | (Rps::Scissors, Strategy::Loose) => Rps::Paper,
(Rps::Paper, Strategy::Win) | (Rps::Rock, Strategy::Loose) => Rps::Scissors,
(Rps::Scissors, Strategy::Win) | (Rps::Paper, Strategy::Loose) => Rps::Rock,
}
}
}
impl TryFrom<&str> for Strategy {
type Error = RPSError;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value {
"X" => Ok(Strategy::Loose),
"Y" => Ok(Strategy::Draw),
"Z" => Ok(Strategy::Win),
_ => Err(RPSError::ParseError(value.to_owned())),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::common::file::read_string;
use anyhow::Result;
#[test]
fn test_parse() -> Result<()> {
let input = "A Y";
let expected = (Rps::Rock, Rps::Paper);
let result = Rps::parse_line(input)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_assess() -> Result<()> {
let first = Rps::Scissors;
let second = Rps::Paper;
let expected = 9;
let result = first.asses_pair(&second);
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part1() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let expected = ResultType::Integer(15);
let result = day.part1(&lines)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_parse_strategy() -> Result<()> {
let input = "A Y";
let expected = (Rps::Rock, Strategy::Draw);
let result = Strategy::parse_line(input)?;
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_assess_stragety() -> Result<()> {
let first = Rps::Scissors;
let second = Strategy::Win;
let expected = Rps::Rock;
let result = second.fullfill(&first);
assert_eq!(result, expected);
Ok(())
}
#[test]
fn test_part2() -> Result<()> {
let day = Day {};
let lines = read_string(day.get_day_number(), "example01.txt")?;
let expected = ResultType::Integer(12);
let result = day.part2(&lines)?;
assert_eq!(result, expected);
Ok(())
}
}