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.




package require Tcl 8.5
 
### Choices are represented by integers, which are indices into this list:
###    Rock, Paper, Scissors
### Normally, idiomatic Tcl code uses names for these sorts of things, but it
### turns out that using integers simplifies the move-comparison logic.
 
# How to ask for a move from the human player
proc getHumanMove {} {
    while 1 {
	puts -nonewline "Your move? \[R\]ock, \[P\]aper, \[S\]cissors: "
	flush stdout
	gets stdin line
	if {[eof stdin]} {
	    puts "\nBye!"
	    exit
	}
	set len [string length $line]
	foreach play {0 1 2} name {"rock" "paper" "scissors"} {
	    # Do a prefix comparison
	    if {$len && [string equal -nocase -length $len $line $name]} {
		return $play
	    }
	}
	puts "Sorry, I don't understand that. Try again please."
    }
}
 
# How to ask for a move from the machine player
proc getMachineMove {} {
    global states
    set choice [expr {int(rand() * [::tcl::mathop::+ {*}$states 3])}]
    foreach play {1 2 0} count $states {
	if {[incr sum [expr {$count+1}]] > $choice} {
	    puts "I play \"[lindex {Rock Paper Scissors} $play]\""
	    return $play
	}
    }
}
 
# Initialize some state variables
set states {0 0 0}
set humanWins 0
set machineWins 0
 
# The main game loop
while 1 {
    # Get the moves for this round
    set machineMove [getMachineMove]
    set humanMove [getHumanMove]
    # Report on what happened
    if {$humanMove == $machineMove} {
	puts "A draw!"
    } elseif {($humanMove+1)%3 == $machineMove} {
	puts "I win!"
	incr machineWins
    } else {
	puts "You win!"
	incr humanWins
    }
    puts "Cumulative scores: $humanWins to you, $machineWins to me"
    # Update the state of how the human has played in the past
    lset states $humanMove [expr {[lindex $states $humanMove] + 1}]
}