How can I create a reset button for this game? - javascript

I have my current code posted, I'm trying to create a reset button. A button has already been created in my HTML, and I'm grabbing it at the bottom, and adding an event listener, which isn't working for some reason, also trying to figure out the correct code to add it for my game resets when the button is clicked. However having a difficult time with the syntax.
// Array of words
const words = ['planet', 'stars', 'astroid', 'moon', 'satilite', 'orbit', 'universe', 'umbra', 'lunar', 'space', 'astronomy', 'eclipse', 'nebula', 'mars', 'meteorite']
// guesses Array
let myGuesses = []
//variables
let wordSpace = ' - '
let guess = ' '
let space; //number of spaces in word
//score
let tries = 10
let counter ;
//Get random word
let index = Math.floor(Math.random()* words.length)
//play function
function play() {
let userInput = prompt(`would you like to play spaceman? (Y/N)`, "Y")
console.log(words[index])
for(let i = 0; i < words[index].length; i++){
console.log(words[0][i])
let div = document.createElement('div')
div.classList.add('letters')
div.innerHTML=' - '//words[0][i]
document.querySelector('.word-space').append(div)
}
}
//handle click function, inactivates buttons, and changes color to grey; once clicked
let handleclick = e => {
e.target.removeEventListener('click', handleclick)
e.target.style.backgroundColor= 'grey'
console.log(e.target.innerHTML)
myGuesses.push(e.target.innerHTML)
console.log(myGuesses)
console.log(words[index].includes(e.target.innerHTML))
if(words[index].includes(e.target.innerHTML)){
document.querySelector('.word-space').innerHTML= ' '
// let correct = document.createElement('ul')
for(let i = 0; i < words[index].length; i++){
// correct.setAttribute('id','correctLetters' )
// let guess= document.createElement('li')
// guess.setAttribute('class','guess')
console.log(words[0][i])
let div = document.createElement('div')
div.classList.add('letter')
if (myGuesses.includes(words[index][i])){
div.innerHTML = words[index][i]
} else {
div.innerHTML = ' - '
}
document.querySelector('.word-space').append(div)
}
getNumOfTries()
} else {
tries --
getNumOfTries()
}
}
function ans () {
const buttons = document.querySelectorAll('.letter')
buttons.forEach(letter => letter.addEventListener('click',handleclick))
}
ans()
function getNumOfTries (){
console.log(tries)
const showTries = document.querySelector('#myTries')
showTries.innerHTML = ' You have ' + tries + ' tries'
if(tries < 1){
setTimeout(() =>{prompt(`Would you like to try again? (Y,N)`, 'Y')
showTries.innerHTML = 'You loose!'
},2000)
}
// if(tries > 0 && words[index].length === myGuesses.length) {
if(tries > 0 && Array.from(document.querySelectorAll('.letters')).every(letter => letter.innerHTML !== ' - ')) {
// showTries.innerHTML = 'You Win!'
setTimeout(() =>{alert(`You Win!`)
showTries.innerHTML = 'You Win!'
},1000)
}
}
//reset game
let tryAgain = document.querySelector('.Try-Again')
tryAgain.addEventListener('clcik', play)
prevent
div.innerHTML=' - '//words[0][i]
document.querySelector('.word-space').append(div)
play()

You've got a typo there :)
tryAgain.addEventListener('clcik', play)
some notes to the written code:
you don't need to reference the element, if you're not going to use it elsewhere, just do:
document.querySelector('.Try-Again')?.addEventListener('click', play);
i'm not sure about what the "prevent" should do here
you haven't defined a global reference to a "div" parameter that you want to use at:
div.innerHTML=' - '//words[0][i]
use semicolon ';' by the end of each executed code for better code clarity + checkout some coding standards
use code editors like Visual Studio code or other tools - they usually have some basic code formatter (learn they shortcuts, it will optimize your coding process) + other usefull features like clicking through defined param references etc.

Related

Rock Paper Scissors game loop

I'm working on a task to build a Stone,Paper,Scissors Game.
The game is supposed to run in 3 possible modes. best of 3 / best of 5 and endless games.
My code works fine until the point where I reset the game to begin a new game.
For a reason which I can not figure out, it plays multiple instances of my game, each time I call the game() function.
(after the game is over - a reset game button appears and the game mode needs to be reseleted)
I've added the entire project to this https://jsfiddle.net/vydkg3bz/, since I don't manage to pinpoint where the issue is.
I believe, due to all the extra logging I added, that the problem is somewhere in the game() function, as I tried to replace most of the code around it, with the same result.
Maybe someone has a moment to review my code and give me a hint where I should look?
// global variables
var choicesObj = {
Rock: "url('./img/stone.png')",
Paper: "url('./img/paper.png')",
Scissors: "url('./img/scissors.png')",
}
var choices = Object.keys(choicesObj);
var moveAI;
var movePlayer;
var winnerRound;
var winnerGame;
var roundCount = 0;
// actual game
function game(requiredWins) {
console.log("game");
enabler()
var inputs = document.querySelectorAll(".inputButton");
console.log(inputs);
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener("click", function() {
movePlayer = this.id
handleClick(movePlayer)
});
}
// checking choice, display choice IMG, call compareChoices function
function handleClick(buttonID) {
console.log("handleClick");
moveAIfunc()
movePlayer = buttonID;
console.log('movePlayer: ' + movePlayer);
console.log('moveAI: ' + moveAI);
document.getElementById("mainImgPlayer").style.backgroundImage = choicesObj[movePlayer];
document.getElementById("mainImgAI").style.backgroundImage = choicesObj[moveAI];
compareChoices(moveAI,movePlayer);
console.log("requiredWins", requiredWins);
gameEnd();
displays();
}
// AI control
function moveAIfunc() {
moveAI = choices[Math.floor(Math.random() * choices.length)]
}
// compare choices
function compareChoices(a, b) {
console.log("compareChoices");
a = choices.indexOf(a);
b = choices.indexOf(b);
console.log("requiredWins", requiredWins);
roundCount++;
if (a == b) {
winnerNone()
return;
}
if (a == choices.length - 1 && b == 0) {
winnerPlayer()
return;
}
if (b == choices.length - 1 && a == 0) {
winnerAI()
return;
}
if (a > b) {
winnerAI()
return;
}
else {
winnerPlayer()
return;
}
}
// game end
function gameEnd() {
console.log("gameEnd");
console.log(requiredWins,winsPlayer,winsAI);
if (winsPlayer == requiredWins) {
console.log(requiredWins,winsPlayer,winsAI);
winnerGame = "Player";
disabler()
createEndButton()
return;
}
if (winsAI == requiredWins) {
console.log(requiredWins,winsPlayer,winsAI);
winnerGame = "AI";
disabler()
createEndButton()
return;
}
}
}```
Your code seems to be pretty complicated. The state of the wins/required wins etc. is not saved adequately in it. For playing you add event listeners per game (click), which is pretty inefficient imho.
I have created a mockup (a minimal reproducable example) for the handling of games, using data attributes to remember the state of a game and event delegation for the handling. Maybe it's useful for you.
Not related, but here's my take on RPS.
document.addEventListener(`click`, handle);
function handle(evt) {
if (evt.target.id === `play`) {
return play(evt.target);
}
if (evt.target.name === `nWins`) {
return reset(evt.target);
}
}
function reset(radio) {
const value = radio === `none`
? Number.MAX_SAFE_INTEGER : +radio.value;
const bttn = document.querySelector(`#play`);
const result = document.querySelector(`#result`);
result.textContent = `(Re)start initiated, click [Play]`;
result.dataset.wins = bttn.dataset.wins = 0;
bttn.dataset.plays = 0;
return bttn.dataset.winsrequired = value;
}
function play(bttn) {
if (+bttn.dataset.winsrequired < 1) {
return document.querySelector(`#result`)
.textContent = `Select best of to start playing ...`;
}
const result = document.querySelector(`#result`);
bttn.dataset.plays = +bttn.dataset.plays + 1;
const won = Math.floor(Math.random() * 2);
const wins = +result.dataset.wins + won;
result.textContent = `${won ? `You win` : `You loose`} (plays: ${
bttn.dataset.plays}, wins ${wins})`;
result.dataset.wins = wins;
if (+bttn.dataset.plays === +bttn.dataset.winsrequired) {
result.textContent = `You won ${wins} of ${
bttn.dataset.winsrequired} games. Choose 'Best of'
to start another game`;
bttn.dataset.plays = bttn.dataset.winsrequired = 0;
result.dataset.wins = 0;
document.querySelectorAll(`[name="nWins"]`)
.forEach(r => r.checked = false);
}
}
Best of
<input type="radio" name="nWins" value="3"> 3
<input type="radio" name="nWins" value="5"> 5
<input type="radio" name="nWins" value="none"> indefinite
<button id="play" data-winsrequired="0" data-plays="0" >Play</button>
<p id="result" data-wins="0"></p>

How to add elements one by one and update in browser using javascript?

So as shown below , I have a function that is executed on a button click , and it adds 30 div elements to the document with some attributes.
var bar_counter = 0;
bar_lengths = new Array();
function createBar(btnObj)
{
while(bar_lengths.length < 30){
var size = Math.floor(Math.random() * (50 - 1) ) + 1;
if(size>50)
{
alert("Please enter a value between 1 and 50 only!");
}
else{
if(bar_lengths.indexOf(size) === -1){
bar = document.createElement("div");
bar_counter = parseInt(bar_counter) + parseInt(1);
bar.setAttribute("class","bars");
bar.setAttribute("name","bar"+bar_counter);
bar.setAttribute("id",size);
bar.style.height=10*size+"px";
var color1 = '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
var color2 = '#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6);
bar.style.backgroundImage="linear-gradient(359deg,"+color1+","+color2+")";
bar.insertAdjacentHTML("beforeend","<font color='red' style='vertical-align: text-bottom; background-color:white'>"+size+"</font>");
bar.insertAdjacentHTML("beforeend","<font color='red' style='vertical-align: text-bottom; background-color:white'> </font>");
var place_to_insert = document.getElementById("placeBars");
place_to_insert.appendChild(bar);
bar_lengths.push(size);
}
}
}
btnObj.disabled=true;
temp="";
for(var i= 0; i < bar_lengths.length; i++) {
temp = temp+bar_lengths[i]+" , ";
}
document.getElementById("YourArray").innerHTML = temp;
}
It is working perfectly , but on button click , the browser window is instantly populated with 30 div elements.
What i want to do is add a delay to this loop to show them getting added one by one in the window. How can i do it? I tried using setTimeout() and setInterval() functions , but it didn't work. TIA.
~regards
You could use this helper function to "pause" the execution of your code with the duration you want:
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
and then await timeout(3000).
Note that the outer function needs to be async in order to use await inside it
Loop with delay simple method...
const loopCountMax = 30
;
let loopCount = 1
, refInterval = setInterval(() =>
{
// code of your stuff
// ...
if (++loopCount > loopCountMax ) clearInterval(refInterval)
}
, 1000); // delay value

i am trying to make a function that check for errors, but i get only one or two errors which is wrong

var checkErrors = 0;
// Check for mistakes function
function yourResult() {
let checkErrors = 0;
let textEnterd = testArea.value;
let orginTextMatch = originText.substring(0, textEnterd.length);
if (textEnterd.length == originText.length || textEnterd == originText) {
if (textEnterd != orginTextMatch) {
++checkErrors;
}
theResult.innerHTML **strong text** = "You did " + checkErrors + " mistakes.";
}
}
testArea.addEventListener('keyup', function() {
yourResult();
}, false);
// "return"
This is a test typing project, i am stuck at the yourResult function, i cant get the desire errors.
The main mistake is that the error count is reset after each stroke.
So put that outside and you can match the characters as they're typed.
It's not far off, so you can try this instead:
// Put check errors variable outside of the function to keep count between the key strokes
// Reset to 0 when starting the test over
let checkErrors = 0;
function yourResult(){
let textEnterd = testArea.value;
let charPos = textEnterd.length;
// Match the characters at their positions. If they don't match, add them up
if (charPos > 0 && textEnterd.charAt(charPos) != originText.charAt(charPos)) {
++checkErrors;
}
theResult.innerHTML = "You did " + checkErrors + " mistakes.";
}

How to force loop to wait until user press submit button?

I have simple function which checks if entered pin code is valid. But i don't know how to force for-loop to wait until i enter code again to check again it's validity.
So how it should be - i type PIN code, then click OK button and it checks whether it's correct (if it is, i can see my account menu; if it's not i have to type it again and i have 2 chances left). My code fails, because PIN when code is wrong program should wait until i type new code and press OK button again.
I tried setTimeout(), callback(), but it doesn't work. This is what i have - a function with for-loop that just runs 3 times (as it is suppose to, but not instantly) without giving a chance to correct the PIN code.
That's whole, unfinished yet, code: http://jsfiddle.net/j1yz0zuj/
Only function with for-loop, which checks validity of PIN code:
var submitKey = function(callback)
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
for (i=0; i<3; i++)
{
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (2-i);
callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
if (i=2) console.log("blokada");
}
}
else if (digitStatus == 1)
{
}
}
Your approach is wrong. You should not make the user wait!!! You need 2 more variables at the top of your programm pincount=0 and pininputallowed. Increase pincount in the submit key function by 1 and then check if pincount<3.
Here is a corrected version of your code.
http://jsfiddle.net/kvsx0kkx/16/
var pinCount=0,
pinAllowed=true;
var submitKey = function()
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
pinCount++;
if(pinCount >= 3) {
pinAllowed = false;
onScreen.innerHTML = "<br>blokada";
}
if(pinAllowed){
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (3-pinCount);
inputLength = 0;
document.getElementById("screen").innerHTML += "<br>Wpisz kod PIN: ";
//callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
}
}
else if (digitStatus == 1)
{
}
}
You need to create much more variables to control your machine. Your add/delete digit function had conditions that were badly written and only worked if the text on the screen was short enough.
var inputLength = 0;
addDigit = function(digit){
//numKeyValue = numKeyValue instanceof MouseEvent ? this.value : numKeyValue;{
if (inputLength < pinLength) {
onScreen.innerHTML += this.value;
inputLength++;
}
//if (onScreen.innerHTML == 1234) console.log("PIN został wprowadzony");
},
delDigit = function(){
if (inputLength >= 0) {
onScreen.innerHTML = onScreen.innerHTML.slice(0, -1);
inputLength--;
}
};
If you want to empty the screen at any moment you can insert onScreen.innerHTML = ''; anywhere
ps: Thanks for the exercise and nice automat you made there.

jQuery "keyup" crashing page when checking 'Word Count'

I have a word counter running on a DIV and after typing in a few words, the page crashes. The browser continues to work (par scrolling) and no errors are showing in Chrome's console. Not sure where I'm going wrong...
It all started when I passed "wordCount(q);" in "keyup". I only passed it there as it would split-out "NaN" instead of a number to countdown from.
JS:
wordCount();
$('#group_3_1').click(function(){
var spliced = 200;
wordCount(spliced);
}) ;
$('#group_3_2').click(function(){
var spliced = 600;
wordCount(spliced);
}) ;
function wordCount(q) {
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0)
var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
$('.message1').keyup(function() {
wordCount(q);
});
try
{
if (new Number( word_count ) < 0) {
$(".word_count").attr("id","bad");
}
else {
$(".word_count").attr("id","good");
}
} catch (error)
{
//
}
};
HTML:
<input type="checkbox" name="entry.3.group" value="1/6" class="size1" id="group_3_1">
<input type="checkbox" name="entry.3.group" value="1/4" class="size1" id="group_3_2">
<div id="entry.8.single" class="message1" style="height: 400px; overflow-y:scroll; overflow-x:hidden;" contenteditable="true"> </div>
<span class="word_count" id="good"></span>
Thanks in advanced!
This is causing an infinite loop if (new Number(word_count) < 0) {.
Your code is a mess altogether. Just study and start with more basic concepts and start over. If you want to describe your project to me in a comment, I would be glad to show you a good, clean, readable approach.
Update:
Part of having a good architecture in your code is to keep different parts of your logic separate. No part of your code should know about or use anything that isn't directly relevant to it. Notice in my word counter that anything it does it immediately relevant to its word-counter-ness. Does a word counter care about what happens with the count? Nope. It just counts and sends the result away (wherever you tell it to, via the callback function). This isn't the only approach, but I just wanted to give you an idea of how to approach things more sensefully.
Live demo here (click).
/* what am I creating? A word counter.
* How do I want to use it?
* -Call a function, passing in an element and a callback function
* -Bind the word counter to that element
* -When the word count changes, pass the new count to the callback function
*/
window.onload = function() {
var countDiv = document.getElementById('count');
wordCounter.bind(countDiv, displayCount);
//you can pass in whatever function you want. I made one called displayCount, for example
};
var wordCounter = {
current : 0,
bind : function(elem, callback) {
this.ensureEditable(elem);
this.handleIfChanged(elem, callback);
var that = this;
elem.addEventListener('keyup', function(e) {
that.handleIfChanged(elem, callback);
});
},
handleIfChanged : function(elem, callback) {
var count = this.countWords(elem);
if (count !== this.current) {
this.current = count;
callback(count);
}
},
countWords : function(elem) {
var text = elem.textContent;
var words = text.match(/(\w+\b)/g);
return (words) ? words.length : 0;
},
ensureEditable : function(elem) {
if (
elem.getAttribute('contenteditable') !== 'true' &&
elem.nodeName !== 'TEXTAREA' &&
elem.nodeName !== 'INPUT'
) {
elem.setAttribute('contenteditable', true);
}
}
};
var display = document.getElementById('display');
function displayCount(count) {
//this function is called every time the word count changes
//do whatever you want...the word counter doesn't care.
display.textContent = 'Word count is: '+count;
}
I would do probably something like this
http://jsfiddle.net/6WW7Z/2/
var wordsLimit = 50;
$('#group_3_1').click(function () {
wordsLimit = 200;
wordCount();
});
$('#group_3_2').click(function () {
wordsLimit = 600;
wordCount();
});
$('.message1').keydown(function () {
wordCount();
});
function wordCount() {
var text = $('.message1').text(),
textLength = text.length,
wordsCount = 0,
wordsRemaining = wordsLimit;
if(textLength > 0) {
wordsCount = text.replace(/[^\w ]/g, '').split(/\s+/).length;
wordsRemaining = wordsRemaining - wordsCount;
}
$('.word_count')
.html(wordsRemaining + " words remaining...")
.attr('id', (parseInt(wordsRemaining) < 0 ? 'bad' : 'good'));
};
wordCount();
It's not perfect and complete but it may show you direction how to do this. You should use change event on checkboxes to change wordsLimit if checked/unchecked. For styling valid/invalid words remaining message use classes rather than ids.
I think you should use radio in place of checkboxes because you can limit 200 or 600 only at a time.
Try this like,
wordCount();
$('input[name="entry.3.group"]').click(function () {
wordCount();
$('.word_count').html($(this).data('val') + " words remaining...");
});
$('.message1').keyup(function () {
wordCount();
});
function wordCount() {
var q = $('input[name="entry.3.group"]:checked').data('val');
var content_text = $('.message1').text(),
char_count = content_text.length;
if (char_count != 0) var word_count = q - content_text.replace(/[^\w ]/g, "").split(/\s+/).length;
$('.word_count').html(word_count + " words remaining...");
try {
if (Number(word_count) < 0) {
$(".word_count").attr("id", "bad");
} else {
$(".word_count").attr("id", "good");
}
} catch (error) {
//
}
};
Also you can add if your span has bad id then key up should return false;
See Demo

Categories