1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use std::ops::Neg;

use advent_of_code::errors::Error;

use super::rps::RPS;

#[derive(Debug, PartialEq, Eq)]
pub enum GameState {
    Win,
    Draw,
    Lose,
}

impl GameState {
    // Defines a function to gather that player1 <GameStates> the returned value i.e. player1
    // [Win]s against player2
    pub fn against(&self, player1: &RPS) -> RPS {
        let opposite = -player1;
        match self {
            Self::Draw => player1.clone(),
            Self::Win => opposite,
            Self::Lose => -opposite,
        }
    }

    pub fn points(&self) -> usize {
        match self {
            Self::Win => 6,
            Self::Draw => 3,
            Self::Lose => 0,
        }
    }
}

impl Neg for GameState {
    type Output = GameState;

    fn neg(self) -> Self::Output {
        match self {
            Self::Win => Self::Lose,
            Self::Draw => Self::Draw,
            Self::Lose => Self::Win,
        }
    }
}

impl TryFrom<char> for GameState {
    type Error = Error;

    fn try_from(value: char) -> std::result::Result<Self, Self::Error> {
        match value {
            'X' => Ok(Self::Lose),
            'Y' => Ok(Self::Draw),
            'Z' => Ok(Self::Win),
            _ => Err(Error::InvalidStruct(
                "Not a valid Rock, Paper or Scissors".to_owned(),
            )),
        }
    }
}

impl TryFrom<&str> for GameState {
    type Error = Error;

    fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
        value
            .chars()
            .next()
            .ok_or(Error::InvalidStruct(
                "RPS string must not be empty".to_owned(),
            ))?
            .try_into()
    }
}