Rock-paper-scissors: Difference between revisions
(→{{header|Java}}: Added fixes for expandability, added rules for RPScLSp in comments) |
(J) |
||
Line 220: | Line 220: | ||
} |
} |
||
}</lang> |
}</lang> |
||
=={{header|J}}== |
|||
<lang j>require'misc strings' |
|||
game=:3 :0 |
|||
outcomes=. rps=. 0 0 0 |
|||
choice=. 1+?3 |
|||
while.#response=. prompt' Choose Rock, Paper or Scissors: ' do. |
|||
playerchoice=. 1+'rps' i. tolower {.deb response |
|||
if.4 = choice do. |
|||
smoutput 'Unknown response.' |
|||
smoutput 'Enter an empty line to quit' |
|||
continue. |
|||
end. |
|||
smoutput ' I choose ',choice {::;:'. Rock Paper Scissors' |
|||
smoutput (r=. 3 | choice-playerchoice) {:: 'Draw';'I win';'You win' |
|||
outcomes=. outcomes+0 1 2 = r |
|||
rps=. rps+1 2 3=playerchoice |
|||
choice=. 1+3|(?0) I.~ (}:%{:)+/\ 0, rps |
|||
end. |
|||
('Draws:','My wins:',:'Your wins: '),.":,.outcomes |
|||
)</lang> |
|||
Example use (playing to give the computer implementation the advantage): |
|||
<lang j> game'' |
|||
Choose Rock, Paper or Scissors: rock |
|||
I choose Scissors |
|||
You win |
|||
Choose Rock, Paper or Scissors: rock |
|||
I choose Paper |
|||
I win |
|||
Choose Rock, Paper or Scissors: rock |
|||
I choose Paper |
|||
I win |
|||
Choose Rock, Paper or Scissors: rock |
|||
I choose Paper |
|||
I win |
|||
Choose Rock, Paper or Scissors: rock |
|||
I choose Paper |
|||
I win |
|||
Choose Rock, Paper or Scissors: |
|||
Draws: 0 |
|||
My wins: 4 |
|||
Your wins: 1</lang> |
|||
=={{header|Java}}== |
=={{header|Java}}== |
Revision as of 16:37, 6 July 2011
In this task, the goal is to implement the classic children's game Rock-paper-scissors, as well as a simple predictive AI player.
Rock Paper Scissors has two players. 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's AI player, keep track of the player's choice frequency, and use that choice frequency to make a weighted random choice to beat the player's likely choice.
Ada
<lang Ada>with Ada.Text_IO; with Ada.Numerics.Float_Random;
procedure Rock_Paper_Scissors is
package Rand renames Ada.Numerics.Float_Random; Gen: Rand.Generator;
type Choice is (Rock, Paper, Scissors);
Cnt: array (Choice) of Natural := (1, 1, 1); -- for the initialization: pretend that each of Rock, Paper, -- and Scissors, has been played once by the human -- else the first computer choice would be deterministic
function Computer_Choice return Choice is Random_Number: Natural := Integer(Rand.Random(Gen) * (Float(Cnt(Rock)) + Float(Cnt(Paper)) + Float(Cnt(Scissors)))); begin if Random_Number < Cnt(Rock) then -- guess the human will choose Rock return Paper; elsif Random_Number - Cnt(Rock) < Cnt(Paper) then -- guess the human will choose Paper return Scissors; else -- guess the human will choose Scissors return Rock; end if; end Computer_Choice;
Finish_The_Game: exception;
function Human_Choice return Choice is Done: Boolean := False; T: constant String := "enter ""r"" for Rock, ""p"" for Paper, or ""s"" for Scissors""!"; U: constant String := "or enter ""q"" to Quit the game"; Result: Choice; begin Ada.Text_IO.Put_Line(T); Ada.Text_IO.Put_Line(U); while not Done loop Done := True; declare S: String := Ada.Text_IO.Get_Line; begin if S="r" or S="R" then Result := Rock; elsif S="p" or S = "P" then Result := Paper; elsif S="s" or S="S" then Result := Scissors; elsif S="q" or S="Q" then raise Finish_The_Game; else Done := False; end if; end; end loop; return Result; end Human_Choice;
type Result is (Human_Wins, Draw, Computer_Wins);
function "<" (X, Y: Choice) return Boolean is -- X < Y if X looses against Y begin case X is when Rock => return (Y = Paper); when Paper => return (Y = Scissors); when Scissors => return (Y = Rock); end case; end "<";
Score: array(Result) of Natural := (0, 0, 0);
C,H: Choice;
Res: Result;
begin
-- play the game loop C := Computer_Choice; -- the computer makes its choice first H := Human_Choice; -- now ask the player for his/her choice Cnt(H) := Cnt(H) + 1; -- update the counts for the AI if C < H then Res := Human_Wins; elsif H < C then Res := Computer_Wins; else Res := Draw; end if; Ada.Text_IO.Put_Line("COMPUTER'S CHOICE: " & Choice'Image(C) & " RESULT: " & Result'Image(Res)); Ada.Text_IO.New_Line; Score(Res) := Score(Res) + 1; end loop;
exception
when Finish_The_Game => Ada.Text_IO.New_Line; for R in Score'Range loop Ada.Text_IO.Put_Line(Result'Image(R) & Natural'Image(Score(R))); end loop;
end Rock_Paper_Scissors;</lang>
First and last few lines of the output of a game, where the human did permanently choose Rock:
./rock_paper_scissors enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: SCISSORS RESULT: HUMAN_WINS enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: ROCK RESULT: DRAW enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: SCISSORS RESULT: HUMAN_WINS enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: ROCK RESULT: DRAW [...] enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: ROCK RESULT: DRAW enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game r COMPUTER'S CHOICE: PAPER RESULT: COMPUTER_WINS enter "r" for Rock, "p" for Paper, or "s" for Scissors"! or enter "q" to Quit the game q HUMAN_WINS 2 DRAW 5 COMPUTER_WINS 21
C
<lang C>#include <stdio.h>
- include <stdlib.h>
int rand_i(int n) { int rand_max = RAND_MAX - (RAND_MAX % n); int ret; while ((ret = rand()) >= rand_max); return ret/(rand_max / n); }
int weighed_rand(int *tbl, int len) { int i, sum, r; for (i = 0, sum = 0; i < len; sum += tbl[i++]); if (!sum) return rand_i(len);
r = rand_i(sum) + 1; for (i = 0; i < len && (r -= tbl[i]) > 0; i++); return i; }
int main() { int user_action, my_action; int user_rec[] = {0, 0, 0}; char *names[] = { "Rock", "Paper", "Scissors" }; char str[2]; char *winner[] = { "We tied.", "Meself winned.", "You win." };
while (1) { my_action = (weighed_rand(user_rec, 3) + 1) % 3;
printf("\nYour choice [1-3]:\n" " 1. Rock\n 2. Paper\n 3. Scissors\n> ");
/* scanf is a terrible way to do input. should use stty and keystrokes */ if (!scanf("%d", &user_action)) { scanf("%1s", str); if (*str == 'q') return 0; continue; } user_action --; if (user_action > 2 || user_action < 0) { printf("invalid choice; again\n"); continue; } printf("You chose %s; I chose %s. %s\n", names[user_action], names[my_action], winner[(my_action - user_action + 3) % 3]);
user_rec[user_action]++; } }</lang>
J
<lang j>require'misc strings' game=:3 :0
outcomes=. rps=. 0 0 0 choice=. 1+?3 while.#response=. prompt' Choose Rock, Paper or Scissors: ' do. playerchoice=. 1+'rps' i. tolower {.deb response if.4 = choice do. smoutput 'Unknown response.' smoutput 'Enter an empty line to quit' continue. end. smoutput ' I choose ',choice {::;:'. Rock Paper Scissors' smoutput (r=. 3 | choice-playerchoice) {:: 'Draw';'I win';'You win' outcomes=. outcomes+0 1 2 = r rps=. rps+1 2 3=playerchoice choice=. 1+3|(?0) I.~ (}:%{:)+/\ 0, rps end. ('Draws:','My wins:',:'Your wins: '),.":,.outcomes
)</lang>
Example use (playing to give the computer implementation the advantage):
<lang j> game
Choose Rock, Paper or Scissors: rock I choose Scissors
You win
Choose Rock, Paper or Scissors: rock I choose Paper
I win
Choose Rock, Paper or Scissors: rock I choose Paper
I win
Choose Rock, Paper or Scissors: rock I choose Paper
I win
Choose Rock, Paper or Scissors: rock I choose Paper
I win
Choose Rock, Paper or Scissors:
Draws: 0 My wins: 4 Your wins: 1</lang>
Java
This could probably be made simpler, but some more complexity is necessary so that other items besides rock, paper, and scissors can be added (as school children and nerds like to do [setup for rock-paper-scissors-lizard-spock is in multi-line comments]). The method getAIChoice()
borrows from the Ada example in spirit, but is more generic to additional items.
<lang java5>import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class RPS { public enum Item{ ROCK, PAPER, SCISSORS, /*LIZARD, SPOCK*/; } //EnumMap uses a simple array under the hood public static Map<Item, List<Item>> beats = new EnumMap<Item, List<Item>>(Item.class){{ put(Item.ROCK, Arrays.asList(Item.SCISSORS/*, Item.LIZARD*/)); put(Item.PAPER, Arrays.asList(Item.ROCK/*, Item.SPOCK*/)); put(Item.SCISSORS, Arrays.asList(Item.PAPER/*, Item.LIZARD*/)); /* put(Item.SPOCK, Arrays.asList(Item.ROCK, Item.SCISSORS)); put(Item.LIZARD, Arrays.asList(Item.SPOCK, Item.PAPER)); */ }};
public static Map<Item, List<Item>> losesTo = new EnumMap<Item, List<Item>>(Item.class){{ put(Item.SCISSORS, Arrays.asList(Item.ROCK/*, Item.SPOCK*/)); put(Item.ROCK, Arrays.asList(Item.PAPER/*, Item.SPOCK*/)); put(Item.PAPER, Arrays.asList(Item.SCISSORS/*, Item.LIZARD*/)); /* put(Item.SPOCK, Arrays.asList(Item.PAPER, Item.LIZARD)); put(Item.LIZARD, Arrays.asList(Item.SCISSORS, Item.ROCK)); */ }};
public static Map<Item, Integer> counts = new EnumMap<Item, Integer>(Item.class){{ put(Item.ROCK, 1); put(Item.PAPER, 1); put(Item.SCISSORS, 1); //add other counts here for additional types /* put(Item.SPOCK, 1); put(Item.LIZARD, 1); */ }};
private static int totalThrows = Item.values().length;
public static void main(String[] args){ Scanner in = new Scanner(System.in); System.out.print("Make your choice: "); while(in.hasNextLine()){ Item aiChoice = getAIChoice(); String input = in.nextLine(); Item choice; try{ choice = Item.valueOf(input.toUpperCase()); }catch (IllegalArgumentException ex){ System.out.println("Invalid choice"); continue; } counts.put(choice, counts.get(choice) + 1); totalThrows++; System.out.println("Computer chose: " + aiChoice); if(aiChoice.equals(choice)){ System.out.println("Tie!"); }else if(beats.get(choice).contains(aiChoice)){ System.out.println("You chose...wisely. You win!"); }else{ System.out.println("You chose...poorly. You lose!"); } System.out.print("Make your choice: "); } }
private static Item getAIChoice() { int rand = (int)(Math.random() * (totalThrows)); for(Item item:Item.values()){ if(rand < counts.get(item)){ Collections.shuffle(losesTo.get(item)); return losesTo.get(item).get(0); } rand -= counts.get(item); } return null; } }</lang> Sample output:
Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: rock Computer chose: SCISSORS You chose...wisely. You win! Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: rock Computer chose: SCISSORS You chose...wisely. You win! Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: rock Computer chose: ROCK Tie! Make your choice: rock Computer chose: ROCK Tie! Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: rock Computer chose: PAPER You chose...poorly. You lose! Make your choice: scissors Computer chose: PAPER You chose...wisely. You win! Make your choice: scissors Computer chose: PAPER You chose...wisely. You win! ...
Python
<lang python>#!/usr/bin/python from random import randint, random from operator import add from functools import reduce from bisect import bisect_left
WHATBEATS = { 'paper' : 'scissors',
'scissors' : 'rock', 'rock' : 'paper' }
ORDER = ('rock', 'paper', 'scissors')
CHOICEFREQUENCY = {}
def probChoice(choices, probabilities):
scalefactor = reduce(add, probabilities) prob_accumulator = 0 accumulator = [] for p in probabilities: prob_accumulator += float(p) / scalefactor accumulator.append(prob_accumulator) r = random() bsct = bisect_left(accumulator, r) chc = choices[bsct] return chc
def checkWinner(a, b):
global WHATBEATS
abeater = WHATBEATS[a] bbeater = WHATBEATS[b]
if b == abeater: return b elif a == bbeater: return a
return None
def sanitizeChoice(a):
# Drop it to lower-case return a.lower()
def registerPlayerChoice(choice):
global CHOICEFREQUENCY if choice in CHOICEFREQUENCY: CHOICEFREQUENCY[choice] += 1 else: CHOICEFREQUENCY[choice] = 1
def getRandomChoice():
global WHATBEATS global ORDER global CHOICEFREQUENCY if len(CHOICEFREQUENCY.keys()) == 0: return ORDER[randint(0,len(ORDER)-1)] choices = CHOICEFREQUENCY.keys() probabilities = CHOICEFREQUENCY.values() return WHATBEATS[probChoice(choices, probabilities)]
while True:
choice = raw_input() choice = sanitizeChoice(choice) if not choice in ORDER: continue
compChoice = getRandomChoice() print "Computer picked", compChoice+",",
# Don't register the player choice until after the computer has made # its choice. registerPlayerChoice(choice)
winner = checkWinner(choice, compChoice)
if winner == None: winner = "nobody"
print winner, "wins!"</lang>
Output, where player always chooses Rock:
!504 #5 j0 ?0 $ ./rps.py rock Computer picked scissors, rock wins! rock Computer picked paper, paper wins! rock Computer picked paper, paper wins! rock Computer picked paper, paper wins! rock Computer picked paper, paper wins!
Tcl
<lang tcl>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}]
}</lang> Sample run:
Your move? [R]ock, [P]aper, [S]cissors: rock I play "Scissors" You win! Cumulative scores: 1 to you, 0 to me Your move? [R]ock, [P]aper, [S]cissors: r I play "Paper" I win! Cumulative scores: 1 to you, 1 to me Your move? [R]ock, [P]aper, [S]cissors: s I play "Paper" You win! Cumulative scores: 2 to you, 1 to me Your move? [R]ock, [P]aper, [S]cissors: sciss I play "Paper" You win! Cumulative scores: 3 to you, 1 to me Your move? [R]ock, [P]aper, [S]cissors: p I play "Paper" A draw! Cumulative scores: 3 to you, 1 to me Your move? [R]ock, [P]aper, [S]cissors: zaphod beeblebrox Sorry, I don't understand that. Try again please. Your move? [R]ock, [P]aper, [S]cissors: r I play "Scissors" You win! Cumulative scores: 4 to you, 1 to me Your move? [R]ock, [P]aper, [S]cissors: ^D Bye!