Play Rock-Paper-Scissors
You are player 1. I am player 2.
A Rock-Paper-Scissors Tournament
The code displayed when you press the first four buttons below implements a rock-paper-scissors tournament. When viewing the JavaScript version of the code, a fifth button labeled Try to win by: followed by a value (3 by default) is visible.
Assignment
Question and Coding (100 points + possible bonus points)
- Describe the range of values that Math.random may return. (10 points)
- Modify Utility.randomInt (under To Do) so that it returns a random integer between the values of min and max inclusive. (20 points)
- Modify KingOfTheHill (under To Do) so that an instance of KingOfTheHill:
- always wins at least one out of three matches in the Rock-Paper-Scissors tournament described above. (30 points)
- always wins at least two out of three matches in the Rock-Paper-Scissors tournament. (15 points)
- always wins at least two out of three matches in the fewest number of rounds. (pride)
- Submit your answer and your code for Utility.randomInt and KingOfTheHill by end of day, Tuesday, 3 January 2017 (extended due to snow days):
- Email your answer and code to jspurgeon@vcstudent.org with the subject "Exercise[31]". (10 points)
- Work submitted after the due date will be penalized 5 points per day late, up to 20 points.
How to Play in Java
@ www.compilejava.net ...
class PlayRPS { public static void main(String[] args) { RPSMatch match = new RPSMatch(null, null); match.setChoiceOfPlayer1(args[0]); match.setIndexOfChoiceOfPlayer2(Utility.randomInt(0, 2)); System.out.println(match.getReplay()); } }
How We Play (Above)
function PlayRPS(choice) { var match = new RPSMatch(null, null); match.setChoiceOfPlayer1(choice); match.setIndexOfChoiceOfPlayer2(Utility.randomInt(0, 2)); var results = match.getReplay(); console.log(results); if (document.getElementById('RPSAlertFlag').selectedIndex === 0) { alert(results); } }
class RPSMatch{
static class CONST {
final static int PLAYERS_TIE = 0;
final static int PLAYER1_WINS = 1;
final static int PLAYER2_WINS = -1;
static String[] CHOICES() {
String[] a = {"rock", "paper", "scissors"};
return a;
}
}
static int getIndexOf(String choice) {
String[] choices = CONST.CHOICES();
for (int i=0; i< choices.length; i++) {
if (choices[i].equals(choice)) {
return i;
}
}
return -1;
}
static String getChoiceAt(int index) {
return index >= 0 && index <= 2 ?
CONST.CHOICES()[index] : "bad choice";
}
RPSCompetitor player1 = null;
RPSCompetitor player2 = null;
class State {
int indexOfChoiceOfPlayer1;
int indexOfChoiceOfPlayer2;
double netScore;
long roundsPlayed;
}
State state;
RPSMatch(RPSCompetitor p1, RPSCompetitor p2) {
player1 = p1;
player2 = p2;
state = new State();
}
int getIndexOfNextChoiceOf(RPSCompetitor player) {
if (player == null) {
throw new Error("player is null");
}
if (player instanceof CanGetIndex) {
return player.getIndex();
}
if (player instanceof CanGetChoice) {
return RPSMatch.getIndexOf(player.getChoice());
}
throw new Error("player can't play");
}
void setPlayer1(RPSCompetitor player) {
player1 = player;
}
void setPlayer2(RPSCompetitor player) {
player2 = player;
}
void setChoiceOfPlayer1(String choice) {
setIndexOfChoiceOfPlayer1(RPSMatch.getIndexOf(choice));
}
void setChoiceOfPlayer2(String choice) {
setIndexOfChoiceOfPlayer2(RPSMatch.getIndexOf(choice));
}
void setIndexOfChoiceOfPlayer1(int i) {
if (-1 < i && i < 3) {
state.indexOfChoiceOfPlayer1 = i;
}
}
void setIndexOfChoiceOfPlayer2(int i) {
if (-1 < i && i < 3) {
state.indexOfChoiceOfPlayer2 = i;
}
}
int getIndexOfChoiceOfPlayer1() {
return state.indexOfChoiceOfPlayer1;
}
int getIndexOfChoiceOfPlayer2() {
return state.indexOfChoiceOfPlayer2;
}
double getNetScore() {
return state.netScore;
}
long getRoundsPlayed() {
return state.roundsPlayed;
}
void makePlayersChoose() {
int i1 = getIndexOfNextChoiceOf(player1);
int i2 = getIndexOfNextChoiceOf(player2);
setIndexOfChoiceOfPlayer1(i1);
setIndexOfChoiceOfPlayer2(i2);
}
void revealChoices() {
state.netScore += getOutcome();
state.roundsPlayed++;
}
void playAgain() {
makePlayersChoose();
revealChoices();
}
int getOutcome() {
int i1 = getIndexOfChoiceOfPlayer1();
int i2 = getIndexOfChoiceOfPlayer2();
int numChoices = CONST.CHOICES().length;
int beatsPlayer2 = (i2 + 1) % numChoices;
return (i1 == i2) ? CONST.PLAYERS_TIE :
(i1 == beatsPlayer2 || i2 == -1) ? CONST.PLAYER1_WINS :
CONST.PLAYER2_WINS;
}
String getChoiceOfPlayer1() {
return RPSMatch.getChoiceAt(getIndexOfChoiceOfPlayer1());
}
String getChoiceOfPlayer2() {
return RPSMatch.getChoiceAt(getIndexOfChoiceOfPlayer2());
}
String getResult() {
int outcome = getOutcome();
return outcome == CONST.PLAYER1_WINS ? "player 1 wins" :
outcome == CONST.PLAYER2_WINS ? "player 2 wins" : "players tie";
}
String getReplay() {
return getChoiceOfPlayer1() +
" vs. " + getChoiceOfPlayer2() +
": " + getResult();
}
double getPlayer1Score() {
return (getRoundsPlayed() + getNetScore())/2;
}
double getPlayer2Score() {
return (getRoundsPlayed() - getNetScore())/2;
}
String getScoreBoard() {
return "Player 1: " + getPlayer1Score() +
", Player 2: " + getPlayer2Score();
}
}
function RPSMatch(player1, player2) {
Object.freeze(this.CONST);
var state = {
indexOfChoiceOfPlayer1: 0,
indexOfChoiceOfPlayer2: 0,
netScore: 0,
roundsPlayed: 0
}
function getIndexOfNextChoiceOf(player) {
if (player === undefined) {
throw new Error("player is undefined");
}
if (player.hasOwnProperty("getIndex")) {
return player.getIndex();
}
if (player.hasOwnProperty("getChoice")) {
return this.getIndexOf(player.getChoice());
}
throw new Error("player can't play");
}
this.setPlayer1 = function(player) {
player1 = player;
}
this.setPlayer2 = function(player) {
player2 = player;
}
this.setChoiceOfPlayer1 = function(choice) {
this.setIndexOfChoiceOfPlayer1(this.getIndexOf(choice));
}
this.setChoiceOfPlayer2 = function(choice) {
this.setIndexOfChoiceOfPlayer2(this.getIndexOf(choice));
}
this.setIndexOfChoiceOfPlayer1 = function(i) {
if (-1 < i && i < 3) {
state.indexOfChoiceOfPlayer1 = i;
}
}
this.setIndexOfChoiceOfPlayer2 = function(i) {
if (-1 < i && i < 3) {
state.indexOfChoiceOfPlayer2 = i;
}
}
this.getIndexOfChoiceOfPlayer1 = function() {
return state.indexOfChoiceOfPlayer1;
}
this.getIndexOfChoiceOfPlayer2 = function() {
return state.indexOfChoiceOfPlayer2;
}
this.getNetScore = function() {
return state.netScore;
}
this.getRoundsPlayed = function() {
return state.roundsPlayed;
}
this.makePlayersChoose = function() {
var i1 = getIndexOfNextChoiceOf(player1);
var i2 = getIndexOfNextChoiceOf(player2);
this.setIndexOfChoiceOfPlayer1(i1);
this.setIndexOfChoiceOfPlayer2(i2);
}
this.revealChoices = function() {
state.netScore += this.getOutcome();
state.roundsPlayed++;
}
this.playAgain = function() {
this.makePlayersChoose();
this.revealChoices();
}
}
RPSMatch.prototype = {
CONST: {
PLAYERS_TIE: 0,
PLAYER1_WINS: 1,
PLAYER2_WINS: -1,
CHOICES: function() {
return ["rock", "paper", "scissors"];
}
},
getIndexOf: function(choice) {
return this.CONST.CHOICES().indexOf(choice);
},
getChoiceAt(index) {
return index >= 0 && index <= 2 ?
this.CONST.CHOICES()[index] : "bad choice";
},
getChoiceOfPlayer1: function() {
return this.getChoiceAt(this.getIndexOfChoiceOfPlayer1());
},
getChoiceOfPlayer2: function() {
return this.getChoiceAt(this.getIndexOfChoiceOfPlayer2());
},
getOutcome: function() {
var i1 = this.getIndexOfChoiceOfPlayer1(),
i2 = this.getIndexOfChoiceOfPlayer2();
var numChoices = this.CONST.CHOICES().length;
var beatsPlayer2 = (i2 + 1) % numChoices;
return (i1 === i2) ? this.CONST.PLAYERS_TIE :
(i1 === beatsPlayer2 || i2 === -1) ? this.CONST.PLAYER1_WINS :
this.CONST.PLAYER2_WINS;
},
getResult: function() {
var outcome = this.getOutcome();
return outcome === this.CONST.PLAYER1_WINS ? "player 1 wins" :
outcome === this.CONST.PLAYER2_WINS ? "player 2 wins" : "players tie";
},
getReplay: function() {
return this.getChoiceOfPlayer1() +
" vs. " + this.getChoiceOfPlayer2() +
": " + this.getResult();
},
getPlayer1Score: function() {
return (this.getRoundsPlayed() + this.getNetScore())/2;
},
getPlayer2Score: function() {
return (this.getRoundsPlayed() - this.getNetScore())/2;
},
getScoreBoard: function() {
return "Player 1: " + this.getPlayer1Score() +
", Player 2: " + this.getPlayer2Score();
}
}
interface CanGetIndex {
int getIndex();
}
interface CanGetChoice {
String getChoice();
}
class RPSCompetitor {
public RPSMatch match;
void onGameOver() {
}
int getIndex() {
throw new Error("Not implemented");
}
String getChoice() {
throw new Error("Not implemented");
}
}
class RoverRandom extends RPSCompetitor implements CanGetIndex {
public int getIndex() {
return Utility.randomInt(0, 2);
}
}
class CyclePath extends RPSCompetitor implements CanGetIndex {
int lastChoice = Utility.randomInt(0, 2);
public int getIndex() {
int thisChoice = (lastChoice + 1) % 3;
lastChoice = thisChoice;
return thisChoice;
}
}
class JohnnyOneNote extends RPSCompetitor implements CanGetIndex {
int i = Utility.randomInt(0, 2);
public int getIndex() {
return i;
}
}
function RPSCompetitor() {
this.match = undefined;
this.onGameOver = function() {
}
}
function RoverRandom() {
RPSCompetitor.call(this);
this.getIndex = function() {
return Utility.randomInt(0, 2);
}
}
function CyclePath() {
RPSCompetitor.call(this);
var lastChoice = Utility.randomInt(0, 2);
this.getIndex = function() {
var thisChoice = (lastChoice + 1) % 3;
lastChoice = thisChoice;
return thisChoice;
}
}
function JohnnyOneNote() {
RPSCompetitor.call(this);
var i = Utility.randomInt(0, 2);
this.getIndex = function() {
return i;
}
}
import java.util.*;
class Utility {
static int randomInt(int min, int max) {
Double r = 0.0;
return r.intValue();
}
static RPSCompetitor removeRandomElement(ArrayList<RPSCompetitor> array) {
int i = Utility.randomInt(0, array.size() - 1);
return array.remove(i);
}
}
class KingOfTheHill extends RPSCompetitor implements CanGetIndex {
ArrayList<Integer> opponentsMoves = new ArrayList<Integer>();
int myMove;
private boolean movesAreIdentical(ArrayList<Integer> moves) {
return false; // FIX
}
private boolean movesCycle(ArrayList<Integer> moves) {
return false; // FIX
}
public int getIndex() {
if (movesAreIdentical(opponentsMoves)) {
myMove = Utility.randomInt(0, 2); // YOU CAN DO BETTER
}
else if (movesCycle(opponentsMoves)) {
myMove = Utility.randomInt(0, 2); // YOU CAN DO BETTER
}
else {
myMove = Utility.randomInt(0, 2); // CAN YOU DO BETTER?
}
return myMove;
}
public void onGameOver() {
int nextIndex = opponentsMoves.size();
opponentsMoves.add(match.getIndexOfChoiceOfPlayer2());
if (opponentsMoves.size() > 10) {
opponentsMoves.remove(0);
}
}
}
Utility = {
randomInt: function(min, max) {
// HINT: see Math.random and Math.floor
return min; // FIX
},
removeRandomElement: function(array) {
var i = Utility.randomInt(0, array.length - 1);
return array.splice(i, 1)[0];
}
}
function KingOfTheHill() {
RPSCompetitor.call(this);
var opponentsMoves = [];
var myMove;
function movesAreIdentical(moves) {
return false; // FIX
}
function movesCycle(moves) {
return false; // FIX
}
this.getIndex = function() {
opponentsLastMove = opponentsMoves[opponentsMoves.length -1];
if (movesAreIdentical(opponentsMoves)) {
myMove = Utility.randomInt(0, 2); // YOU CAN DO BETTER
}
else if (movesCycle(opponentsMoves)) {
myMove = Utility.randomInt(0, 2); // YOU CAN DO BETTER
}
else {
myMove = Utility.randomInt(0, 2); // CAN YOU DO BETTER?
}
return myMove;
}
this.onGameOver = function() {
opponentsMoves.push(this.match.getIndexOfChoiceOfPlayer2());
if (opponentsMoves.length > 10) {
opponentsMoves.splice(0, 1);
}
}
}
public class RPSTournament {
public static ArrayList<RPSCompetitor> getCompetitors() {
RPSCompetitor c1 = new RoverRandom();
RPSCompetitor c2 = new CyclePath();
RPSCompetitor c3 = new JohnnyOneNote();
ArrayList<RPSCompetitor> competitors = new ArrayList<RPSCompetitor>();
competitors.add(c1);
competitors.add(c2);
competitors.add(c3);
ArrayList<RPSCompetitor> shuffled = new ArrayList<RPSCompetitor>();
while (competitors.size() > 0) {
shuffled.add(Utility.removeRandomElement(competitors));
}
return shuffled;
}
public static String[] compete(int winByCount) {
RPSMatch match;
long roundsPlayed;
ArrayList<RPSCompetitor> competitors = RPSTournament.getCompetitors();
RPSCompetitor player1;
RPSCompetitor player2;
double p1Score;
double p2Score;
String[] results = new String[3];
for (int i = 0; i < competitors.size(); i++) {
//System.out.println("***** NEW MATCH *****");
player1 = new KingOfTheHill();
player2 = competitors.get(i);
match = new RPSMatch(player1, player2);
player1.match = match;
player2.match = match;
for (int n=0; Math.abs(match.getNetScore()) < winByCount; n++) {
match.playAgain();
player1.onGameOver();
player2.onGameOver();
//System.out.println(match.getReplay());
if (n >= 1000000) {
throw new Error("Too many rounds!");
}
}
//System.out.println(match.getScoreBoard());
p1Score = match.getPlayer1Score();
p2Score = match.getPlayer2Score();
roundsPlayed = match.getRoundsPlayed();
if (p1Score > p2Score) {
results[i] = "Player 1 won in " + roundsPlayed + " rounds.";
}
else if (p2Score > p1Score) {
results[i] = "Player 2 won in " + roundsPlayed + " rounds.";
}
else {
results[i] = "Players tied in " + roundsPlayed + " rounds.";
}
}
return results;
}
public static void main(String[] args) {
try {
String output = "";
String message;
int winByCount = Integer.parseInt(args[0]);
System.out.println("MUST WIN BY " + winByCount);
String[] results = RPSTournament.compete(winByCount);
System.out.println("*** Final Results ***");
for (int i = 0; i < results.length; i++) {
message = "Match " + (i + 1) + ": " + results[i];
output += message + "\n";
}
System.out.println(output);
}
catch(Error error) {
System.out.println(error.getMessage());
}
}
}
RPSTournament = {
getCompetitors: function() {
var c1 = new RoverRandom();
var c2 = new CyclePath();
var c3 = new JohnnyOneNote();
var competitors = [c1, c2, c3];
var shuffled = [];
while (competitors.length > 0) {
shuffled.push(Utility.removeRandomElement(competitors));
}
return shuffled;
},
compete: function(winByCount) {
var match,
roundsPlayed,
competitors = RPSTournament.getCompetitors(),
player1,
player2,
p1Score,
p2Score,
results = [];
for (var i = 0; i < competitors.length; i++) {
console.log("***** NEW MATCH *****");
match = new RPSMatch();
player1 = new KingOfTheHill();
player2 = competitors[i];
match.setPlayer1(player1);
match.setPlayer2(player2);
player1.match = match;
player2.match = match;
for (var n=0; Math.abs(match.getNetScore()) < winByCount; n++) {
match.playAgain();
player1.onGameOver();
player2.onGameOver();
//console.log(match.getReplay());
if (n >= 1000000) {
throw Error("Too many rounds!");
}
}
console.log(match.getScoreBoard());
p1Score = match.getPlayer1Score();
p2Score = match.getPlayer2Score();
roundsPlayed = match.getRoundsPlayed();
if (p1Score > p2Score) {
results.push("Player 1 won in " + roundsPlayed + " rounds.");
}
else if (p2Score > p1Score) {
results.push("Player 2 won in " + roundsPlayed + " rounds.");
}
else {
results.push("Players tied in " + roundsPlayed + " rounds.");
}
}
return results;
},
main: function(args) {
try {
var output = "";
var message;
var winByCount = parseInt(args[0]);
console.log("MUST WIN BY " + winByCount);
var results = RPSTournament.compete(winByCount);
console.log("*** Final Results ***");
for (var i = 0; i < results.length; i++) {
message = "Match " + (i + 1) + ": " + results[i];
console.log(message);
output += message + "\n";
}
output += "\nSee console log for more details.";
alert(output);
}
catch(error) {
alert(error.message);
}
}
}