I use a function that randomly selects another function, which works.
But sometimes it runs the same function twice or even more often in a row.
Is there a way to prevend this?
My current code:
window.setInterval(function(){
var arr = [func1, func2, func3],
rand = Math.floor(Math.random() * arr.length),
randomFunction = arr[rand];
randomFunction();
}, 5000);
Pretty simple so far. But how do I prevent func1 (for example) to run twice in a row
You can simply store the index of the last function called and the next time, get a random number which is not the last seen index, like this
var lastIndex, arr = [func1, func2, func3];
window.setInterval(function() {
var rand;
while ((rand = Math.floor(Math.random() * arr.length)) === lastIndex) ;
arr[(lastIndex = rand)]();
}, 5000);
The while loop is the key here,
while ((rand = Math.floor(Math.random() * arr.length)) === lastIndex) ;
Note: The ; at the end is important, it is to say that the loop has no body.
It will generate a random number and assign it to rand and check if it is equal to lastIndex. If they are the same, the loop will be run again till lastIndex and rand are different.
Then assign the current rand value to the lastIndex variable, because we dont't want the same function to be called consecutively.
How about a simple check to prevent picking an index that matches the previous pick?
var arr = [func1, func2, func3, ...];
var previousIndex = false;
var pickedIndex;
if (previousIndex === false) { // never picked anything before
pickedIndex = Math.floor(Math.random() * arr.length);
}
else {
pickedIndex = Math.floor(Math.random() * (arr.length - 1));
if (pickedIndex >= previousIndex) pickedIndex += 1;
}
previousIndex = pickedIndex;
arr[pickedIndex]();
This will pick a random function in constant time that is guaranteed to be different from the previous one.
You can select random values from 0-1. And after every run, swap the recently executed function in the array with the last element in the array i.e. arr[2].
var arr = [func1, func2, func3];
window.setInterval(function(){
var t, rand = Math.floor(Math.random() * (arr.length-1)),
randomFunction = arr[rand];
t = arr[rand], arr[rand] = arr[arr.length-1], arr[arr.length-1] = t;
randomFunction();
}, 5000);
Related
I've encountered a problem that's driving me crazy for like 10 days now... Basically i have a function that fire (the handleText) and I want to wait the end of that function before running a second one (the handleBackground).
I've tried deferred and promises but I think I don't understand them well because I can't get it to work the way I want.
function handleText(){
//BLAST INIT
var blastedwords = $('.baselineHeader').blast({
delimiter: 'word',
});
//SHUFFLE ORDER IN ARRAY
blastedwords = shuffleAds( blastedwords);
function shuffleAds(arr){
for(var j, x, i = arr.length; i; j = parseInt(Math.random() * i), x = arr[--i], arr[i] = arr[j], arr[j] = x);
return arr;
}
//ADD CLASS TO SPAN CREATED WITH BLAST WITH A RANDOM DELAY
blastedwords.each(function(index) {
var min = 45, max = 100;
var delay = Math.floor(Math.random() * (max - min) + min);
var that = $(this);
var t = setTimeout(function() {
$(that).addClass("test");
}, delay * index);
});
}
function handleBackground(){
$('.baselineHeader').addClass('processed');
}
handleText();
handleBackground();
Right now, handleText start, and handleBackground (my second function that I want to run after the first one is finished) fire at the same time and doesn't wait for handleText to finish.
I want handleText to run and wait for the each loop to give class to every span created with blast.js before running handleBackground.
Can someone help me with this ?
Have a great day folks :)
I'd recommend setting up a global variable totalTime which you can use to add up the inidivual delays. That way you can use another setTimeout to call the function handleBackground() after totalTime has passed.
var totalTime = 0;
blastedwords.each(function(index) {
var min = 45,
max = 100;
var delay = Math.floor(Math.random() * (max - min) + min) * index;
totalTime += delay;
var that = $(this);
var t = setTimeout(function() {
$(that).addClass("test");
}, delay);
});
setTimeout(handleBackground, totalTime);
Define delay outside of your function and each iteration increase the delay so that the next one is a random amout of time after the last one.
var delay = 0;
blastedwords.each(function(index) {
var min = 45, max = 100;
delay += Math.floor(Math.random() * (max - min) + min);
var that = $(this);
var t = setTimeout(function() {
$(that).addClass("test");
}, delay * index);
});
I have four different button effects where each effect are declared in a variable.
Therefore, I bring all of these four variables and place them within an array called arr in which is used in the clickByItself() function using Math.floor(Math.random()) methods.
Without the for loop, the code clicks by itself randomly in one of the four buttons every time I reload the page.
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
$(arr[random]).click();
}
However, using the for loop I am not being able to make these clicks one-by-one within the maximum of 10 times.
var blueButtonEffect = code effect here;
var redButtonEffect = code effect here;
var greenButtonEffect = code effect here;
var yellowButtonEffect = code effect here;
var arr = [blueButtonEffect, redButtonEffect, greenButtonEffect, yellowButtonEffect];
//will click on buttons randomly
function clickByItself() {
let random = Math.floor(Math.random() * arr.length)
var i;
for (i = 0; i < 10; i++) {
$(arr[random]).click();
setTimeout(clickByItself(), 1000);
}
}
The final output with the current code above is the four buttons being clicked at the same time, not one-by-one.
So, how can I have this function to press a random button by 10 times one-by-one with one second of interval from each click?
To fix your code you need:
A base case for your recursion
Pass a function reference to setTimeout. Currently, you are executing clickByItself and passing its return value (which is undefined) to setTimeout.
Do not use setTimeout in a loop without increasing the time by a factor of i, as the for loop will queue all the function calls at the same time
Alternatively, you can use a "times" argument to avoid looping
You could try something like
function clickByItself(times = 0) {
let random = Math.floor(Math.random() * arr.length)
$(arr[random]).click();
if (++times < 10) {
setTimeout(function(){clickByItself(times);}, 1000);
}
}
An example with console logs
https://jsfiddle.net/pfsrLwh3/
The problem is that the for loop calls the setTimeout 10 times very quickly. If you want to wait until the previous function call finishes prior to calling the next, then you should use recursion or just use a setInterval.
Recursion:
function clickByItself(numIterations) {
let random = Math.floor(Math.random() * arr.length)
let i;
$(arr[random]).click();
if( numIterations < 10 ){
setTimeout(() => {
clickByItself(numIterations += 1)
}, 1000)
}
}
clickByItself(0);
With setInterval
let numIterations = 0;
function clickByItself() {
let random = Math.floor(Math.random() * arr.length);
let i;
$(arr[random]).click();
numIterations += 1;
if( numIterations > 10) {
clearInterval(loop)
}
}
let loop = setInterval(test2, 1000);
Are you saying this is working for only 4 times but I think your above code will run in an infinite loop as you are calling clickByItself() again in the for loop.
If you want press a random button by 10 times one-by-one with one second of interval from each click then replace the for loop with
for (i = 0; i < 10; i++) {
setTimeout($(arr[random]).click(), 1000);
}
I am new to JavaScript, I have two roll functions for each roll of a frame. I am unable to get the values of each of those rolls into a frame function to call on and use. If someone could help this would be great! thanks in advance, My code is below.
var Bowling = function() {
var STARTING_TOTAL = 0;
ROLL_ONE = Math.floor(Math.random() * 11);
ROLL_TWO = Math.floor(Math.random() * 11);
this.score = STARTING_TOTAL;
var firstScore;
var secondScore;
var totalScore;
Bowling.prototype.firstRoll = function() {
firstScore = ROLL_ONE
return firstScore;
};
Bowling.prototype.secondRoll = function() {
secondScore = Math.floor(Math.random() * 11 - firstScore);
return secondScore;
};
Bowling.prototype.frameScore = function () {
totalScore = firstScore + secondScore
return totalScore;
};
};
I can only guess what you're trying to achieve. I refactored you code a little bit:
var Bowling = function () {
var STARTING_TOTAL = 0;
this.score = STARTING_TOTAL; // remains unused; did you mean "totalScore"?
this.firstScore = 0;
this.secondScore = 0;
};
Bowling.prototype.firstRoll = function () {
this.firstScore = Math.floor(Math.random() * 11);
return this.firstScore;
};
Bowling.prototype.secondRoll = function () {
this.secondScore = Math.floor(Math.random() * 11 - this.firstScore);
return this.secondScore;
};
Bowling.prototype.frameScore = function () {
this.totalScore = this.firstScore + this.secondScore
return this.totalScore;
};
// now use it like this:
var bowling = new Bowling();
console.log(bowling.firstRoll());
console.log(bowling.secondRoll());
console.log(bowling.frameScore());
In my approach however, firstScore and secondScore are public properties.
To address the question of why the second roll can be negative: as your code currently stands, if the second roll is smaller than the first roll, the result will be negative. If you want it so that if the first roll is 6, the second roll will be a number between 0 and 4, try something like:
function randomInt(maxNum) {
return Math.floor(Math.random() * maxNum)
}
var maxRoll = 11
var rollOne = randomInt(maxRoll)
var rollTwo = randomInt(maxRoll - rollOne)
console.log(rollOne)
console.log(rollTwo)
Press "Run Code Snippet" over and over again to see it work.
Changes I've made:
I made a function, randomInt that gives a random number from 0 to some max number. This saves you from needing to write the same code twice.
I created a variable maxRoll that keeps track of what the highest possible roll is.
I subtract maxRoll from the first roll to determine what the max number for the second roll should be (maxRoll - rollOne). That's then given to randomInt.
So I have a dice rolling function. I want to roll 2 dice 20 times, each time I roll, I want to add those numbers, and then I want to see how many times the sum appears, and put that number into an array.
function Dice() {
this.roll = function() {
var randomValue = Math.ceil(Math.random() * 6);
this.side = randomValue;
return this.side;
}
}
var dice1 = new Dice();
var dice2 = new Dice();
function getAmount() {
var finalArray = [];
function diceSum() {
var sum = dice1.roll() + dice2.roll();
return sum;
}
var trackSum = [];
for (var i = 1; i <= 20; i++) {
trackSum.push(diceSum());
}
var reduced = trackSum.reduce(function(acc, sum, i, arr) {
return acc.i += sum;
}, {});
return reduced;
}
so I get trackSum which has 20 numbers in an array which, each number is the sum of the 2 dice rolled. If sum = 2 and it appears 5 times, sum = 4, appears 2 times, sum = 3, appears 1 time, final array should look like
[5, 2, 1]
So far I tried a reduce method, filter, and forEach. I just can't figure out how to compare the numbers to see how many times it's appearing. Any help would be appreciated. Thanks!
Use an object whose keys are the sums, and values are the number of times the sum appears. Loop through trackSum and increment the value of the corresponding element.
var freq = {};
trackSum.forEach(function(sum) {
freq[sum] = freq[sum] ? freq[sum] + 1 : 1;
}
console.log(freq);
Apologies for answering my own question, but after some googling, I figured it out. So don't pay attention to the reduce method I have in my original post. This is what I did instead:
var countedSum = trackSum.reduce(function(emptyObj, sum, i, arr){
if (sum in emptyObj){
emptyObj[sum]++
} else {
emptyObj[sum] = 1
}
return emptyObj;
}, {});
This returns an object, with the sum value as the key, and the amount of times it happens as the value.
Then I learned a new method that grabs the values and puts it in an array.
Object.values(countedSum)
That solves it!
Thanks #Barmar for your help!
I'm trying to push a random number of bunnies into a canvas from 1~10 in Javascript.
However, the Math.random() method doesn't seem to be working. It just gives me one bunny. What am I doing wrong?
var field = [];
var randomNum = Math.floor(Math.random() * 10);
field.push(randomNum * new Bunny());
function Bunny() {
...
}
It won't give you any bunnies at all. randomNum * new Bunny() will be NaN1, because you're trying to multiply an object with a number.
If you want multiple bunnies, you have to create them, probably in a loop:
var field = [];
var randomNum = Math.floor(Math.random() * 10);
for (var n = 0; n < randomNum; ++n) { // Loop
field.push(new Bunny()); // creating and pushing
} // multiple bunnies
function Bunny() {
// ...
}
1 Or a number, if you've overridden valueOf on Bunny.prototype, which seems unlikely.
var field = [];
var randomNum = Math.floor(Math.random() * 10);
for (k=0; k<randomNum; k++)
{
field.push(new Bunny());
}