Task

Implement the classic children's game Rock-paper-scissors, as well as a simple predictive   AI   (artificial intelligence)   player.

Rock Paper Scissors is a two player game.

Each player chooses one of rock, paper or scissors, without knowing the other player's choice.

The winner is decided by a set of rules:

   Rock beats scissors
   Scissors beat paper
   Paper beats rock


If both players choose the same thing, there is no winner for that round.

For this task, the computer will be one of the players.

The operator will select Rock, Paper or Scissors and the computer will keep a record of the choice frequency, and use that information to make a weighted random choice in an attempt to defeat its opponent.



Extra credit

Support additional choices   additional weapons.




 
use 5.012;
use warnings;
use utf8;
use open qw(:encoding(utf-8) :std);
use Getopt::Long;
 
package Game {
    use List::Util qw(shuffle first);
 
    my $turns        = 0;
    my %human_choice = ( rock => 0, paper => 0, scissors => 0, );
    my %comp_choice  = ( rock => 0, paper => 0, scissors => 0, );
    my %what_beats =
      ( rock => 'paper', paper => 'scissors', scissors => 'rock', );
    my $comp_wins  = 0;
    my $human_wins = 0;
    my $draws      = 0;
 
    sub save_human_choice {
        my $ch = lc pop;
        if ( exists $human_choice{ $ch } ) {
            ++$human_choice{ $ch };
        }
        else {
            die __PACKAGE__ . ":: wrong choice: '$ch'";
        }
    }
 
    sub get_comp_choice {
        my @keys = shuffle keys %human_choice;
        my $ch;
        my ( $prob, $rand ) = ( 0, rand );
        $ch = ( first { $rand <= ( $prob += ( $human_choice{ $_ } / $turns ) ) } @keys )
            if $turns > 0;
        $ch //= $keys[0];
        $ch = $what_beats{ $ch };
        ++$comp_choice{ $ch };
        return $ch;
    }
 
    sub make_turn {
        my ( $comp_ch, $human_ch ) = ( pop(), pop() );
        ++$turns;
        if ( $what_beats{ $human_ch } eq $comp_ch ) {
            ++$comp_wins;
            return 'I win!';
        }
        elsif ( $what_beats{ $comp_ch } eq $human_ch ) {
            ++$human_wins;
            return 'You win!';
        }
        else {
            ++$draws;
            return 'Draw!';
        }
    }
 
    sub get_final_report {
        my $report =
            "You chose:\n"
          . "  rock     = $human_choice{rock} times,\n"
          . "  paper    = $human_choice{paper} times,\n"
          . "  scissors = $human_choice{scissors} times,\n"
          . "I chose:\n"
          . "  rock     = $comp_choice{rock} times,\n"
          . "  paper    = $comp_choice{paper} times,\n"
          . "  scissors = $comp_choice{scissors} times,\n"
          . "Turns: $turns\n"
          . "I won: $comp_wins, you won: $human_wins, draws: $draws\n";
        return $report;
    }
}
 
sub main {
    GetOptions( 'quiet' => \my $quiet );
    greet() if !$quiet;
    while (1) {
        print_next_line() if !$quiet;
        my $input = get_input();
        last unless $input;
        if ( $input eq 'error' ) {
            print "I don't understand!\n" if !$quiet;
            redo;
        }
        my $comp_choice = Game::get_comp_choice();
        Game::save_human_choice($input);
        my $result = Game::make_turn( $input, $comp_choice );
        describe_turn_result( $input, $comp_choice, $result )
          if !$quiet;
    }
    print Game::get_final_report();
}
 
sub greet {
    print "Welcome to the Rock-Paper-Scissors game!\n"
      . "Choose 'rock', 'paper' or 'scissors'\n"
      . "Enter empty line or 'quit' to quit\n";
}
 
sub print_next_line {
    print 'Your choice: ';
}
 
sub get_input {
    my $input = <>;
    print "\n" and return if !$input;    # EOF
    chomp $input;
    return if !$input or $input =~ m/\A \s* q/xi;
    return 
        ( $input =~ m/\A \s* r/xi ) ? 'rock'
      : ( $input =~ m/\A \s* p/xi ) ? 'paper'
      : ( $input =~ m/\A \s* s/xi ) ? 'scissors'
      :                               'error';
}
 
sub describe_turn_result {
    my ( $human_ch, $comp_ch, $result ) = @_;
    print "You chose \u$human_ch, I chose \u$comp_ch. $result\n";
}
 
main();
 