Billedale
Billedale

Reputation: 51

Where am I losing randomness?

So I'm building a rock, paper, scissors game. I want to get math.random() to give me a random result that I compare to the one chosen by the user.

It mostly works. I even thought it fully worked for a while, but I'm losing randomness at some point.

To try it, I input a fixed "user" choice and ran the code a few hundred times with setInterval(myfunction, 20). They're always lopsided victories, and always with the same outcome:

If I run it with playerPick = 1, the computer always wins.

If I run it with playerPick = 2 or 3, the user always wins.

Can anyone see where I goofed?

//Global variable and constants.

const ROCK = 1;
const PAPER = 2;
const SCISSORS = 3;

//This is the game.

function whoWins(){
    const playerPick = 2; //for debugging, it can be 1, 2, or 3.
    const computer = computerPick();
    if (playerPick == computer){
        return draw();
    } else if (playerPick == 1 && computer == 2){
        return lose();
    } else if (playerPick == 2 && computer == 3){
        return lose();
    } else if (playerPick == 3 && computer == 1){
        return lose();
    } else {
        return win();
    }
}

//These are the inputs for the game.

rockButton.addEventListener('click', () => {
    playerPick = ROCK;
    return whoWins()});
paperButton.addEventListener('click', () => {
    playerPick = PAPER;
    return whoWins()});
scissorsButton.addEventListener('click', () => {
    playerPick = SCISSORS;
    return whoWins()});

function computerPick() {
    let computerChoice = '';
    const getRandom = Math.random;
    if (getRandom() >= 2/3) {
        computerChoice = ROCK;
    } else if (getRandom() >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

I'm very new to all this, but still, this is not random.

Upvotes: 2

Views: 88

Answers (2)

vivalldi
vivalldi

Reputation: 521

For future reference, you might find it easier to work with an array.

const getRandomChoice = () => {
  const options = ['Rock', 'Paper', 'Scissors'];

  const randomIndex = Math.floor(Math.random() * options.length);

  const choice = options[randomIndex];
  return choice;
}

const counts = {};

for(let i = 0; i < 10000; i++) {
  let p = getRandomChoice();
  counts[p] = (counts[p] || 0) + 1;
}

console.log(counts);

By multiplying the result of Math.random by the length of the array (3) we can get a value that is between [0, 3) (not inclusive of 3). We then call Math.floor to "chop" off any decimals.

Upvotes: 1

Terry Lennox
Terry Lennox

Reputation: 30685

This should be a simple fix, as mentioned in the comments you need to call Math.random once, otherwise the probabilities are skewed.

I think the probabilities of a PAPER would have been 0.66 * 0.66 = ~ 44% with the original code, the probability of SCISSORS would have been 0.66 * 0.33 = ~ 22%. The new function should resolve this.

const ROCK = 1;
const PAPER = 2;
const SCISSORS = 3;

// Original computerPick function
function computerPickOriginal() {
    let computerChoice = '';
    const getRandom = Math.random;
    if (getRandom() >= 2/3) {
        computerChoice = ROCK;
    } else if (getRandom() >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

// Fixed computerPick function.
function computerPick() {
    let computerChoice = '';
    const choice = Math.random();
    if (choice >= 2/3) {
        computerChoice = ROCK;
    } else if (choice >= 1/3){
        computerChoice = PAPER;
    } else {
        computerChoice = SCISSORS;
    }
    return computerChoice;
}

function decodeChoice(choice) {
    if (choice == ROCK) return "Rock";
    if (choice == PAPER) return "Paper";
    if (choice == SCISSORS) return "Scissors";
}

// Check the distribution of each version of the code.
console.log("Checking distributions (10000 picks)..");
let original_counts = {};
let counts = {};
for(let i = 0; i < 10000; i++) {
  let k = computerPick();
  counts[k] = (counts[k] || 0) + 1;
  let k2 = computerPickOriginal();
  original_counts[k2] = (original_counts[k2] || 0) + 1;
}

console.log('Computer Pick Distribution (original): ', Object.entries(original_counts).map(([key,value]) => `${decodeChoice(key)}: ${value}`));
console.log('Computer Pick Distribution (fixed): ', Object.entries(counts).map(([key,value]) => `${decodeChoice(key)}: ${value}`));

Upvotes: 2

Related Questions