How do I re run the function in my Simon game? - javascript

I'm pretty confident that I have the basic logic behind my Simon game. However, I'm having issues in going to the next round. I have attempted to use an if statement and while loop but neither have worked. How should I proceed?
A brief explanation: gameColors is an array that has all the random colors that will be played out in an increasing number over several rounds. The variable sequencelength is used to increase the counter used in the user input function, whereas the variable sequenceLengthInv is the opposite of that, subtracted from gameColors.length to get sequenceLength.
random sequence displays the random sequence of colors up to the sequenceLength value.
JS:
if (sequenceLengthInv >= 0) {
// have the game play the random sequence of colors
for (var i = 0; i < gameColors.length - sequenceLengthInv; i++) {
randomSequence();
}
//listens for the user's input
for (var i = 0; i < color.length; i++) {
userInput();
}
sequenceLength++;
sequenceLengthInv--;
}

Try using for loops and using throw/reject & catch (or break). Also, Promises may be useful here.
async function playGame(availableColors, minColors, maxColors) {
await wait(333);
var colors = Array.from({ length: maxColors }, () => randomChoice(availableColors));
try {
for (var i = minColors; i <= maxColors; i++) {
console.log(colors.slice(0, i));
await playRound(colors.slice(0, i));
}
alert('You win!');
} catch (e) {
if(e !== undefined) {
throw e;
}
alert('You lose!');
}
}
My modified (and now commented) code is available here: https://repl.it/#solly_ucko/InconsequentialRoastedMonitor.

Related

Optimize function without looping through array

I've developed a small memory game, the game contains a function to flip cards for every turn. I'm looping thru the array containing the images every time to pick one card. I'm sure there's away to optimize this function so I don't have to loop thru the array every time in order to just turn one card. But I'm a bit clueless how to proceed and would like to get some guidance of how I possibly could optimize this function.
Can I create some sort of if statement perhaps? Note. Looking for vanilla javascript suggestions only and without creating an object to replace the array.
I'm clueless of another method then to loop thru the array.
function flipCards() {
var i; // Loopvar
// For loop
for (i = 0; i < cardsElems.length; i++) {
if (this == cardsElems[i]) {
if (cardsCounter > 1) {
return;
}
cardsElems[i].className = "brickFront";
cardsElems[i].src = "pics/" + allCards[i] + ".png";
removeListener(cardsElems[i], "click", flipCards);
if (cardsCounter < 1) {
card1 = i;
} else {
card2 = i;
}
cardsCounter += 1;
}
}
if (cardsCounter < 2) {
nextBtn.disabled = true; // Disable button
}
if (cardsCounter == 2) {
turns += 1;
turnNr.innerHTML = turns;
nextBtn.disabled = false; // Enable button
}
if (brickBack.length == 0) {
endGame();
}
} // End flipCards
how about using Loadash ?
It has plenty of function to manipulate arrays and very efficient.
you can store your data inside an object.By key(card name) value(fliped or not). then access by the key to change the value to be flipped or not.
example : {Ace: true} = {'cardName': 'isFlipped'}

Algorithm : finding nearby value the fastest way

I have an array of sorted numbers, and a starting value. For the sake of simplicity, let's say the array has values from 1 to 20, and the starting value is 10.
The value to find can change every 5 seconds, based on user input. It can either increase or decrease and it always keeps in the range of values of the table.
The only thing I cannot know is whether the value is increasing or decreasing. I have come up with the (very) simple algorithm below.
Can you think of a way to improve it ?
Ideally, I think the best approach would be to run the two for loops simultaneously and return when value is found... Using workers for example ?
In my example, the "going down" for loops is exhausted before "going up" starts running. Which, idealy, shouldn't happen, since I'm trying to spread tries -1/+1 each time.
Btw : the reason why I'm trying to do this is because I have to run quite heavy functions in the for loops. The code is running in node.js context.
Here's a JSFiddle and the code below
const attempts = document.getElementById('attempts');
let attemptsCount = Number(attempts.textContent);
const lastValue = 10;
const valueToFind = 16; //this must be found in the least possible number of attempts
const table = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const lookup = () => {
for (var i = lastValue; i > 0; i--) {
if (table[i] == valueToFind) {
alert('Found going down');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
for (var i = lastValue; i < table[table.length-1]; i++) {
if (table[i] == valueToFind) {
alert('Found going up');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
}
//find new value
lookup();
Right now, each for loop you have runs async from one another...so one side will always finish before the other side starts...which is not ideal.
Remember that for loops are set up to initialize, check if bool statement = true, and set next step...so like if statements, you can implement multiple statements in each scenario.
Reducing the loop attempts can be simple as doing it at the same time:
const lookup = () => {
for (var up = lastValue, down = lastValue-1; up < table[table.length-1] || down > 0; up++, down--) {
if (up < table[table.length-1] && table[up] == valueToFind) {
alert('Found going up');
return;
} else if (down > 0 && table[down] == valueToFind) {
alert('Found going down');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
Your JSFiddle updated

setTimeout and recursive IFFEs, nested!? How to calculate the delay.

This problem has really reminded me where I am regarding JS skills... :(
I feel I'm right on the cusp but I'm struggling to understand what to do conceptually, it's not so much a syntax issue as I feel I almost cracked it.
What I need
I'm trying to console.log a series of strings, one letter at a time.
There needs to be a delay between each LETTER outputted, say 300ms.
There must then be a delay between each STRING outputted, say 2000ms.
There are 2 strings in example array, but solution must support a dynamic number of strings.
My Current Code (can be pasted into a console)
var stringList = ['first test','second test'],
stringListLen = stringList.length;
for(var i = 0; i < stringListLen; i++){
// begin with first string and read it's length for iterations
var stringNumber = 0;
var currentString = stringList[stringNumber];
var currentStringLen = currentString.length;
// just see the word for clarification at this point in code
console.log(currentString);
(function (i) {
setTimeout(function () {
for (var j = 0; j < currentStringLen; j++) {
(function (j) {
setTimeout(function () {
// THE MAGIC HAPPENS HERE
console.log(j, currentString.charAt(j));
// End of string, so read next string, reset letter count
if(j === currentStringLen - 1){
stringNumber++;
currentString = stringList[stringNumber];
j = 0;
}
}, 300 * j); // each letter * specified delay
})(j);
};
}, currentStringLen * 300 * i); // letter * delay * letters in word
})(i);
}
The Issue
THE GOOD: I am successfully getting the short delay between letters outputted, and my check to switch to a new word and reset the letter counter when we get to the end of the first word is working...
THE BAD: I can't get the wait between the two words to work. I have tried a few ideas and have just got myself so confused I don't know if my approach is correct now.
THE UGLY: The final letter of the last term is also not outputting, and that is just totally unexpected.
What I've tried.
Okay, I've tried simply changing the "currentStringLen * 300 * i" elements to various combinations that seemed logical but had no effect better or worse. Ultimately I think I am trying to calculate "wait the number of letters in current string times 300 (the letter delay) * " <---- STRIKETHROUGH...
I actually don't know what I'm calculating and that's the issue.
I now think I want to split this into TWO functions, not two nested ones. One to READ AND PASS IN a string to another function that JUST outputs the letters with a short delay, then once it gets to the last letter it calls the first function asking for the next word. BUT then I'm still going to need to recurse for the number of strings in the array which creates the same issue...
Am I missing anything fundamental here people?
Is this roughly what you had in mind?
function printLetters(stringList) {
var wordIndex = 0,
letterIndex = 0;
printNext();
function printNext() {
var word = stringList[wordIndex];
var letter = word.charAt(letterIndex);
console.log(letter);
++letterIndex;
if (letterIndex === word.length) {
letterIndex = 0;
++wordIndex;
if (wordIndex < stringList.length) {
setTimeout(printNext, 2000);
}
return;
}
setTimeout(printNext, 300);
}
}
printLetters(['first test', 'second test']);
Here there's only ever one setTimeout running at once and a new one is being set as required with the appropriate time.
While I don't recommend having multiple timers running at once, it can be done. Something like this:
function printLetters(stringList) {
var letterCount = 0,
startTime = Date.now();
stringList.forEach(function(word, wordCount) {
word.split('').forEach(function(letter) {
setTimeout(function() {
console.log(letter, Date.now() - startTime);
}, wordCount * 1700 + (letterCount * 300));
++letterCount;
});
});
}
printLetters(['first test', 'second test']);
Here I've included the time delta in the logging to give a better sense of what is going on when. The gap between strings is 2000 but the constant in the code is 1700 because there's already 300 being added.
I would do a quite different approach. Rather than doing a bunch of precalculated timeouts and their associated closures, i would do just one timeout at a time, using recursion to then move on to the next timeout:
function delayShow(words) {
if (!words || words.length === 0) {
return;
} else if (words[0].length === 0) {
words.shift()
setTimeout(() => delayShow(words), 2000);
} else {
console.log(words[0].charAt(0));
words[0] = words[0].substr(1);
setTimeout(() => delayShow(words), 300);
}
}
delayShow(['first test','second test']);
You can use a condition inside the loop, and when you're on the last iteration of the strings characters, you call the recursive function again, inside a timeout, to iterate over the next string in the array etc.
var stringList = ['first test', 'second test'];
(function rec(j) {
var chars = stringList[j].split('');
chars.forEach(function(char, i) {
setTimeout(function() {
console.log(char);
// if it's the last iteration && there are more strings in the array
if ((i == (chars.length - 1)) && (j < stringList.length - 1)) {
setTimeout( function() {
rec(++j); // play it again
}, 2000);
}
}, i * 300);
});
})(0);
Even something as trivial as setTimeout's, is a good example of using async / await. So I've included an example below.
As you will see, the code is much easier to follow. With the added advantage of not creating multiple setTimeout's running concurrently,.
It also really helps make things much easier to change things, eg. If I asked you to alter the code below so that SPACE takes less time than other letters, to make it flow more naturally, it wouldn't take a lot of thought to work out what to change.
var
stringList = ['first test','second test'];
async function delay(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});
}
async function run() {
let wordpos = 0;
let wordElem = document.querySelector('.words');
while (true) {
let word = stringList[wordpos];
let text = '';
for (var letterpos = 0; letterpos < word.length; letterpos ++ ) {
let letter = word[letterpos];
text = text + letter;
wordElem.innerText = text;
await delay(300);
}
await delay(2000);
wordpos = (wordpos + 1) % stringList.length;
}
}
run();
<h1 class="words">?</h1>

Trouble pushing to an array in JS

Below is just a section of my code but I know it's problematic because I can't get it to return any value except 'undefined'. I have been over this for hours and cannot figure it out.
I want to be able to input a number and have its factors pushed to an array. I have tested it by alerting the first item in the array and I get nothing. I'm sure this is a pretty easy but I just can't figure it out. Here is the code:
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
//determine factors and push to array for later use
var factorsArray = [];
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
return factorsArray.push[i];
} else {
continue;
}
}
};
factors(numberInQuestion);
alert(factorsArray[0]);
Thanks for any help!
you can only return one value
you must use (), not [] for calling push
factorsArray should be local to factors (put the definition inside the function)
the else { continue; } is useless
Here is the fully corrected code:
var numberInQuestion = prompt("Of what number are you wanting to find the factors of?");
//determine factors
function factors(numberInQuestion){
var factorsArray = []; // make it local
for (var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
factorsArray.push(i); // use (), and don't return here
} // no need for else { continue; } because it's a loop anyway
}
return factorsArray; // return at the end
};
var result = factors(numberInQuestion); // assign the result to a variable
alert(result);
Here's a JSFiddle.
You have an error in your pushing syntax. Correct syntax for pushing is -
factorsArray.push(i);
Also returning immediately from the function after finding the first divisor will not give you the full list. You probably want to return after you've found out all the divisors.
Taking all of the above into consideration, you should rewrite your function as follow -
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion - 1; i++){
if(numberInQuestion % i === 0) {
factorsArray.push(i);
}
}
}
and you will be OK.
You've coded this so that when you find the first factor your function returns immediately. Just get rid of the return keyword in that statement. (What "return" means in JavaScript and other similar languages is to immediately exit the function and resume from where the function was called.)
Oh, also, you call functions (like .push()) with parentheses, not square brackets.
The function should not return when pushing to the array. Return the array after executing the loop. The else clause is also unnecessary.
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
function factors(numberInQuestion){
var factorsArray = [];
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0 && isPrime(i)){
factorsArray.push(i);
}
}
return factorsArray;
};
var factors = factors(numberInQuestion);
alert(factors[factors.length-1]);
//From: http://stackoverflow.com/questions/11966520/how-to-find-prime-numbers
function isPrime (n)
{
if (n < 2) return false;
var q = Math.sqrt (n);
for (var i = 2; i <= q; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
Given the purpose of the example two items must be considered
The code does not determine if the number is actually prime. The code will return the smallest factor possible since the loop starts at two and increments, then returns the first element in the array. The largest factor would actually be the last element in the array. I have corrected the example to find the greatest prime factor. You can test it via this fiddle: http://jsfiddle.net/whKGB/1/

JavaScript: Improving the performance of a FOR Loop to stop the browser locking up?

I have an array of objects, I loop through this array one object at a time and I make a couple of checks to see if each object in that array meets certain criteria, if that object meets this criteria I then copy a property of this object in to an array (that property also contains another object).
for(var v = 0; features.length > v; v++){
var feature = features[v];
//If the feature is on the map then we want to add it
if(feature.onScreen()){
dataArray.push(feature.attributes);
}
}
Now for some reason if this array of objects is big (5000+) in size this operation becomes very expensive and the browser just locks up for a second or two (Some times more).
I cant go in to much more info of what the code does but I was wondering given this loop, what would be the best way to give the browser a break lets say every 500 iterations and so it doesn't lock up and then continue on etc.
Thanks
Not sure... but what about putting that code into a function, then breaking out and recalling the function (say every xMs)?
eg,
var LastPos = 0;
function DoLoop()
{
for(var v = LastPos; features.length > v; v++){
var feature = features[v];
//If the feature is on the map then we want to add it
if(feature.onScreen()){
dataArray.push(feature.attributes);
}
if(v > 500)
{
LastPos = v;
break;
}
}
setTimeout('DoLoop()', 10);
}
setTimeOut() can solve this.
var maxStatement = 1000, currentIndex=0, timeoutVar = ''
var sLimit = features.length
function multiStepLoop(){
for (var i=currentIndex; i<currentIndex+maxStatement; i++){
//---DO YOUR STUFFS HERE
}
var a = sLimit-i;
currentIndex += maxStatement;
if (maxStatement >= a){ maxStatement=a }
if (a<=0){
callBackFunction() //-- function to call when your peocess finish.
}else{
timeoutVar = setTimeout('multiStepLoop()',1)
}
}
multiStepLoop();
The drawback is that you need to cover all you want to do after this process complete into a function, then run it like a callBack_Function.
Note : Jus need to set the time for setTimeout to 1 millisecond & the browser won't display the waiting-cursor.
Change the structure of the loop to :
var index = 0;
function myfunc() {
while(features.length > index){
var feature = features[v];
//If the feature is on the map then we want to add it
if(feature.onScreen()){
dataArray.push(feature.attributes);
}
index++;
//You can do this
var waitingTime = 0;
If (index% 500=0) waitingTime=100; //or 10, 20...
//Or just with a short interval
var waitingTime = 10;
setTimeOut('myfunc', waitingTime);
}
}
Or with a parameter : [I prefer this one]
function myfunc(index) {
if(!index) {
index=0;
}
while(features.length > index){
var feature = features[v];
//If the feature is on the map then we want to add it
if(feature.onScreen()){
dataArray.push(feature.attributes);
}
//You can do this
var waitingTime = 0;
If (index% 500=0) waitingTime=100; //or 10, 20...
//Or just with a short interval
var waitingTime = 10;
setTimeOut('myfunc', waitingTime);
}
}
[EDIT]
Change setTimeOut calls...
And in your code, when you call the function, don't give the param, to initialize the index!
What if you did something with a little burst and a little recursion:
Fiddle Code
Main Function
What happens is a given index is passed with a burst amount of intervals (i.e. 500), upon ending the for loop at the given number of burst iterations, it recalls itself with the same burst number. The function ends when the end of the features array is completed.
var transferFeatures = function (index, burst) {
for (var z = 0; z <= burst; z++) {
if (index === features.length) {
return;
}
var feature = features[index];
if (feature.onScreen) {
dataArray.push(feature.attributes);
}
index++;
}
if (index !== features.length) {
transferFeatures(index, burst);
}
};
Testing
To simulate load, I created an array of objects using various key-value pairs (most importantly, your attributes inner object):
//To simulate a random boolean
var randomBoolean = function () {
return Math.random() <= 0.5;
};
//A random integer
var getRand = function () {
return (Math.floor(Math.random() * 10).toString());
};
// Create a bunch of dummy objects
var randomFeatures = function (arr, i) {
for (var p = 0; p < i; p++) {
arr.push({
onScreen: randomBoolean(),
attributes: {
width: getRand(),
height: getRand(),
someAtt: "I'm just an attribute",
coolKidsRideBikes: true,
foo: "bar",
bar: "baz"
}
});
}
};
Granted, it's not the same onScreen() test you will be using, but either way, it evaluates to a boolean value. I think if you apply this concept with your code, you could have amazing results.
Everything in the Fiddle I linked too at the top is called like this:
randomFeatures(features, 5000);
console.log(features.length);
transferFeatures(0,500);
console.log(dataArray.length);
Load Testing
I simulated 5000000 (5 million) random objects being pushed onto features with a burst of 1000 and the script completed in around 3.29 seconds.

Categories

Resources