Rock Paper Scissors Javascript Game Add Choice Limit - javascript

I've created a Javascript-based Rock Paper Scissors game and it's pretty good so far. However, I want to create an option for "best out of 3" or "best out of 5". Please could some of you awesome JS people have a look at my code and see how I could implement the best of "x" scenario. Basically, I guess it comes down to the number of clicks.
Anyway, the code speaks for itself and here is a live example of the code:
Rock Paper Scissors Live Github Example
And the code:
(function(){
/*
* Rock, paper, scissors
*
* The classic game recreated in Javascript for playing in the browser.
*
*/
// create the choices
var choices = [
'rock',
'paper',
'scissors'
];
var CHOICES_LENGTH = choices.length;
// create the text for winning or drawing
var USER_WINS = "You win!";
var COMP_WINS = "Computer wins";
var DRAW = "Draw"
var MEH = '<i class="fa fa-meh-o" aria-hidden="true"></i>';
var SMILE = '<i class="fa fa-smile-o" aria-hidden="true"></i>';
var FROWN = '<i class="fa fa-frown-o" aria-hidden="true"></i>';
var score = 0;
var computer_score = 0;
var gameType;
var clicks = 0;
// score elements
var userScore = getById('score');
var compScore = getById('computerScore');
userScore.textContent = score;
compScore.textContent = computer_score;
// get the game area and get access to all the buttons
var game = getById('game');
var userChoices = game.getElementsByTagName('button');
var comp = getById('computer');
var compChoices = comp.getElementsByTagName('div');
// get the results element and hide it initially
var results = getById('results');
hide(results);
var gameOver = getById('gameOver');
hide(gameOver);
// get the intro element and the buttons for choosing a game type
var intro = getById('intro');
var bestOf3 = getById('bestOf3');
var bestOf5 = getById('bestOf5');
// start the best of 3 game
bestOf3.onclick = function() {
enableGame();
gameType = 3;
}
bestOf5.onclick = function() {
enableGame();
gameType = 5;
}
function enableGame() {
enable(userChoices);
hide(intro);
}
// add an onclick event to each button and disable them initially
for(var i = 0; i < userChoices.length; i++) {
userChoices[i].onclick = selection;
userChoices[i].disabled = true;
}
function computerSelection() {
var randomIndex = Math.floor(Math.random() * CHOICES_LENGTH);
var compChoice = choices[randomIndex];
return compChoice;
}
function selection() {
// get the user and computer choice
var chosen = this.id;
var comp = computerSelection();
// get the users chosen item
var chosenItem = getById(chosen);
// prepare the chosenCompItem so we can assign it to a dynamic id
var chosenCompItem;
if(comp === 'rock') {
chosenCompItem = getById('computerRock');
}
else if(comp === 'paper') {
chosenCompItem = getById('computerPaper');
}
else if(comp === 'scissors') {
chosenCompItem = getById('computerScissors');
}
// show results and disable all choices so no more can
// be made while waiting for the pop up to fade out
show(results);
reappear(results);
disable(userChoices);
disable(compChoices);
// make the selected item stand out from the rest
chosenItem.classList.add('selected');
chosenCompItem.classList.add('selected');
// decide who wins
if(chosen === comp) {
results.textContent = DRAW;
// ugly repetive code. what can I do???
timeout();
results.innerHTML += MEH;
}
else if(chosen === 'rock' && comp === 'scissors') {
results.textContent = USER_WINS;
score += 1;
userScore.textContent = score;
timeout();
results.innerHTML += SMILE;
}
else if(chosen === 'paper' && comp === 'rock') {
results.textContent = USER_WINS;
score += 1;
userScore.textContent = score;
timeout();
results.innerHTML += SMILE;
}
else if(chosen === 'scissors' && comp === 'paper') {
results.textContent = USER_WINS;
score += 1;
userScore.textContent = score;
timeout();
results.innerHTML += SMILE;
}
else {
results.textContent = COMP_WINS;
computer_score +=1;
compScore.textContent = computer_score;
timeout();
results.innerHTML += FROWN;
}
console.log(clicks);
}
// utilities
function getById(id) {
return document.getElementById(id);
}
function hide(element) {
element.style.display = 'none';
}
function show(element) {
element.style.display = 'block';
}
function disappear(element) {
element.className = 'disappear';
}
function reappear(element) {
element.className = 'reappear';
}
function disable(elements) {
for(var i = 0; i < elements.length; i++) {
elements[i].disabled = true;
elements[i].classList.add('unselected');
}
}
function enable(elements) {
for(var i = 0; i < elements.length; i++) {
elements[i].disabled = false;
elements[i].classList.add('default');
elements[i].classList.remove('selected', 'unselected');
}
}
function timeout() {
setTimeout(function(){
disappear(results);
enable(userChoices);
enable(compChoices);
}, 2000)
}
})();
The code has massive room for improvement but the main thing here is, how do I make it so the user gets only 3 or 5 plays??
Hope this all makes sense.
Thanks

Assuming your definition of best of "N" is the same as mine, my code should work.
My definition of "best of N" is:
To win, one person must win N/2 rounds, with N/2 being rounded up to the nearest whole number.
You could use this code:
if(userScore + compScore === gameType) { gameOver(); }
And use a simple if statement to see who won.
Hope this helped!

Related

How to design/redesign a JavaScript function to end and reset the rock paper scissors game after 9 rounds? The current function not working correctly

The following code is the totality of a version of paper rock scissors. The function: limitGameToBestOutOfNine () is only working some of the time. There are instances where the user or computer score increments above 9 wins. How do I fix this? Thanks in advance.
Code:
/*variable statements follow*/
const computerChoiceDisplay = document.getElementById('computer-choice');
const buttons = document.getElementsByClassName('control');
const userChoiceDisplay = document.getElementById('player-choice');
const resultDisplay = document.getElementById('result');
const possibleChoices = document.getElementsByClassName('control');
const resultOutput = document.getElementById('result-output');
const playerImage = document.getElementById('player-image');
const computerImage = document.getElementById('computer-image');
const choices = ["rock", "paper", "scissors"];
let userChoice;
let computerChoice;
let result ;
let playerChoice ;
let score = 0;
let mistakes = 0;
let scoreContainer;
let userSpan;
let computerSpan;
let playerScore = document.getElementById("userScore");
let compScore = document.getElementById("compScore");
let resetButton = document.getElementById("resetScore");
let compChoice;
let completedRounds = 0;
/**
* This Array from statement sets event listener to the button class control array
* which listens for the user and computer choices which are fed to the getresult function
*/
Array.from(possibleChoices).forEach(possibleChoice => possibleChoice.addEventListener('click', (e) => {
userChoice = e.target.id;
resultDisplay.innerHTML = userChoice;
compChoice = generateComputerChoice();
gameImages(userChoice, computerChoice);
getResult();
}));
/**
* This adds an event listener for the reset button
*/
document.getElementById("resetScore").addEventListener("click", resetScore);
/**
* This function generates a random number for the computer and displays
* the output to the innerHTML
*/
function generateComputerChoice() {
const randomNumber = Math.floor(Math.random() * 3) + 1;
if (randomNumber === 1) {
computerChoice = 'rock';
}
if (randomNumber === 2) {
computerChoice = 'paper';
}
if (randomNumber === 3) {
computerChoice = 'scissors';
}
computerChoiceDisplay.innerHTML = computerChoice;
}
/**
* Provides the logic to determin what to do in the event that either
* the user or computer wins, as well as what to do in the even of a draw.
*/
function getResult () {
if (computerChoice === userChoice) {
result = "It's a draw!";
}
if (computerChoice === 'rock' && userChoice === 'paper') {
result = "You Win!";
incrementUserScore();
console.log("paperWin");
}
if (computerChoice === 'rock' && userChoice === 'scissors') {
result = "You lost!";
incrementComputerScore();
}
if (computerChoice === 'paper' && userChoice === 'scissors') {
result = "You Win!";
incrementUserScore();
}
if (computerChoice === 'paper' && userChoice === 'rock') {
result = "You lost!";
incrementComputerScore();
}
if (computerChoice === 'scissors' && userChoice === 'rock') {
result = "You win!";
incrementUserScore();
}
if (computerChoice === 'scissors' && userChoice === 'paper') {
result = "You lost!";
incrementComputerScore();
}
resultOutput.innerHTML = result;
toggleBackgroundColor()
}
/**
* This function allows for the dynamic change of images based on user or
* computer choices.
*/
function gameImages(playerChoice, computerChoice) {
console.log(playerChoice, computerChoice);
playerImage.src = `assets/images/${playerChoice}.jpg`;
playerImage.alt = choices [userChoice];
computerImage.src = `assets/images/${computerChoice}.jpg`;
computerImage.alt = choices[computerChoice];
}
/**
* Gets the user score from the DOM and increments it by 1
*/
function incrementUserScore() {
console.log("incrementing");
// playerScore = playerScore++
score++;
playerScore.innerHTML = score;
console.log(playerScore);
completeRound()
}
/**
* Gets the computer score from the DOM and increments it by 1
*/
function incrementComputerScore() {
mistakes++;
compScore.innerHTML = mistakes;
console.log(compScore);
completeRound()
}
/**
* This function provides the logic used to reset the user and
* computer score back to zero, upon the user request.
*/
function resetScore() {
score = 0;
mistakes = 0;
playerScore.innerHTML = score;
compScore.innerHTML = mistakes;
}
/**
* This function is to limit the amount of playable paper, rock, and scissors game to best out of 9
*/
function limitGameToBestOutOfNine () {
// Check who has the higher score
if (score > mistakes) {
console.log('Player has won the game!');
alert('Player has won the game!');
} else if (mistakes > score) {
console.log('Computer has won the game!');
alert('Computer has won the game!');
} else {
console.log('It\'s a tie!');
alert('It\'s a tie!');
}
// Reset the scores
score = 0;
mistakes = 0;
}
function completeRound() {
// increment a completed round
completedRounds++;
console.log("Completed Rounds: " + completedRounds)
if (completedRounds == 10) {
limitGameToBestOutOfNine()
}
}
/***
* This function toggles the background color of the .player .computer class
* based on the winner of the current game. The winner color is green and the loser color is red
*/
function toggleBackgroundColor() {
const player = document.getElementById('player');
const computer = document.getElementById('computer');
const winner = resultOutput.innerHTML.toLowerCase();
console.log(winner)
if (winner.includes('you win')) {
console.log('win')
player.style.backgroundColor = "#00FF00";
computer.style.backgroundColor = "#FF0000";
}
else if (winner.includes('you lost')) {
console.log('lose')
player.style.backgroundColor = "#FF0000";
computer.style.backgroundColor = "#00FF00";
}
else {
console.log('draw')
player.style.backgroundColor = "#00FF00";
computer.style.backgroundColor = "#00FF00";
}
}
I have reviewed the code and do not know what the issue is. The problem started happening after I added the toggleBackgroundColor() function.

JS DOM: How to stop click event from firing?

I'm trying to get my buttons to stop working when a certain condition is met. For some reason, whenever I get to score 5, on either side, it just keeps going and doesn't even display the score and I don't know why. I've tried using a while loop but it kept crashing. Is there a simple way of just turning it off like there is in jQuery?
const rock = document.querySelector('.rock');
const paper = document.querySelector('.paper');
const scissors = document.querySelector('.scissors');
const h3 = document.querySelector('h3');
const pscore = document.querySelector('#pscore');
const cscore = document.querySelector('#cscore');
let computerScore = 0;
let playerScore = 0;
function computerPlay() {
var choice = Math.floor(Math.random() * 3 ) + 1; //generate a number 1-3 to find computer choice
if(choice == 1) {
return 'rock';
}
else if(choice == 2) {
return 'paper';
}
else {
return 'scissors'
}
}
let result; // simpler way of rewriting code?
rock.addEventListener('click', () => {
if(computerPlay() == 'rock') {
result = `The computer chose rock and you chose rock! It's a tie! No change in score.`;
h3.textContent = result;
}
else if(computerPlay() == 'paper') {
result = `The computer chose paper and you chose rock! You lose! Computer Score +1!`;
h3.textContent = result;
computerScore++;
cscore.textContent = computerScore;
}
else {
result = `The computer chose scissors and you chose rock! You win! Player Score +1!`;
h3.textContent = result;
playerScore++;
pscore.textContent = playerScore;
}
});
let playerPaper = paper.addEventListener('click', () => {
if(computerPlay() == 'paper') {
result = `The computer chose paper and you chose paper! It's a tie!`;
h3.textContent = result;
}
else if(computerPlay() == 'scissors') {
result = `The computer chose scissors and you chose paper! You lose!`;
h3.textContent = result;
computerScore++;
cscore.textContent = computerScore;
}
else {
result = `The computer chose rock and you chose paper! You win!`;
h3.textContent = result;
playerScore++;
pscore.textContent = playerScore;
}
});
let playerScissors = scissors.addEventListener('click', () => {
if(computerPlay() == 'scissors') {
result = `The computer chose scissors and you chose scissors! It's a tie!`;
h3.textContent = result;
}
else if(computerPlay() == 'rock') {
result = `The computer chose rock and you chose scissors! You lose!`;
h3.textContent = result;
computerScore++;
cscore.textContent = computerScore;
}
else {
result = `The computer chose paper and you chose scissors! You win!`;
h3.textContent = result;
playerScore++;
pscore.textContent = playerScore;
}
})
function playGame(computerChoice) {
computerChoice = computerPlay();
if(playerScore == 5) {
h3.textContent = `The score is 5 to ${computerScore}! You win!`;
}
else if(computerScore == 5) {
h3.textContent = `The score is 5 to ${playerScore}! The computer wins!`;
}
}
Everything works perfectly except for the 'end game' feature. Thanks in advance for all the help or critiques!
How to disable a "click" event
The people in the comments have some brilliant ideas on how you can improve your game. I would suggest looking into that. Although, if you want to prevent click events there are several ways to achieve that.
1. Add a disabled attribute to your button.
window.onload = () => {
const button = document.getElementById("myClicker")
button.disabled = true
}
<button id="myClicker">Click</button>
2. Add a CSS property pointer-events: none;
window.onload = () => {
const button = document.getElementById("myClicker")
button.addEventListener("click",function(){console.log("hi")})
button.style.pointerEvents = "none";
}
<button id="myClicker">Click</button>
The second method is for the situation when you don't need to indicate to the user that the button is no longer interactable.
3. Prevent Default event.
window.onload = () => {
const button = document.getElementById("myClicker")
button.addEventListener("click",function(e){
e.preventDefault;
})
}
<button id="myClicker">Click</button>
4. Remove Event listener.
document.getElementById("yourButtonId").removeEventListener("Click", yourFunctinoNameHere);
and welcome Charles. So, as #StackSlave mentioned, you keep querying computerPlay(), which will skew the results, probably favouring the last else (for each button respectively). This is a logic mistake - but the programme will still run.
You're trying to do quite a lot on the onclick events, and you're not paying attention to any kind of enable indicator. So.. here we go. I've seperated the programme into two pieces:
Round
Ui
The Round takes a user-choice, generates a computer-choice, and reports the score. The Ui attaches the buttons to choosing one of the options in the round, generating some output, updating scores and possibly ending the game. There's also a reset on the Ui which would allow you to start a new game ;) Note I've grouped things into objects (vs just functions) since each piece tracks a little state. I'd likely provide the constants at the top (rock..cscore) to the Ui constructor rather than use globals.
const rock = document.querySelector(".rock");
const paper = document.querySelector(".paper");
const scissors = document.querySelector(".scissors");
const h3 = document.querySelector("h3");
const pscore = document.querySelector("#pscore");
const cscore = document.querySelector("#cscore");
const NO_PICK = -1;
const PICK_ROCK = 0;
const PICK_PAPER = 1;
const PICK_SCISSORS = 2;
class Round {
constructor() {
this.player = NO_PICK;
this.computer = NO_PICK;
}
computerPlay() {
if (this.computer != NO_PICK) return; //Prevent double play - you might want to throw an exception instead?
this.computer = Math.floor(Math.random() * 3);
}
playAsString(number) {
switch (number) {
case PICK_ROCK:
return "rock";
case PICK_PAPER:
return "paper";
case PICK_SCISSORS:
return "scissors";
}
}
chooseRock() {
if (this.player !== NO_PICK) return; //Prevent double play
this.player = PICK_ROCK;
}
choosePaper() {
if (this.player !== NO_PICK) return; //Prevent double play
this.player = PICK_PAPER;
//return computerWinner(PICK_PAPER);
}
chooseScissors() {
if (this.player !== NO_PICK) return; //Prevent double play
this.player = PICK_SCISSORS;
//return computerWinner(PICK_SCISSORS);
}
reset() {
this.player = -1;
this.computer = -1;
}
//Return 0: tie, +1 player won, -1 computer won
playerScore() {
this.computerPlay();
if (this.player === NO_PICK) throw "Player hasn't played yet";
if (this.computer === this.player) return 0;
switch (this.computer) {
case PICK_ROCK:
return this.player === PICK_SCISSORS
? -1 //Rock beats scissors
: 1; //Paper beats rock
case PICK_SCISSORS:
return this.player === PICK_PAPER
? -1 //Scissors beat paper
: 1; //Rock beats scissors
case PICK_PAPER:
return this.player === PICK_ROCK
? -1 //Paper beats rock
: 1; //Scissors beat paper
}
}
winDescription(score) {
switch (score) {
case -1:
return "You lose! Computer score +1!";
case 0:
return "It's a tie! No change in score.";
case 1:
return "You win! Play score +1!";
}
}
gameDescription(score) {
return (
"The computer chose " +
this.playAsString(this.computer) +
" and you chose " +
this.playAsString(this.player) +
"! " +
this.winDescription(score)
);
}
}
class Ui {
constructor() {
this.playScore = 0;
this.compScore = 0;
this.enabled = true;
this.round = new Round();
rock.addEventListener("click", () => {
this.rockPress();
});
paper.addEventListener("click", () => {
this.paperPress();
});
scissors.addEventListener("click", () => {
this.scissorsPress();
});
//Bonus: you can have a reset button that calls this.reset
}
gameOver() {
this.enabled = false;
if (this.playScore > this.compScore) {
this.report("You win " + this.playScore + ":" + this.compScore + "!");
} else {
this.report("You lose " + this.playScore + ":" + this.compScore + "!");
}
}
sharedProcess() {
let gameScore = this.round.playerScore(); //Note this might throw if one of the press functions hasn't been called
this.report(this.round.gameDescription(gameScore));
if (gameScore < 0) {
this.compScore -= gameScore;
} else if (gameScore > 0) {
this.playScore += gameScore;
}
cscore.textContent = this.compScore;
pscore.textContent = this.playScore;
//Note this condition isn't exactly what you wrote so you may want to change, but you could do some sort of
// one has to be +2 over the other (win by two)
if (this.compScore >= 5 || this.playScore >= 5) {
this.gameOver();
}
this.round.reset(); //Setup for next game
}
rockPress() {
if (!this.enabled) return;
this.round.chooseRock();
this.sharedProcess();
}
paperPress() {
if (!this.enabled) return;
this.round.choosePaper();
this.sharedProcess();
}
scissorsPress() {
if (!this.enabled) return;
this.round.chooseScissors();
this.sharedProcess();
}
report(message) {
h3.textContent = message;
}
reset() {
this.playScore = 0;
this.compScore = 0;
this.enabled = true;
this.report("Game on!");
}
}
//Start the game - you don't need this ui variable
// unless you want to call one of the methods
const ui = new Ui();
I wouldn't disable anything. Just add and remove classes instead. Here is a very simple RockPaperScissors game. I put a small Library above // magic under here. Hopefully you can learn something here:
//<![CDATA[
/* js/external.js */
let get, post, doc, htm, bod, nav, M, I, mobile, S, Q, hC, aC, rC, tC, shuffle, rand, RockPaperScissors; // for use on other loads
addEventListener('load', ()=>{
get = (url, success, responseType = 'json', context = null)=>{
const x = new XMLHttpRequest;
const c = context || x;
x.open('GET', url); x.responseType = responseType;
x.onload = ()=>{
if(success)success.call(c, x.response);
}
x.send();
return x;
}
post = function(url, send, success, responseType ='json', context = null){
const x = new XMLHttpRequest;
const c = context || x;
x.open('POST', url); x.responseType = responseType;
x.onload = ()=>{
if(success)success.call(c, x.response);
}
if(typeof send === 'object' && send && !(send instanceof Array)){
if(send instanceof FormData){
x.send(send);
}
else{
const fd = new FormData;
let s;
for(let k in send){
s = send[k];
if(typeof s === 'object' && s)s = JSON.stringify(s);
fd.append(k, s);
}
x.send(fd);
}
}
else{
throw new Error('send argument must be an Object');
}
return x;
}
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
S = (selector, within)=>{
let w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
let w = within || doc;
return w.querySelectorAll(selector);
}
hC = function(node, className){
return node.classList.contains(className);
}
aC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.add(...a);
return aC;
}
rC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.remove(...a);
return rC;
}
tC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.toggle(...a);
return tC;
}
shuffle = array=>{
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
rand = (min, max)=>{
let mn = min, mx = max;
if(mx === undefined){
mx = mn; mn = 0;
}
return mn+Math.floor(Math.random()*(mx-mn+1));
}
RockPaperScissors = function(displayFunc){
const a = ['rock', 'paper', 'scissors'];
this.wins = this.losses = this.ties = 0; this.text;
this.play = (you)=>{
let c = a[rand(2)], r;
switch(you){
case 'rock':
switch(c){
case 'rock':
r = 'You and the Computer chose "rock". IT\'S A TIE!'; this.ties++;
break;
case 'paper':
r = 'You chose "rock". The Computer chose "paper". YOU LOSE!'; this.losses++;
break;
case 'scissors':
r = 'You chose "rock". The Computer chose "scissors". YOU WIN!'; this.wins++;
break;
}
break;
case 'paper':
switch(c){
case 'rock':
r = 'You chose "paper". The Computer chose "rock". YOU WIN!'; this.wins++;
break;
case 'paper':
r = 'You and the Computer chose "paper". IT\'S A TIE!'; this.ties++;
break;
case 'scissors':
r = 'You chose "paper". The Computer chose "scissors". YOU LOSE!'; this.losses++;
break;
}
break;
case 'scissors':
switch(c){
case 'rock':
r = 'You chose "scissors". The Computer chose "rock". YOU LOSE!'; this.losses++;
break;
case 'paper':
r = 'You chose "scissors". The Computer chose "paper". YOU WIN!'; this.wins++;
break;
case 'scissors':
r = 'You and the Computer chose "scissors". IT\'S A TIE!'; this.ties++;
break;
}
break;
}
this.text = r;
if(displayFunc)displayFunc(this);
return this;
}
this.reset = ()=>{
this.wins = this.losses = this.ties = 0;
if(displayFunc)displayFunc(this);
return this;
}
}
// magic under here
const game = I('game'), rock = I('rock'), paper = I('paper'), scissors = I('scissors');
const again = I('again'), res = I('res'), replay = I('replay'), reset = I('reset');
const you = I('you'), comp = I('comp'), ties = I('ties');
setTimeout(()=>{
rC(game, 'played');
}, 0);
const rps = new RockPaperScissors(t=>{
you.textContent = t.wins; comp.textContent = t.losses; ties.textContent = t.ties;
res.textContent = t.text; aC(game, 'played'); rC(again, 'played');
});
rock.onclick = paper.onclick = scissors.onclick = function(){
rps.play(this.id);
}
function playAgain(){
res.textContent = ''; aC(again, 'played'); rC(game, 'played');
}
replay.onclick = playAgain;
reset.onclick = function(){
rps.reset(); playAgain();
}
}); // end load
//]]>
*{
box-sizing:border-box; color:#000; font:bold 22px Tahoma, Geneva, sans-serif; padding:0; margin:0; overflow:hidden;
}
html,body,.main{
width:100%; height:100%;
}
.main{
background:#999; overflow-y:auto;
}
.bar{
width:100%; height:39px; border-bottom:1px solid #777; background:#ccc; padding:2px
}
h1{
display:inline-block; font-size:27px; margin-left:7px;
}
#score{
display:flex; justify-content:space-around;
}
#score>div{
color:#555;
}
#y>span{
color:#070;
}
#c>span{
color:#700;
}
#t>span{
color:#777;
}
#game{
display:flex; transition:margin-top 0.25s ease-in-out;
}
.played{
margin-top:-38px;
}
#again{
display:flex; flex-wrap:wrap; margin-top:0;
}
#again.played{
margin-top:-76px;
}
#res{
width:100vw; font-size:18px; padding:0 10px; text-align:center;
}
input[type=button]{
flex:1; height:38px; background:linear-gradient(#1b7bbb,#147); color:#fff; border:1px solid #007; border-radius:5px; cursor:pointer;
}
#paper,#replay{
background:linear-gradient(#679467,#235023); border-color:#070;
}
#scissors,#reset{
background:linear-gradient(#b75757,#502323); border-color:#700;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
<title>Rock, Paper, Scissors</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script src='js/external.js'></script>
</head>
<body>
<div class='bar'><h1>Rock, Paper, Scissors</h1></div>
<div id='score'>
<div id='y'>You: <span id='you'>0</span></div>
<div id='c'>Computer: <span id='comp'>0</span></div>
<div id='t'>Ties: <span id='ties'>0</span></div>
</div>
<div class='main'>
<div class='played' id='game'>
<input id='rock' type='button' value='rock' />
<input id='paper' type='button' value='paper' />
<input id='scissors' type='button' value='scissors' />
</div>
<div class='played' id='again'>
<input id='replay' type='button' value='replay' />
<input id='reset' type='button' value='reset' />
<div id='res'></div>
</div>
</div>
</body>
</html>

Rock Paper Javascript trouble

I have looked through quite a few of the posts regarding rock, paper, scissors, but I am still unable to find a clear solution. I am relatively new, just trying to fully understand what I'm doing.
I have gone back and forth through this with a few different approaches. Therefore, I fear that there are inconsistencies in the code structure.
I am mostly stuck right now with updating the scoreboard and trying to update playerChoice from the DOM. Any help would be much appreciated. Thank you all.
const player = {
currentChoice: null,
playerScore: 0
}
const computer = {
currentChoice: null,
computerScore: 0
}
const choices = ["Lapis", "Papyrus", "Scalpellus"];
player.currentChoice = choices[0]
function computerChooses() {
const randomIndex = Math.floor(Math.random() * choices.length);
computer.currentChoice = choices[randomIndex]
}
//using dot-method to access the array
function compareChoices() {
computerChooses();
if (computer.currentChoice === player.currentChoice) {
postResult("It's a tie! Both players chose " + player.currentChoice);
//conditionals computer against player
} else if (computer.currentChoice === choices[0]) {
if (player.currentChoice === choices[1]) {
postResult("The Player wins.");
player.playerScore += 1
} else {
postResult("The Computer wins.");
computer.computerScore += 1
}
} else if (computer.currentChoice === choices[1]) {
if (player.currentChoice === choices[2]) {
postResult("The Player Wins")
player.playerScore += 1
} else {
postResult("The Computer wins.");
computer.computerScore += 1
}
} else if (computer.currentChoice === choices[2]) {
if (player.currentChoice === choices[0]) {
postResult("The Player wins.");
player.playerScore += 1
} else {
postResult("The Computer wins.")
computer.computerScore += 1
}
}
}
function postResult(results) {
const resultText = document.createElement('p');
resultText.innerText = results;
document.body.appendChild(resultText);
}
compareChoices();
function weaponSelect() {
document.getElementById
}
document.getElementById('button').addEventListener('click', buttonSelect)
buttonSelect();
from your example, I took the liberty to change things a bit by creating a function to hold the core game, and then call it to play 10 times and show the result
the idea is to divide the use of the game from the game implementation as it should not be the game concern to count how many times a player won, the game should focus only on running the game itself, that is the most basic principle of programming together with DRY (Do not Repeat Yourself)...
I start with the implementation by using the javascript Classes that wraps all the game should do...
will hold the 3 variables
will be able to automatically choose an option
as well ask for one
and passing 2 options, will determine the winner
Then I started by using it to run 10 times and calculate the winner for each time... completely independent from the game it self.
I've commented the code, let me know if something's off...
// game
class RpsGame {
constructor() {
this.options = ["rock", "paper", "scissors"];
[this.ROCK, this.PAPER, this.SCISSORS] = this.options;
}
// will ask the player for a manual input
askPlayerSelection = () => {
var s = null;
while (s == null) {
s = prompt(`What do you want to play? press 1: ${ROCK} 2: ${PAPER} 3: ${SCISSORS}`);
try {
s = parseInt(s, 10); // convert to integer
if (!Number.isInteger(s) || s < 1 && s > 3) s = null; // has to be valid
}
catch(err) { s = null; } // if not valid, set as null so it asks again
}
return this.options[s-1];
}
// automatically assign an option
getAutoPlayerSelection = () => {
var s = Math.floor(Math.random() * 2); // betwen 0 and 2
return this.options[s];
}
// calculates the winner between player1 and player2
calculateWinner = (player1, player2) => {
// same
if (player1 === player2) return 'DRAW';
// p1 has rock
else if (player1 === this.ROCK) {
if (player2 === this.PAPER) return 'player2';
if (player2 === this.SCISSORS) return 'player1';
}
// p1 has paper
else if (player1 === this.PAPER) {
if (player2 === this.ROCK) return 'player1';
if (player2 === this.SCISSORS) return 'player2';
}
// p1 has scissors
else {
if (player2 === this.PAPER) return 'player1';
if (player2 === this.ROCK) return 'player2';
}
}
}
// #############################################################################################
// let's start the game engine
var engine = new RpsGame();
var selection = { rounds: [], player1: 0, player2: 0 };
// let's play 10 times
for (var i=1; i<10; i += 1) {
var player1 = engine.getAutoPlayerSelection(); // change to engine.askPlayerSelection if manual input
var player2 = engine.getAutoPlayerSelection();
var winner = engine.calculateWinner(player1, player2);
selection.rounds.push(winner); // save the winner
if (winner !== 'DRAW') selection[winner] += 1; // append
}
// put result in the HTML
document.querySelector('pre').innerHTML = JSON.stringify(selection, null, 2);
pre {
background-color: #eef;
padding: 10px;
}
<pre></pre>
in order to test, click Run code snippet as many times you want to run the 10 times

jquery: avoid too much recursion

I have a script that distribute the 54 playing cards randomly on 4 players, it will generate 2 random numbers to get a random card, the category number (from 1 to 4) which means: "hearts,spades,diamonds,clubs", and the card number (from 1 to 13).
The problem is there are too much recursions in this script, so how I can avoid this error by calling the functions in threads or something similar?
my code:
$(document).ready(function(){
var human = [];
var east = [];
var west = [];
var north = [];
var used_cards = [];
distributeCards(north,$('#north'));
distributeCards(east,$('#east'),'vertical');
distributeCards(west,$('#west'));
distributeCards(human,$('#south'));
function distributeCards(array,container,view){
for(var i = 0; i < 13; i++){
var category,card;
do{
var uniqueCard = uniqueRandomCard();
}while(typeof uniqueCard === "undefined")
category = uniqueCard[0];
card = uniqueCard[1];
array.push(uniqueCard);
var category_name = '';
if(category === 1){
category_name = 'hearts';
}
else if(category === 2){
category_name = 'spades';
}
else if(category === 3){
category_name = 'diamonds';
}
else if(category === 4){
category_name = 'clubs';
}
if(card === 1){
card = 'ace';
}
else if(card === 11){
card = 'jack';
}
else if(card === 12){
card = 'queen';
}
else if(card === 13){
card = 'king';
}
if(view === 'vertical'){
$(container).children('.row').append('<img src="cards/backRotate.png" class="card"/>');
}
else if(view === 'horizontal'){
$(container).children('.row').append('<img src="cards/back.png" class="card"/>');
}
}
}
function randomNumberFromRange(min,max){
return Math.round(Math.floor(Math.random()*max)+min);
}
function uniqueRandomCard(){
var card = randomNumberFromRange(1, 13);
var category = randomNumberFromRange(1, 4);
if(!inAssocArray(category,card,used_cards)){
var array = [];
array[0] = category;
array[1] = card;
used_cards.push(array);
return array;
}
else{
uniqueRandomCard();
}
}
function inAssocArray(key,value,array){
var flag = false;
for(var i = 0; i < array.length; i++){
if(array[i][0] === key && array[i][1]=== value){
flag = true;
}
}
return flag;
}
});
Fixing recursion the way you've implemented it is quite easy. Simply replace your call and if statement with a while statement.
function uniqueRandomCard(){
var card = randomNumberFromRange(1, 13);
var category = randomNumberFromRange(1, 4);
while(inAssocArray(category,card,used_cards)) {
card = randomNumberFromRange(1, 13);
category = randomNumberFromRange(1, 4);
}
var array = [];
array[0] = category;
array[1] = card;
used_cards.push(array);
return array;
}
That being said there are some fundamental better ways of handling limited sets like this. Storing unused cards and randomly selecting from that is far superior.

Tic Tac Toe with Minimax and Javascript

I am attempting to create a Tic Tac Toe game using Javascript as part of my learning on FreeCodeCamp and after my 5th attempt still haven't managed to get it to work. I think i'm doing the correct thing, but the computer AI is still making very stupid decisions and loosing.
Here is my entire AI function, which can be called using a console log to see the recommended move from the AI. However when i hard code values for moves already taken and ask for the next move, it doesn't pick what i would expect.
/*
Attempt to inplement a Tic Tac Toe minimax game
5th attempt, so hopefully this time it works!
*/
var util = require("util");
//These are all the winning square sequences, as string to make various things easier
var validRows = [
['00','01','02'], // left column
['10','11','12'], // middle column
['20','21','22'], // right column
['00','10','20'], // top row
['01','11','21'], // middle row
['02','12','22'], // bottom row
['00','11','22'], // Diag TL to BR
['20','11','02'] // Diag BL to TR
];
//Our scoring arrays for evaulating moves
var max1 = ['100','010','001'];
var min1 = ['200','020','002'];
var max2 = ['110','011'];
var min2 = ['220','022'];
var max3 = ['111'];
var min3 = ['222'];
//All the valid squares
var validSquaresFactory = ['00','10','20','01','11','21','02','12','22'];
//Store all the moves somewhere
//var computerMoves = ['10','22'];
//var userMoves = ['00','02'];
//Store all the moves somewhere
var computerMoves = ['11','22'];
var userMoves = ['00','01'];
function Board(minOrMax,computerMoves,userMoves){
this.computer = computerMoves;
this.user = userMoves;
this.minOrMax = minOrMax;
this.remaining = this.genRemaining();
this.complete = this.genComplete();
var results = this.getScore();
this.score = results[0];
this.winOrLoose = results[1];
}
Board.prototype.genRemaining = function(){
//Create an array of all moves taken
var takenMoves = this.computer.concat(this.user);
//Calculate all remaining squares
var validSquares = validSquaresFactory.filter(function(object){
return takenMoves.indexOf(object) === -1;
});
return validSquares;
}
Board.prototype.genComplete = function(){
return ((this.computer.length + this.user.length) === 9);
}
Board.prototype.flipMinOrMax = function(){
return (this.minOrMax === 'max') ? 'min' : 'max'
}
Board.prototype.genArrays = function(minOrMax,square){
var tempUser = this.user.slice(0);
var tempComputer = this.computer.slice(0);
if(minOrMax === 'min'){
tempUser.push(square);
} else {
tempComputer.push(square);
}
return [tempComputer,tempUser];
}
Board.prototype.generateBoards = function(minOrMax){
var boards = [];
var that = this;
this.remaining.forEach(function(remainingSquare){
var moves = that.genArrays(minOrMax,remainingSquare);
boards.push(new Board(minOrMax,moves[0],moves[1]));
})
//console.log(boards);
return boards;
}
Board.prototype.getScore = function(){
var that = this;
var winOrLoose = false;
var returnScore = validRows.reduce(function(storage,row,index,array){
var score = row.reduce(function(storage1,square){
if (that.computer.indexOf(square) !== -1) {
storage1 += '1';
} else if (that.user.indexOf(square) !== -1) {
storage1 += '2';
} else {
storage1 += '0';
}
return storage1;
},'')
var finalScore = 0;
if(max1.indexOf(score) != -1){
finalScore = 1;
} else if(min1.indexOf(score) != -1){
finalScore = -1;
} else if(max2.indexOf(score) != -1){
finalScore = 10;
} else if(min2.indexOf(score) != -1){
finalScore = -10;
} else if(max3.indexOf(score) != -1){
winOrLoose = true;
finalScore = 100;
} else if(min3.indexOf(score) != -1){
winOrLoose = false;
finalScore = -100;
}
storage.push(finalScore);
return storage;
},[])
var condensedReturnScore = returnScore.reduce(function(storage,score){
return storage+score;
})
return [condensedReturnScore,winOrLoose];
}
function generateMove(){
var board = new Board('max',computerMoves,userMoves);
var scores = [];
var boards = board.generateBoards('max');
boards.forEach(function(board){
scores.push(testMove(board,4));
});
scores = scores.map(function(score,index){
return [board.remaining[index],score];
});
var returnValue = scores.reduce(function(storage,score){
return (score[1].score > storage[1].score) ? score : storage;
});
return [returnValue[0],returnValue[1].score];
}
function testMove(masterBoard,count){
count --;
var boards = [];
boards = masterBoard.generateBoards(generateMinOrMax(masterBoard.minOrMax));
//console.log('/////////Master Board/////////');
//console.log(masterBoard);
//console.log(boards);
//console.log('//////////////////////////////');
boards = boards.map(function (move) {
if (move.complete === true || count === 0 || move.winOrLoose === true){
return move;
} else {
var returnScore = testMove(move,count);
return returnScore;
}
});
returnBoard = boards.reduce(function(storage,board,index,array){
if(board.minOrMax === 'max'){
return (board.score > storage.score) ? board : storage;
} else {
return (board.score < storage.score) ? board : storage;
}
});
return returnBoard;
}
function generateMinOrMax(minOrMax){
return (minOrMax === 'max') ? 'min' : 'max'
}
I've checked the scoring function and from what i can see it is returning the expected score for any move i try, but because of the shear number of possibilities calculated it's very hard to debug efficiently.
Any help/pointers on this would be most appreciated as i have really hit a brick wall with this, can't see the forrest for the trees e.t.c
If you would like to test this with the GUI, it's on codepen at - http://codepen.io/gazzer82/pen/yYZmvJ?editors=001
So after banging my head against this for day, as soon as i posted this i found the issues. Firstly using the wrong variable for minormax in my reduce function, so it wasn't flipping correctly and not setting the winOrLoose value correctly for a score of -100. Here is the corrected version.
*
Attempt to inplement a Tic Tac Toe minimax game
5th attempt, so hopefully this time it works!
*/
var util = require("util");
//These are all the winning square sequences, as string to make various things easier
var validRows = [
['00','01','02'], // left column
['10','11','12'], // middle column
['20','21','22'], // right column
['00','10','20'], // top row
['01','11','21'], // middle row
['02','12','22'], // bottom row
['00','11','22'], // Diag TL to BR
['20','11','02'] // Diag BL to TR
];
//Our scoring arrays for evaulating moves
var max1 = ['100','010','001'];
var min1 = ['200','020','002'];
var max2 = ['110','011'];
var min2 = ['220','022'];
var max3 = ['111'];
var min3 = ['222'];
//All the valid squares
var validSquaresFactory = ['00','10','20','01','11','21','02','12','22'];
//Store all the moves somewhere
//var computerMoves = ['10','22'];
//var userMoves = ['00','02'];
//Store all the moves somewhere
var computerMoves = ['00','20','01'];
var userMoves = ['10','11','02'];
//01,21,22 - 01//
function Board(minOrMax,computerMoves,userMoves){
this.computer = computerMoves;
this.user = userMoves;
this.minOrMax = minOrMax;
this.remaining = this.genRemaining();
this.complete = this.genComplete();
var results = this.getScore();
this.score = results[0];
this.winOrLoose = results[1];
}
Board.prototype.genRemaining = function(){
//Create an array of all moves taken
var takenMoves = this.computer.concat(this.user);
//Calculate all remaining squares
var validSquares = validSquaresFactory.filter(function(object){
return takenMoves.indexOf(object) === -1;
});
return validSquares;
}
Board.prototype.genComplete = function(){
return ((this.computer.length + this.user.length) === 9);
}
Board.prototype.flipMinOrMax = function(){
return (this.minOrMax === 'max') ? 'min' : 'max'
}
Board.prototype.genArrays = function(minOrMax,square){
var tempUser = this.user.slice(0);
var tempComputer = this.computer.slice(0);
if(minOrMax === 'min'){
tempUser.push(square);
} else {
tempComputer.push(square);
}
return [tempComputer,tempUser];
}
Board.prototype.generateBoards = function(minOrMax){
var boards = [];
var that = this;
this.remaining.forEach(function(remainingSquare){
var moves = that.genArrays(minOrMax,remainingSquare);
boards.push(new Board(minOrMax,moves[0],moves[1]));
})
//console.log(boards);
return boards;
}
Board.prototype.getScore = function(){
var that = this;
var winOrLoose = false;
var returnScore = validRows.reduce(function(storage,row,index,array){
var score = row.reduce(function(storage1,square){
if (that.computer.indexOf(square) !== -1) {
storage1 += '1';
} else if (that.user.indexOf(square) !== -1) {
storage1 += '2';
} else {
storage1 += '0';
}
return storage1;
},'')
var finalScore = 0;
if(max1.indexOf(score) != -1){
finalScore = 1;
} else if(min1.indexOf(score) != -1){
finalScore = -1;
} else if(max2.indexOf(score) != -1){
finalScore = 10;
} else if(min2.indexOf(score) != -1){
finalScore = -10;
} else if(max3.indexOf(score) != -1){
winOrLoose = true;
finalScore = 100;
} else if(min3.indexOf(score) != -1){
winOrLoose = true;
finalScore = -100;
}
storage.push(finalScore);
return storage;
},[])
var condensedReturnScore = returnScore.reduce(function(storage,score){
return storage+score;
})
return [condensedReturnScore,winOrLoose];
}
function generateMove() {
var board = new Board('max', computerMoves, userMoves);
if (board.remaining.length === 1) {
return [board.remaining[0], 0];
} else {
var scores = [];
var boards = board.generateBoards('max');
boards.forEach(function (board) {
scores.push(testMove(board, 4));
});
scores = scores.map(function (score, index) {
return [board.remaining[index], score];
});
var returnValue = scores.reduce(function (storage, score) {
return (score[1].score > storage[1].score) ? score : storage;
});
return [returnValue[0], returnValue[1].score];
}
}
function testMove(masterBoard,count){
count --;
var boards = [];
boards = masterBoard.generateBoards(generateMinOrMax(masterBoard.minOrMax));
boards = boards.map(function (move) {
if (move.complete === true || count <= 0 || move.winOrLoose === true){
return move;
} else {
var returnScore = testMove(move,count);
return returnScore;
}
});
returnBoard = boards.reduce(function(storage,board,index,array){
if(generateMinOrMax(masterBoard.minOrMax) === 'max'){
return (board.score > storage.score) ? board : storage;
} else {
return (board.score < storage.score) ? board : storage;
}
});
return returnBoard;
}
function generateMinOrMax(minOrMax){
return (minOrMax === 'max') ? 'min' : 'max'
}
function getScore(user,computer,minOrMax){
var that = this;
that.computer = computer;
that.user = user;
that.minOrMax = minOrMax;
var returnScore = validRows.reduce(function(storage,row,index,array){
var score = row.reduce(function(storage1,square){
if (that.computer.indexOf(square) !== -1) {
storage1 += '1';
} else if (that.user.indexOf(square) !== -1) {
storage1 += '2';
} else {
storage1 += '0';
}
return storage1;
},'')
var finalScore = 0;
if(max1.indexOf(score) != -1){
finalScore = 1;
} else if(min1.indexOf(score) != -1){
finalScore = -1;
} else if(max2.indexOf(score) != -1){
finalScore = 10;
} else if(min2.indexOf(score) != -1){
finalScore = -10;
} else if(max3.indexOf(score) != -1){
finalScore = 100;
} else if(min3.indexOf(score) != -1){
finalScore = -100;
}
storage.push(finalScore);
return storage;
},[])
var condensedReturnScore = returnScore.reduce(function(storage,score){
if(that.minOrMax === 'max'){
return (score >= storage) ? score : storage;
} else {
return (score <= storage) ? score : storage;
}
})
return condensedReturnScore;
}

Categories

Resources