Rock-paper-scissors

From Rosetta Code
Revision as of 07:47, 6 July 2011 by rosettacode>Dkf (moved Rock Paper Scissors to Rock-paper-scissors: Capitalization policy/match WP name)
Rock-paper-scissors is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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>

  1. 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>

Java

Works with: Java version 1.5+

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 like to do). The method getAIChoice() borrows from the Ada example in spirit, but is more generic to additional items. <lang java5>import java.util.EnumMap; import java.util.Map; import java.util.Scanner;

public class RPS { public enum Item{ ROCK, PAPER, SCISSORS; //add other throwable items here if you wish } //EnumMap uses a simple array under the hood public static Map<Item, Item> beats = new EnumMap<Item, Item>(Item.class){{ put(Item.ROCK, Item.SCISSORS); put(Item.PAPER, Item.ROCK); put(Item.SCISSORS, Item.PAPER); //add other rules here for additional types }};

public static Map<Item, Item> losesTo = new EnumMap<Item, Item>(Item.class){{ put(Item.SCISSORS, Item.ROCK); put(Item.ROCK, Item.PAPER); put(Item.PAPER, Item.SCISSORS); //add other rules here for additional types }};

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 }};

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).equals(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)){ return losesTo.get(item); } 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!