ascii_hangman_backend/
game.rs

1//!Defines the game state and logic
2use crate::secret::Secret;
3use std::fmt;
4
5/// A subset of the game state. Can be derived from `Game` struct.
6#[derive(Debug, PartialEq, Clone)]
7pub enum State {
8    /// The game is ongoing.
9    Ongoing,
10    /// The player won and the game is continuable.
11    Victory,
12    /// The player lost and the game is continuable.
13    Defeat,
14    /// The player won and there are no more words to guess.
15    VictoryGameOver,
16    /// The player lost and there are no more secrets to guess.
17    DefeatGameOver,
18}
19
20/// The game state.
21#[derive(Debug, PartialEq)]
22pub struct Game {
23    pub secret: Secret,
24    pub lifes: u8,
25    pub last_guess: char,
26    pub state: State,
27    pub last_game: bool,
28}
29
30impl Game {
31    /// Constructor.
32    pub fn new(secretstr: &str, lifes: u8, last_game: bool) -> Self {
33        // parse `secretsstr`, flip 'visible' every CONF_LINE_SECRET_MODIFIER__VISIBLE
34        let secret = Secret::new(secretstr);
35        Self {
36            secret,
37            lifes,
38            last_guess: ' ',
39            state: State::Ongoing,
40            last_game,
41        }
42    }
43
44    /// Process a guess and modify the game state.
45    pub fn guess(&mut self, character: char) {
46        if character == '\n' {
47            return;
48        };
49        self.last_guess = character;
50
51        let found = self.secret.guess(character);
52
53        if !found {
54            self.lifes -= 1;
55        }
56
57        self.state = if self.lifes == 0 {
58            // Disclose the secret
59            self.secret.disclose_all();
60
61            if self.last_game {
62                State::DefeatGameOver
63            } else {
64                State::Defeat
65            }
66        } else if self.secret.is_fully_disclosed() {
67            if self.last_game {
68                State::VictoryGameOver
69            } else {
70                State::Victory
71            }
72        } else {
73            State::Ongoing
74        };
75    }
76}
77
78impl fmt::Display for Game {
79    /// Graphical representation of the game state.
80    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
81        writeln!(
82            f,
83            "Lives:\t{}\tLast guess: {}\n",
84            self.lifes, self.last_guess
85        )
86    }
87}
88// ***********************
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    /// Play simulation
95    #[test]
96    fn test_game_simulation() {
97        let mut game = Game::new("_ab _cd", 2, true);
98        //println!("{:?}",game);
99
100        assert_eq!(format!("{}", game.secret), " a b   _ _\n");
101        assert_eq!(game.lifes, 2);
102        assert_eq!(game.last_guess, ' ');
103        assert_eq!(game.state, State::Ongoing);
104        assert_eq!(game.last_game, true);
105
106        // now we guess right
107        game.guess('c');
108        //println!("{:?}",game);
109
110        assert_eq!(format!("{}", game.secret), " a b   c _\n");
111        assert_eq!(game.lifes, 2);
112        assert_eq!(game.last_guess, 'c');
113        assert_eq!(game.state, State::Ongoing);
114        assert_eq!(game.last_game, true);
115
116        // now we guess wrong
117        game.guess('x');
118        //println!("{:?}",game);
119
120        assert_eq!(format!("{}", game.secret), " a b   c _\n");
121        assert_eq!(game.lifes, 1);
122        assert_eq!(game.last_guess, 'x');
123        assert_eq!(game.state, State::Ongoing);
124        assert_eq!(game.last_game, true);
125
126        // we guess wrong again and we loose
127        game.guess('y');
128        //println!("{:?}",game);
129        assert_eq!(format!("{}", game.secret), " a b   c d\n");
130        assert_eq!(game.lifes, 0);
131        assert_eq!(game.last_guess, 'y');
132        assert_eq!(game.state, State::DefeatGameOver);
133        assert_eq!(game.last_game, true);
134    }
135}