Syncing a random variable - javascript

I am currently making simple UI for my rock paper scissors game. I am using a random number for my getComputerChoice function which then is used to display an image and decide the outcome of the game. I tried setting the outcome of getComputerChoice to a variable but then I ran into the issue that the computer's choice would remain the same each time I ran a new game.
const model = {
wins: 0,
losses: 0,
draws: 0,
getCompChoice: function() {
var num = Math.floor(Math.random() * 3);
var choices = ["rock", "paper", "scissors"];
var choice = choices[num];
return choice;
},
gameLogic: function(player, computer) {
if (player === false) {
return alert("Please enter a valid guess");
} else if (player === computer) {
this.draws++;
return "draw";
} else if (
(player === "rock" && computer === "paper") ||
(player === "paper" && computer === "scissors") ||
(player === "scissors" && computer === "rock")
) {
this.losses++;
return "lose";
} else {
this.wins++;
return "win";
}
}
};
const view = {
updateScore: function() {
document.getElementById("wins").textContent = "Wins:" + " " + model.wins;
document.getElementById("losses").textContent =
"Losses:" + " " + model.losses;
document.getElementById("draws").textContent = "Draws:" + " " + model.draws;
return;
},
displayRollComputer: function() {
var computer = document.getElementById("computer");
computer.classList.remove("rock", "paper", "scissors");
computer.classList.add(controller.compChoice);
console.log(controller.compChoice);
},
displayRollUser: function() {
var user = document.getElementById("user");
user.classList.remove("rock", "paper", "scissors");
user.classList.add(processPlayer());
}
};
const controller = {
compChoice: model.getCompChoice(),
play: function() {
model.gameLogic(processPlayer(), this.compChoice);
view.updateScore();
view.displayRollComputer();
view.displayRollUser();
}
};
// Helper Functions
function processPlayer() {
const player = document.getElementById("guess").value;
if (player === "rock" || player === "paper" || player === "scissors") {
return player;
} else {
return false;
}
}

That's because the controller.compChoice is not computed every time you call gameLogic(), but only once, during initialization. Change it to:
const controller = {
compChoice: model.getCompChoice(),
play: function() {
this.compChoice = model.getCompChoice(); <
model.gameLogic(processPlayer(), this.compChoice);
view.updateScore();
view.displayRollComputer();
view.displayRollUser();
}
};
and everything should work as expected.

Related

Why is this loop executing without me clicking any button?

I am trying to build a rock paper scissors game and I’m having a hard time with loops and events in JS. When I run this script, it counts userChoice as undefined. Why is the loop not waiting till I click any button?
This is part of the oddin project: Revisiting Rock Paper Scissors - Foundations Course
// Setting the game score to 0.
let userScore = 0;
let computerScore = 0;
const rock = document.querySelector('.rock');
const paper = document.querySelector('.paper');
const scissors = document.querySelector('.scissors');
// Main game function.
function game() {
for (let i = 0; i < 5; i++) {
/* Play the game 5 rounds. */
let values = ["rock", "paper", "scissors"]; /* The possibilities the computer can choose. */
let index = Math.floor(Math.random() * values.length); /* I use the random built-in function to randomly pick a value from the array. */
function getComputerChoice() { /* Function for the computer choice. */
return values[index];
}
let computerChoice = getComputerChoice();
let userChoice
rock.addEventListener("click", function() {
userChoice = 'rock';
});
paper.addEventListener("click", function() {
userChoice = 'paper';
});
scissors.addEventListener("click", function() {
userChoice = 'scissors';
});
function roundOfGame(userChoice, computerChoice) {
if (userChoice === computerChoice) {
return ("It is a tie");
} else if ((userChoice === "rock" && computerChoice === "scissor") || (userChoice === "paper" && computerChoice == "rock") || (userChoice === "scissor" && computerChoice === "paper")) {
userScore = userScore += 1;
return (`Player wins! ${userChoice} beats ${computerChoice}. User score = ${userScore} and computer score = ${computerScore}`)
} else {
computerScore = computerScore += 1;
return (`Computer wins! ${computerChoice} beats ${userChoice}. User score = ${userScore} and computer score = ${computerScore}`)
}
}
console.log(roundOfGame(userChoice, computerChoice));
}
}
game()
It's not how you usually handle user events in JavaScript. addEventListener takes a callback instead of returning for a reason. It's a non-blocking operation and by default everything after addEventListener will run immediately
To make it work as you want you can create a function like this:
function waitForClick(options) {
return new Promise(r => {
const listeners = []
options.forEach(option => {
const waitFor = () => {
r(option.value)
listeners.forEach(listener => {
listener.element.removeEventListener('click', listener.fn)
})
}
option.element.addEventListener('click', waitFor)
listeners.push({ element: option.element, fn: waitFor })
})
})
}
and than await for it:
const userChoice = await waitForClick([{
element: rock,
value: 'rock'
},
{
element: paper,
value: 'paper'
},
{
element: scissors,
value: 'scissors'
},
])
let userScore = 0;
let computerScore = 0;
const rock = document.querySelector('.rock');
const paper = document.querySelector('.paper');
const scissors = document.querySelector('.scissors');
async function game() {
for (let i = 0; i < 5; i++) {
let values = ["rock", "paper", "scissors"];
let index = Math.floor(Math.random() * values.length);
function getComputerChoice() {
return values[index];
}
let computerChoice = getComputerChoice();
const userChoice = await waitForClick([{
element: rock,
value: 'rock'
},
{
element: paper,
value: 'paper'
},
{
element: scissors,
value: 'scissors'
},
])
console.log('player', userChoice, 'computer', computerChoice)
function roundOfGame(userChoice, computerChoice) {
if (userChoice === computerChoice) {
return ("It is a tie");
} else if ((userChoice === "rock" && computerChoice === "scissors") || (userChoice === "paper" && computerChoice == "rock") || (userChoice === "scissors" && computerChoice === "paper")) {
userScore = userScore += 1;
return (`Player wins! ${userChoice} beats ${computerChoice}. User score = ${userScore} and computer score = ${computerScore}`)
} else {
computerScore = computerScore += 1;
return (`Computer wins! ${computerChoice} beats ${userChoice}. User score = ${userScore} and computer score = ${computerScore}`)
}
}
console.log(roundOfGame(userChoice, computerChoice));
}
}
game()
function waitForClick(options) {
return new Promise(r => {
const listeners = []
options.forEach(option => {
const waitFor = () => {
r(option.value)
listeners.forEach(listener => {
listener.element.removeEventListener('click', listener.fn)
})
}
option.element.addEventListener('click', waitFor)
listeners.push({
element: option.element,
fn: waitFor
})
})
})
}
<button class="rock">rock</button>
<button class="paper">paper</button>
<button class="scissors">scissors</button>

What is wrong with my JavaScript assignment

I am practicing the rock-paper-scissors assignment.
When I call playRound function manually it returns normal (e.g tie true false tie tie).
But when I call playRound function with iteration through game function it returns (true true true true) or (false false false false false) or (tie tie tie tie tie), instead of something like (false tie false true tie). I used the if-else clause and now I used switch yet still the same output. please how will I solve the issue?
function getComputerPlay() {
let selectAny = ["paper", "rock", "scissors"];
let randomReturn = Math.floor(Math.random() * selectAny.length);
return selectAny[randomReturn];
}
function getUserSelect(choice) {
let userSelect = choice.toLowerCase();
if (userSelect === "rock" || userSelect === "paper" || userSelect === "scissors") return userSelect;
return "Not valid";
}
function playRound(playSelection, computerSelection) {
if (playSelection === computerSelection) return "Tie!.";
if (playSelection === "paper") {
if (computerSelection === "rock") {
return true;
}
else {
return false;
}
}
if (playSelection === "rock") {
if (computerSelection === "scissors") {
return true;
}
else {
return false;
}
}
if (playSelection === "scissors") {
if (computerSelection === "rock")
return false;
} else {
return true;
}
}
function game(playSelection, computerSelection) {
let win = 0;
let lost = 0;
let tie = 0;
for (let i = 0; i < 5; i++) {
let playRoundReturn = playRound(playSelection, computerSelection);
switch (playRoundReturn) {
case true:
win++;
break;
case false:
lost++;
break;
default:
tie++;
break;
}
}
console.log(win, lost, tie);
return "You won " + win + "times, lost " + lost + "times and draw " + tie + "time";
}
let playSelection = getUserSelect("rock");
let computerSelection = getComputerPlay();
let play = game(playSelection, computerSelection); //called playRound with iteration through game()
//let pl = playRound(playSelection, computerSelection); //called playRound manually
console.log(play);
This is because the playSelection is always 'rock', and the computerSelection only runs the random gen once, so will always be the same value for all 5 rounds.
Move the random generator into the for loop, e.g.
let playRoundReturn = playRound(playSelection, getComputerPlay());

Rock Paper Scissors game with JavaScript

I have recently got into coding with JavaScript and decided to make a game, to test my knowledge. Nothing happens when I press on the objects supposed to start the game, and when I send information through the console, (most of the time) nothing happens.
const paper = document.getElementById('paper');
const scissor = document.getElementById('scissor');
const result_in = document.getElementById("result")
let computer;
let computer_pick;
let result;
//Player choice
rock.onclick = play('rock');
paper.onclick = play('paper');
scissor.onclick = play('scissor');
function play(userinput) {
computer_pick = Math.floor(Math.random() * 3);
console.log(computer_pick);
if (computer_pick === 0) {
computer = 'rock'
} else if (computer_pick === 1) {
computer = 'paper';
} else if (computer_pick === 2) {
computer = 'scissor';
} else { console.log('error') };
console.log(computer);
//
if (computer == userinput) { //tie
result = 'tie';
} else if (computer == 'rock' && userinput == 'paper' || computer == 'paper' && userinput == 'scissor' || computer == 'scissor' && userinput == "rock") {
console.log(win);
result = 'win';
} else if (computer == 'rock' && userinput == 'scissor' || computer == 'paper' && userinput == 'scissor' || computer == 'scissor' && userinput == 'paper') {
console.log(loss);
result = 'lost';
}
//output
document.getElementById('result').innerHTML = You ${result}! The computer threw ${computer}.;
}
Are you waiting until the DOM is loaded?
Where are you inject this file to DOM? in head tag or body tag!
If you inject this code in head tag you need to wait until the DOM become loaded
something like this:
window.onload = function() {
// Your script
}
There are some errors in your code:
rock.onclick is not correct - rock.addEventlistener('click', function(e) {}) is correct
console.log(win) (or loss) is not correct - you try to console.log() a variable that doesn't exist - to output a string in console.log() you should put it in quotes console.log('win')
document.getElementById('result').innerHTML = You ${result}! The computer threw ${computer}.; is not correct - you should use backticks for string interpolation
You didn't define rock as you did paper and scissor
This is not an coding error, but a simple logic problem: you have three result ALTERNATIVES: tie, win, lost. If it's not a tie and user hasn't win (won), then user lost. You don't need the last else if, only else
The same is true for the computer_pick variable - there's no room for error (the random value can only be 0, 1 or 2), so you don't need the else for error. And if computer_pick is not 0 or 1, then it has to be 2 (no need for the else if, only for else).
const rock = document.getElementById('rock');
const paper = document.getElementById('paper');
const scissor = document.getElementById('scissor');
const result_in = document.getElementById("result");
let computer;
let computer_pick;
let result;
//Player choice
rock.addEventListener('click', function(e) {
play('rock')
})
paper.addEventListener('click', function(e) {
play('paper')
})
scissor.addEventListener('click', function(e) {
play('scissor')
})
function play(userinput) {
computer_pick = Math.floor(Math.random() * 3);
console.log('computer_pick:', computer_pick);
if (computer_pick === 0) {
computer = 'rock'
} else if (computer_pick === 1) {
computer = 'paper';
} else {
computer = 'scissor';
}
console.log('computer:', computer);
//
if (computer == userinput) { //tie
result = 'tie';
} else if (computer == 'rock' && userinput == 'paper' || computer == 'paper' && userinput == 'scissor' || computer == 'scissor' && userinput == "rock") {
console.log('win');
result = 'win';
} else {
console.log('lost');
result = 'lost';
}
//output
document.getElementById('result').innerHTML = `You ${result}! The computer threw ${computer}.`;
}
<div id="rock">ROCK</div><br />
<div id="paper">PAPER</div><br />
<div id="scissor">SCISSORS</div><br />
<div>RESULT: <span id="result"></span></div>
And you could go a bit further by thinking through the logic:
// you can use a query selector with a class
const btns = document.querySelectorAll('.btn')
// gameRulesObj to define what beats what
const gameRulesObj = {
"rock": "paper",
"paper": "scissor",
"scissor": "rock"
}
btns.forEach(e => {
e.addEventListener('click', function(e) {
appendToDOMElement('result', play(this.getAttribute('id'), computerPick(gameRulesObj), gameRulesObj))
})
})
// this function decides if player wins, loses or ties
function play(userinput, computer, obj) {
let result;
if (computer === userinput) {
result = 'tie';
} else if (obj[computer] === userinput) {
result = 'win';
} else {
result = 'lost';
}
return {
result,
computer
};
}
// this function controls what the computer picks
const computerPick = (obj) => {
return Object.keys(obj)[Math.floor(Math.random() * Object.keys(obj).length)]
}
// this function adds the result to the DOM
const appendToDOMElement = (container, {
result,
computer
}) => {
document.getElementById(container).textContent = `You ${result}! The computer threw ${computer}.`
}
<div id="rock" class="btn">ROCK</div><br />
<div id="paper" class="btn">PAPER</div><br />
<div id="scissor" class="btn">SCISSORS</div><br />
<div>RESULT: <span id="result"></span></div>
The second snippet above gives a bit of a structure to the code:
every function has one and only one purpose (they can be described with one line of comment); this means that it's easier to change parts of your app
a lot of variables are eliminated, so you don't have to keep track of them
one variable added (gameRulesObj), so you can define your base rules in one place; the functions now work with any number and set of rules
I know that this structure may be overkill for a simple game like this, but it's good for practicing :)
try < button onClick=play('rock')> and so on
and you need " " for the line document.getElementById('result').innerHTML = " "

Testing if the user input value is in a given array (JavaScript)

I just created a five rounds rock-paper-scissors game using vanilla JavaScript. The program runs just fine so far except for the fact every time I start the game for the very first time it will take any user input as invalid no matter what and won't count that round.
This is my code:
// Global variables
let playerWins = 0;
let computerWins = 0;
let array = [];
let validInput = 0;
let newRound = "";
// This function generates a computer selection
const computerPlay = () => {
array = ["rock", "paper", "scissors"]
return array[Math.floor(Math.random() * array.length)];
}
// This function stores player selection
const playerSelection = (selection) => {
selection = prompt("Enter: 'Rock', 'Paper' or 'Scissors'").toLowerCase();
validInput = array.indexOf(selection);
console.log(validInput);
// This loop will validate user input is correct
while (validInput === -1) {
alert("Invalid input, try again");
selection = prompt("Enter 'Rock', 'Paper' or 'Scissors'").toLowerCase();
validInput = array.includes(selection);
}
return selection;
}
// This function plays a single round of Rock-Paper-Scissors
const playRound = (playerSelection, computerPlay) => {
// If both players select the same item
if (playerSelection === computerPlay) {
return alert("It's a tie!");
}
// If player selects "Rock"
if (playerSelection === "rock") {
if (computerPlay === "scissors") {
playerWins += 1;
return alert("Rock crushes scissors: YOU WIN!!!");
} else {
computerWins += 1;
return alert("Paper covers rock: YOU LOOSE!!!");
}
}
// If player selects "Paper"
if (playerSelection === "paper") {
if (computerPlay === "rock") {
playerWins += 1;
return alert("Paper covers rock: YOU WIN!!!");
} else {
computerWins += 1;
return alert("Scissors cuts paper: YOU LOOSE!!!");
}
}
// If player selects "Scissors"
if (playerSelection === "scissors") {
if (computerPlay === "rock") {
computerWins += 1;
return alert("Rock crushes scissors: YOU LOOSE!!!");
} else {
playerWins += 1;
return alert("Scissors cuts paper: YOU WIN!!!");
}
}
}
// This function keeps score and reports a winner or loser at the end
const trackWins = (pw, cw) => {
alert("COMPUTER WINS: " + cw + "\nPLAYER WINS: " + pw)
if (pw > cw) {
alert("YOU WIN THIS ROUND, CONGRAX!!!")
} else if (cw > pw) {
alert("YOU LOOSE THIS ROUND, SO BEST LUCK FOR THE NEXT TIME :_(")
} else {
alert("IT'S A TIE")
}
}
// This function creates a 5 round game
const game = () => {
for (let i = 0; i < 5; i++) {
playRound(playerSelection(), computerPlay());
}
trackWins(playerWins, computerWins);
}
do {
game();
newRound = prompt("Do yo want to play another round? Type 'y' to continue or any other key to exit").toLowerCase();
} while (newRound === "y");
alert("It was a good game, bye for now!")
I will appreciate any ideas to fix this problem or improve my script, thank you in advance!
Your posted code can be simplified to better reflect question - say, you have an array, and a variable that stores user input. How do you test if the input value is in the array?
var arr=['Rock','Paper','Scissors'];
var inp='Rock'; //user input
You could use a while loop, but there's a much faster way:
var options={'rock':0,'paper':1,'scissors':2}
var inp='Rock'; //user input
var ninp=inp.toLowerCase().trim(); //normalize input
var pick=(options[ninp]);
if (pick==null) // invalid selection
if (pick==0) //rock
if (pick==1) //paper
if (pick==2) //scissors
The code can be further cleaned up with a switch:
switch (pick){
case 0: ... break; //rock
case 1: ... break; //paper
case 2: ... break; //scissors
default: //invalid
}

In this simple code, why is the prompt value being ignored inside the function?

In the follow code:
I have tried passing whatThrow an argument as well. I don't seem to spot what is wrong.
var ranThrow = ["empty", "rock", "paper", "scissors"];
var ranNum = Math.random();
var postRanNum = (ranNum * 3) + 1;
var roundPostRanNum = Math.floor(postRanNum);
var compThrow = ranThrow[roundPostRanNum];
var whatThrow = prompt("Rock, Papper or Scissors?", "rock");
var rpsGame = function () {
if (whatThrow === "rock" && compThrow === "rock") {
return "You tie!";
}
else if (whatThrow === "rock" && compThrow === "paper") {
return "You lose!";
}
else if (whatThrow === "rock" && compThrow === "scissor") {
return "You win!";
}
else {
return "Error";
}
};
rpsGame();
console.log("The computer threw" + " " + compThrow);
The value isn't ignored inside the function, it's the code that is calling the function that is ignoring what the function returns.
If you show the return value, you will see that the function uses the value:
console.log(rpsGame());

Categories