21 game

You are encouraged to solve this task according to the task description, using any language you may know.
21 is a two player game, the game is played by choosing
a number (1, 2, or 3) to be added to the running total.

The game is won by the player whose chosen number causes the running total
to reach exactly 21.

The running total starts at zero.
One player will be the computer.

Players alternate supplying a number to be added to the running total.



Task

Write a computer program that will:

 do the prompting (or provide a button menu), 
 check for errors and display appropriate error messages, 
 do the additions (add a chosen number to the running total), 
 display the running total, 
 provide a mechanism for the player to quit/exit/halt/stop/close the program,
 issue a notification when there is a winner, and
 determine who goes first (maybe a random or user choice, or can be specified when the game begins). 


use rand::Rng;
use std::io;
 
#[derive(Clone)]
enum PlayerType {
    Human,
    Computer,
}
 
#[derive(Clone)]
struct Player {
    name: String,
    wins: u32,  // holds wins
    level: u32, // difficulty level of Computer
    player_type: PlayerType, 
}
 
trait Choose {
    fn choose(&self, game: &Game) -> u8;
}
 
impl Player {
    fn new(name: &str, player_type: PlayerType, level: u32) -> Player {
        Player {
            name: String::from(name),
            wins: 0,
            level,
            player_type,
        }
    }
    fn get_name(&self) -> &str {
        &self.name[..]
    }
    fn get_level(&self) -> u32 {
        self.level
    }
    fn add_win(&mut self) {
        self.wins += 1
    }
    fn level_up(&mut self) {
        self.level += 1
    }
}
 
impl Choose for Player {
    fn choose(&self, game: &Game) -> u8 {
        match self.player_type {
            PlayerType::Human => loop {
                let max_choice = game.max_choice();
                match max_choice {
                    1 => println!("Enter a number 1 to win (or quit):"),
                    _ => println!("Enter a number between 1 and {} (or quit):", max_choice)
                }
                let mut guess = String::new();
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to read line");
                if guess.trim() == "quit" {
                    return 0
                }
                let guess: u8 = match guess.trim().parse() {
                    Ok(num) if num >= 1 && num <= max_choice => num,
                    Ok(_) => continue,
                    Err(_) => continue,
                };
                return guess;
            },
            PlayerType::Computer => match self.level {
                5 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => 1,
                },
                4 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => rand::thread_rng().gen_range(1, 3),
                },
                3 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => rand::thread_rng().gen_range(1, 4),
                },
                2 => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    _ => rand::thread_rng().gen_range(1, 3),
                },
                1 => 1,
                _ => match game.get_total() {
                    total if total == 20 => 1,
                    total if total == 19 => 2,
                    total if total == 18 => 3,
                    _ => match game.get_remaining() % 4 {
                        0 => rand::thread_rng().gen_range(1, 4),
                        _ => game.get_remaining() % 4,
                    },
                },
            },
        }
    }
}
 
struct Game {
    players: Vec<Player>,
    turn: u8,  
    total: u8, 
    start: u8, // determines which player goes first
}
 
impl Game {
    fn init(players: &Vec<Player>) -> Game {
        Game {
            players: players.to_vec(),
            turn: 1,
            total: 0,
            start: rand::thread_rng().gen_range(0, 2),
        }
    }
    fn play(&mut self) -> &Player {
        loop {
            println!(
                "Total now {} (remaining: {})",
                self.get_total(),
                self.get_remaining()
            );
            {
                let player = self.whose_turn();
                println!("Turn: {} ({} turn)", self.get_turn(), player.get_name());
                let choice = player.choose(&self);
                if choice == 0 {
                    self.next_turn();
                    break; // quit
                }
                println!("{} choose {}", player.get_name(), choice);
                self.add_total(choice)
            }
            if self.get_total() >= 21 {
                break;
            }
            println!("");
            self.next_turn();
        }
        self.whose_turn()
    }
    fn add_total(&mut self, choice: u8) {
        self.total += choice;
    }
    fn next_turn(&mut self) {
        self.turn += 1;
    }
    fn whose_turn(&self) -> &Player {
        let index: usize = ((self.turn + self.start) % 2).into();
        &self.players[index]
    }
    fn get_total(&self) -> u8 {
        self.total
    }
    fn get_remaining(&self) -> u8 {
        21 - self.total
    }
    fn max_choice(&self) -> u8 {
        match self.get_remaining() {
            1 => 1,
            2 => 2,
            _ => 3
        }
    }
    fn get_turn(&self) -> u8 {
        self.turn
    }
}
 
fn main() {
    let mut game_count = 0;
    let mut players = vec![
        Player::new("human", PlayerType::Human, 0),
        Player::new("computer", PlayerType::Computer, 1),
    ];
    println!("21 Game");
    println!("Press enter key to start");
    {
        let _ = io::stdin().read_line(&mut String::new());
    }
    loop {
        game_count += 1;
        let mut game = Game::init(&players);
        let winner = game.play();
        {
            let mut index = 0;
            while index < players.len() {
                if players[index].get_name() == winner.get_name() {
                    players[index].add_win();
                }
                index += 1
            }
        }
        println!("\n{} won game {}\n", winner.get_name(), game_count);
        // limit game count
        if game_count >= 10000 {
            break;
        }
        // ask player if they want to keep on playing
        println!("Press enter key to play again (or quit):");
        let mut reply = String::new();
        io::stdin()
            .read_line(&mut reply)
            .expect("Failed to read line");
        if reply.trim() == "quit" {
            break;
        }
        // level up computer
        if winner.get_name() != "computer" {
            println!("Computer leveling up ...");
            players[1].level_up();
            println!("Computer now level {}!", players[1].get_level());
            println!("Beware!\n");
        }
    }
    println!("player: {} win: {}", players[0].get_name(), players[0].wins);
    println!("player: {} win: {}", players[1].get_name(), players[1].wins);
}
 