I'm creating a slider with 6 slides, and I want to randomly move between them, making sure that neither of the previous two slides are shown as the next slide. The functionality doesn't really matter, since what I'm really doing is generating random numbers and keeping track of the previous two. The first slide is always numbered 1, so for the first two iterations that'll be one of the previous numbers that can't be used.
Here's what I have so far, and it works fine for generating the random numbers in the range, but 'caching' the last two values doesn't work reliably:
var rand = Math.floor(Math.random() * 6) + 1;
var prev1 = 1;
var prev2;
function randomSlide() {
// 5 second interval between slides
// Don't show either of previous two slides next
random = setInterval(function() {
prev2 = prev1;
prev1 = rand;
do {
rand = Math.floor(Math.random() * 6) + 1;
} while (rand == prev1 || rand == prev2);
prev1 = rand;
$('#slider').anythingSlider(rand);
//console.log(prev1,prev2);
}, 5000);
}
function firstSlide() {
firstTime = setTimeout(function() {
randomSlide();
}, 5000);
}
firstSlide();
randomSlide();
It's quite simple I think but my brain's getting frazzled trying to parse the values of the two 'cache' variables at the first, and then each subsequent, iteration.
I'm executing a single iteration at the beginning because if randomSlide() executes on load then the first (welcome) slide doesn't get a chance to display.
When you do the prev1 = rand the second time after you've changed the value of rand, you're assigning the new slide's number to it. The next time you enter the loop you do prev2 = prev1, and since prev1 == rand it means that now all three variables prev1, prev2 and rand are the same. Just remove the second prev1 = rand.
Another issue is that you set the interval twice: first you call firstSlide() which executes randomSlide() after a 5 second delay (which sets one interval), then right after you call randomSlide() again which sets another interval.
Here's another (simpler?) approach to getting the result:
<script>
// Return a random number from 1 to 6, exclude
// the last two numbers.
var getRandom = (function() {
var a = [1,2,3,4,5,6];
return function() {
var i = (Math.random() * 4 ) | 0;
a[5] = a.splice(i,1);
return a[5];
}
}());
function writeRandom() {
document.getElementById('d0').innerHTML += getRandom() + '<br>';
}
setInterval(writeRandom, 100)
</script>
<div id="d0"></div>
Not exactly random for the first 2 iterations, but you can fix that by randomising the array when it's initialised. But likely it doesn't matter for a slide show.
It's less code, but the splice part makes it slower in the browsers I tested. My version of the OP is:
var getRandom2 = (function() {
var r0 = r1 = r2 = 1;
return function() {
r0 = r1;
r1 = r2;
do {
r2 = Math.floor(Math.random() * 6) + 1;
} while (r2 == r0 || r2 == r1);
return r1;
}
}());
Related
I am making a Ludo game and my condition is if there are three 6 in a row then the user's chance will be passed to the next user. I made a random number from 1 to 6. I was storing the last digit with a variable called lastDice and compared the last one with a recent random number but I can not get the idea of the last 3 random numbers to compare. But the condition is if the last 3 random numbers are 6s(6,6,6) then the game must stop. Codes are as follows:
const playing = true;
const random = Math.floor(Math.random() * 6 + 1);
const lastDice = 0;
if (playing) {
if (random === 6 && lastDice === 6) {
document.getElementById('score--1').textContent = 0; // update the ui
} else {
nextPlayer(); // its a function to call the next player
}
lastDice = dice; // storing the last random number for comparing next random number
}
You can create a simple array, and use Math.random to get the index of the number to pick:
var lastNums = [0, 0, 0]; // 3rd last, 2nd last, and last number
var index = Math.floor(Math.random()*3);
var chosenNumber = lastNums[index];
// then compare
If you want to remove the first number and replace it with a new dice roll, simply use:
lastNums.shift(); // remove the 3rd last number
lastNums.push(lastDice); // add the current dice roll
Edit: If handling arrays are a bit too complex for you, here's another method using three variables:
// create 3 number variables
// I'll give the variables a non-zero value first
var firstNum = 1;
var secondNum = 2;
var thirdNum = 3;
// shuffle the number variables after each turn
// to set lastDice as previous dice roll
// here I treat firstNum as the last dice roll,
// secondNum as the second last dice roll,
// and thirdNum as the third last dice roll
thirdNum = secondNum;
secondNum = firstNum;
firstNum = lastDice;
Example: I rolled a 4. After each turn, when the numbers are shuffled, thirdNum gets replaced with secondNum, secondNum gets replaced with firstNum, firstNum gets replaced with lastDice. So I should have: firstNum = 4, secondNum = 1, thirdNum = 2.
I guess, I solved the problem. I added the random number to an array and used a condition if the array is more than 3 first index of the array will be removed. and then I compared the indexing value of an array with each other to see if all their value is equally 6. like the following code:
const random = Math.floor(Math.random()*6+1);
// container is an empty array called before
container.push(random);
//remove if conditon full fills
if(container.length >3){
container.shift();
};
console.log(container);
if(container[0] === 6 && container[1] === 6 && container[2] === 6){
scores[activePlayer] = 0;
document.getElementById('score--'+activePlayer).textContent = 0;
switchPlayer();
};
I ran into the challenge where I need a function that returns a random number within a given range from 0 - X. Not only that, but I require the number returned to be unique; not duplicating numbers that have already been returned on previous calls to the function.
Optionally, when this is done (e.g. the range has been 'exhausted'), just return a random number within the range.
How would one go about doing this?
This should do it:
function makeRandomRange(x) {
var used = new Array(x),
exhausted = false;
return function getRandom() {
var random = Math.floor(Math.random() * x);
if (exhausted) {
return random;
} else {
for (var i=0; i<x; i++) {
random = (random + 1) % x;
if (random in used)
continue;
used[random] = true;
return random;
}
// no free place found
exhausted = true;
used = null; // free memory
return random;
}
};
}
Usage:
var generate = makeRandomRange(20);
var x1 = generate(),
x2 = generate(),
...
Although it works, it has no good performance when the x-th random is generated - it searches the whole list for a free place. This algorithm, a step-by-step Fisher–Yates shuffle, from the question Unique (non-repeating) random numbers in O(1)?, will perform better:
function makeRandomRange(x) {
var range = new Array(x),
pointer = x;
return function getRandom() {
pointer = (pointer-1+x) % x;
var random = Math.floor(Math.random() * pointer);
var num = (random in range) ? range[random] : random;
range[random] = (pointer in range) ? range[pointer] : pointer;
return range[pointer] = num;
};
}
(Demo at jsfiddle.net)
Extended version which does only generate one "group" of unique numbers:
function makeRandomRange(x) {
var range = new Array(x),
pointer = x;
return function getRandom() {
if (range) {
pointer--;
var random = Math.floor(Math.random() * pointer);
var num = (random in range) ? range[random] : random;
range[random] = (pointer in range) ? range[pointer] : pointer;
range[pointer] = num;
if (pointer <= 0) { // first x numbers had been unique
range = null; // free memory;
}
return num;
} else {
return Math.floor(Math.random() * x);
}
};
}
(Demo)
You got some great programming answer. Here's one with a more theoretical flavor to complete your panorama :-)
Your problem is called "sampling" or "subset sampling" and there are several ways you could do this. Let N be the range you are sampling frame (i.e., N=X+1) and M be the size of your sample (the number of elements you want to pick).
if N is much larger than M, you'll want to use an algorithm such as the one suggested by Bentley and Floyd in his column "Programming Pearls: a sample of brilliance" (temporarily available without ACM's lock screen here), I really recommend this as they explicitly give code and discuss in terms of hash tables, etc.; there a few neat tricks in there
if N is within the same range as M, then you might want to use the Fisher-Yates shuffle but stop after only M steps (instead of N)
if you don't really know then the algorithm on page 647 of Devroye's book on random generation is pretty fast.
I wrote this function. It keeps its own array with a history of generated numbers, preventing initial duplicates, continuing to output a random number if all numbers in the range have been outputted once:
// Generates a unique number from a range
// keeps track of generated numbers in a history array
// if all numbers in the range have been returned once, keep outputting random numbers within the range
var UniqueRandom = { NumHistory: new Array(), generate: function(maxNum) {
var current = Math.round(Math.random()*(maxNum-1));
if (maxNum > 1 && this.NumHistory.length > 0) {
if (this.NumHistory.length != maxNum) {
while($.inArray(current, this.NumHistory) != -1) { current = Math.round(Math.random()*(maxNum-1)); }
this.NumHistory.push(current);
return current;
} else {
//unique numbers done, continue outputting random numbers, or we could reset the history array (NumHistory = [];)
return current;
}
} else {
//first time only
this.NumHistory.push(current);
return current;
}
}
};
Here's a working Fiddle
I hope this is of use to someone!
Edit: as pointed out by Pointy below, it might get slow with a large range (here is a
fiddle, going over a range from 0-1000, which seems to run fine). However; I didn't require a very large range, so perhaps this function is indeed not suited if you look to generate and keep track of an enormous range.
You may try generating the number using the current date and time value which would make it unique. To make it within the range, you may have to use some mathematical function.
I have a function. Every time I call the function, it should return a UNIQUE (for example, if I call this function 90 times, it should have given 90 different numbers) random number under 100.
I am currently doing
var randomNumber = Math.floor(Math.random() * 100);
But, it's not returning unique numbers. It's only returning random numbers.
Thanks in advance.
Edit:
It should return some alert after calling 100 times.
Make an array of a 100 numbers an cut the chosen number out each time you call it:
var unique = (function() { // wrap everything in an IIFE
var arr = []; // the array that contains the possible values
for(var i = 0; i < 100; i++) // fill it
arr.push(i);
return function() { // return the function that returns random unique numbers
if(!arr.length) // if there is no more numbers in the array
return alert("No more!"); // alert and return undefined
var rand = Math.floor(Math.random() * arr.length); // otherwise choose a random index from the array
return arr.splice(rand, 1) [0]; // cut out the number at that index and return it
};
})();
console.log(unique());
console.log(unique());
console.log(unique());
console.log(unique());
I'm building a Tic-Tac-Toe game.
I am trying to randomise which player gets to go first.
As the board is loaded, a random number between 1 and 2 is generated. If the number is 1, then player1 goes first, else player2 goes first. The currentPlayer is then set.
The function, currentPlayerFlag is then called. It will set the class of whichever player is the current to "active". The active player will then be flagged by lighting up their name.
For some reason, no matter how many times I reload the page, the starting player is always set to player1. Why is this?
No errors are being caught in the console.
Here is my code:
var currentPlayer;
var player1;
var player2;
// Grab original HTML and hold it as a variable
var originalHTML = document.body.innerHTML;
// When the page loads, the startup screen should appear.
window.onload = function() {
document.body.innerHTML = '<div class="screen screen-start" id="start"><header><h1>Tic Tac Toe</h1>Start game</header></div>';
document.querySelector('a').addEventListener("click", loadBoard);
};
// playerObject
function Player(name) {
this.name = name;
this.currentPlayer = false;
};
// Add programming, so that when the player clicks the start button the start screen disappears, the board appears, and the game begins.
function loadBoard() {
document.body.innerHTML = originalHTML;
player1 = new Player(player1);
player2 = new Player(player2);
var startingPlayerNum = Math.floor(Math.random() * 1) + 1 ;
if(startingPlayerNum = 1){
player1.currentPlayer = true;
currentPlayer = player1;
} else {
player2.currentPlayer = true;
currentPlayer = player2
}
//Add clickhandlers for boxes
var a1 = document.getElementById('a1').addEventListener("click", placePiece);
var a2 = document.getElementById('a2').addEventListener("click", placePiece);
var a3 = document.getElementById('a3').addEventListener("click", placePiece);
var b1 = document.getElementById('b1').addEventListener("click", placePiece);
var b2 = document.getElementById('b2').addEventListener("click", placePiece);
var b3 = document.getElementById('b3').addEventListener("click", placePiece);
var c1 = document.getElementById('c1').addEventListener("click", placePiece);
var c2 = document.getElementById('c2').addEventListener("click", placePiece);
var c3 = document.getElementById('c3').addEventListener("click", placePiece);
currentPlayerFlag()
};
// The current player is indicated at the top of the page -- the box with the symbol O or X is highlighted for the current player.
// Do this by simply adding the class .active to the proper list item in the HTML.
function currentPlayerFlag() {
if(currentPlayer === player1){
document.getElementById('player1').classList.add("active");
document.getElementById('player2').className = "players";
}
if(currentPlayer === player2){
document.getElementById('player2').classList.add("players active");
document.getElementById('player1').className = "players";
}
};
Please tell me what I'm doing wrong
The random number algorithm for JavaScript goes as follows:
Math.floor((Math.random() * max)) + min);
So, yours would be:
Math.floor((Math.random() * 2) + 1);
Yours is always returning 1 because your range was wrong, it was generating [1, 2), thus always returning 1. You must allow it to return [1, 3) my changing what you multiply Math.random() with from 1 to 2.
Your usecase:
var startingPlayerNum = Math.floor((Math.random() * 2) + 1);
The reason Math.floor(Math.random() * 1) + 1 returns 1 is because Math.random() generates a number from [0, 1). Then, it rounds to the floor (down) and adds one.
In your if statement, you assign it to one, so it's always 1. == is for comparison, = is for assignment. Try this if:
if(startingPlayerNum == 1){
player1.currentPlayer = true;
currentPlayer = player1;
} else {
player2.currentPlayer = true;
currentPlayer = player2
}
Math.random() returns values within the range of [0, 1). Math.floor() always rounds values down. Therefore, your Math.floor(Math.random() * 1) always returns 0.
You could use Math.round instead, or provide a wider range for your Math.floor.
Math.floor(Math.random() * 1) + 1 ;
would always return 1
It happens because Math.random() returns a value within [0; 1) range (0 inclusive, 1 exclusive).
So Math.random() * 1 is always between 0 and 1.
So Math.floor(Math.random() * 1) is always 0
I am trying to code the dice game Yahtzee in Javascript for fun. I have a basic interface set up with images of dice. In reference to my problem, a few Yahtzee rules to know would be:
There are 5 dice
After 3 rolls, the player has to pick a points category to score with
Here is the code that is not working:
var die1 = Math.floor((Math.random() * 6) + 1),
die2 = Math.floor((Math.random() * 6) + 1),
die3 = Math.floor((Math.random() * 6) + 1),
die4 = Math.floor((Math.random() * 6) + 1),
die5 = Math.floor((Math.random() * 6) + 1),
dieArray = ["", die1, die2, die3, die4, die5],
optionResult1 = 0;
document.getElementById("option1").onclick = function() {
if (rollCount == 3 & option1 == false) {
for (i=0; i < 5; i++) {
i++;
if (dieArray[i] == 1) {
optionResult1++;
}
if (i = 5) {
option1 = true;
document.getElementById("optionResult1").innerHTML = optionResult1;
console.log("finished");
}
}
console.log(optionResult1);
} else if (rollCount != 3) {
document.getElementById("dialogue").innerHTML = "You cannot pick this yet because you have not rolled 3 times.";
} else if (option1 == true) {
document.getElementById("dialogue").innerHTML = "You cannot pick this because you have already used this category.";
}
}
What should happen (assuming it's the third roll and this points category has not already been chosen previously):
The for loop should go through the dieArray and for each die that is a "one", add 1 to the optionResult1 variable. If there were three "ones" out of five dice, optionResult1 should be "3", etc.
What instead happens is that it usually comes back as 1 less than what it should be. Even if I can see in the console that the array clearly shows three "ones", it will give me "2", or sometimes "0". Is there anything wrong with the code you see? There is obviously other code in the document, but I am fairly certain the problem is within this function. Sorry if this was confusing, it's probably easier to understand if you have played Yahtzee...
I know that I could do the same thing with 5 if statements instead, but I am trying to learn from this and I am fairly confident this SHOULD work, I must just be doing something wrong. Thanks!