math.floor and math.radom are one index off - javascript

I'm building a small tic tac toe game.
When it is the computer turn to play, I make him pick a random number to pick an element from an array.
My problem is that the random number will be 3 for example, but the element picked from the array won't be arr[3], but arr[4].
it is a problem because if the number picked is the end of the array, it will return undefined.
here is the javascript code:
var grid = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7', 'item8', 'item9'];
var choice = 9;
function myFunction(clicked_id){
$('#' + clicked_id).html('X');
grid.splice(grid.indexOf(clicked_id), 1);
choice -= 1;
setTimeout(computer, 1000);
player.push(clicked_id);
findElement(player);
console.log(player);
console.log(grid);
console.log(grid.length)
}
function computer(){
var ran = Math.floor(Math.random() * choice);
var res = grid[ran - 1];
$('#' + res).html('O');
grid.splice(grid.indexOf(res), 1);
cpu.push(grid[ran]);
findElement(cpu);
choice -= 1;
console.log(ran);
console.log(cpu);
}
Here is what will be logged in the console log:
["item1"] -> What I clicked on
["item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9"] -> new modified array after using splice.
8 -> new array length
5 - random number picked by computer
["item8"] -> element picked by the computer in the array (arr[6])
'item6' is the box checked on the tic tac toe game.
Here is a link to my codepen to see the code in action.
https://codepen.io/nico3d911/pen/odvmPR?editors=0001
Thanks for your help!

Note that JS is using zero-based indexing - therefore item1 has index 0, item2 has index 1 etc. up to item9 with index 8.
Math.random() returns a number between 0 and 1 inclusive, which means that Math.random() * 9 can return 9 which is out of bounds - the maximum index is 8 for an array of length 9.
Changing the upper bound should fix your problem:
var ran = Math.floor(Math.random() * (choice - 1))
A small nitpick: grid.indexOf(res) is always equal to ran, you can just use grid.splice(ran, 1);

My issue was coming from the fact that I was using splice before pushing the array element into its new array.
Here is the correct javascript code:
function computer(){
var ran = Math.floor(Math.random() * choice);
var res = grid[ran];
$('#' + res).html('O');
cpu.push(grid[ran]); // this line needs to be before having the element removed.
grid.splice(grid.indexOf(res), 1);
findElement(cpu);
choice -= 1;
console.log(ran);
console.log(cpu);
}
The console log are just here to help me fixed the bug.
Thanks for your help!

Related

Creating an array that is consisted of unique numbers

I'm developing a simple game that allows user to generate from 1 to 5 Cat images from certain Cat Api. Then, after clicking start button the app generates shadow copies of those cats(with low opacity). Game will be later about dragging bottom images and fiting them to their shadow copies, that are randomly positioned(only then game makes sense). Then I'm planning make some futher features like time counter, points etc. etc. just for learning purposes.
But what am struggling with is creating a unique random number(that'll be index of particular cat) an will not be repeated during iteration...
Here is the code
const newArray = []; //
const catsArrayList = [...catBoardCopy.querySelectorAll('.cat')] //Array with cat images
function randomizeIndex() { // randomize an index number
let randomIndex = Math.floor((Math.random() * catsArrayList.length - 1) + 1);
console.log(randomIndex);
return randomIndex;
}
catsArrayList.forEach(catElement => { // here I am iterating over an array with cats which length is for example 5(this is max actually)
newArray.push(catsArrayList[randomizeIndex()]); // and pushing those elements with randomly generated index to the new array
})
newArray.forEach(newCat => {
shadowCatsContainer.appendChild(newCat); // here random cats are finally put to html container
})
And all of this work until the point when one of those random numbers is at least one time repeated... of course this happens actually 90% of time.
Im supposing it won't be simple solution to that. I tried so hard to make it work with different techniques, different loops, different array methods and nothing :( Also please take note that Im beginner so I need exhaustive guidance of what is going on :)
Have a nice day.
Your code is close; you can just remove the items that you're assigning to the new array from the source array so you don't use it twice.
const cats = [...catBoardCopy.querySelectorAll('.cat')]
function randomIndex() {
return Math.floor(Math.random() * cats.length);
}
cats.forEach(catElement => {
const index = randomIndex();
shadowCatsContainer.appendChild(cats[index]);
cats.splice(index, 1);
})
One option is to simply shuffle an array:
const cats = ['Tigger', 'Smokey', 'Kitty', 'Simba', 'Sassy'];
function shuffle(array, n = 500) {
const output = array.slice();
for (let i = 0; i < n; ++i) {
swap(output, getRandomInt(0, output.length), getRandomInt(0, output.length))
}
return output;
}
function swap(array, i, j) {
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
const shadowedCats = shuffle(cats);
console.log(cats);
console.log(shadowedCats);
console.log(shuffle(cats));
An example using array. Here I created an array with the possible numbers, it goes from 0 to the number of elements contained in the 'catsArrayList' array. If for example 'catsArrayList' has 3 elements, then the array with the possible numbers will be equal to: [0, 1, 2]
The idea now is to draw a random number from that array and then remove it from the list, and then we can go on repeating the process without getting repeated values.
e.g.
let catsArrayList = ['value1', 'value2', 'value3', 'value4', 'value5', 'value6'] // example
let numbers = [...Array(catsArrayList.length).keys()]
let lengthnumbers = numbers.length
for(let i = 1; i <= lengthnumbers; i++) {
let randoms = Math.floor(Math.random() * numbers.length)
console.log(i + 'ยบ number: ' + numbers.splice(randoms, 1))
}
Click on 'Run code snippet' a few times and you will see that you will get different, non-repetitive random numbers

JS Matching Game Randomizes all but Last Card

I've set up a simple matching game using JavaScript to gather the cards in an array and then randomly repopulate the page. Everything seems to be working great except for one thing; the last image always stays the same. I can't seem to figure out why.
Here's a link to the game: http://hdesigns.x10.mx/matching/
Any help would be greatly appreciated.
Math.random returns a number between 0 and .9 repeating. Never 1.
Change
var idx = Math.floor((Math.random() * (deck.length-1)));
to
var idx = Math.floor(Math.random() * deck.length);
on line 98 of main.js.
Try:
var idx = Math.floor(Math.random() * deck.length);
Instead of:
var idx = Math.floor((Math.random() * (deck.length - 1))));

wait for while loop to end before executing code

I am trying to retrieve data from an object, I generate 3 random numbers from 1-9 and then pick out data from a json object using these random numbers. However it sometimes works and then sometimes doesn't, I think it might be because it doesn't wait for the random numbers to be generated before selecting data from the object, it all occurs on page load:
the jsfiddle:
http://jsfiddle.net/dbqw79j4/1/
the code:
var jsonfile =[
{
"id" : "article1",
"image" : "http://images.domain.com/is/image/boss/BOSS_london_bridge_skyline?$c_overview_large$",
"headline" : "<h2>EIN TAG IN LONDON<span class='h2'>MIT LEWIS HAMILTON</span></h2>"
},
{
"id" : "article2",
"image" : "http://images.domain.com/is/image/boss/FAB_5819?$c_overview_large$",
"headline" : "<h2>EIN TAG IN MONACO<span class='h2'>MIT NICO ROSBERG</span></h2>"
},
...
]
var arr = []
var article1;
var article2;
var article3;
var art1hd;
var art1img;
var art2hd;
var art2img;
var art3hd;
var art3img;
while(arr.length < 3){
var randomnumber=Math.ceil(Math.random()*9)
var found=false;
for(var i=0;i<arr.length;i++){
if(arr[i]==randomnumber){found=true;break}
}
if(!found)arr[arr.length]=randomnumber;
}
console.log(arr);
console.log(arr[0]);
console.log(arr[1]);
console.log(arr[2]);
article1 = arr[0];
article2 = arr[1];
article3 = arr[2];
console.log(article1)
console.log(article2)
console.log(article3)
art1hd = jsonfile[article1]['headline'];
art1img = jsonfile[article1]['image'];
art2hd = jsonfile[article2]['headline'];
art2img = jsonfile[article2]['image'];
art3hd = jsonfile[article3]['headline'];
art3img = jsonfile[article3]['image'];
console.log(art1hd)
console.log(art1img)
console.log(art2hd)
console.log(art2img)
console.log(art3hd)
console.log(art3img)
You generate random numbers from range of 0-9 and your array contains only 9 elements and it is indexed from 0-8
You should use:
while(arr.length < 3){
var randomnumber=Math.ceil(Math.random()*8)
var found=false;
for(var i=0;i<arr.length;i++){
if(arr[i]==randomnumber){found=true;break}
}
if(!found)arr[arr.length]=randomnumber;
}
The problem is, your "jsonfile" array has nine elements. this breaks when you generate the random number 9, as arrays are zero-based, the valid values for indexing the array are 0-8
Math.ceil() is never the right function to generate an integer result based on Math.random() times something as this code does:
var randomnumber = Math.ceil( Math.random() * 9 );
You should always use Math.floor() in code like this instead. If you don't want your range to start with 0, then add the range base after doing the Math.floor().
In other words, if you want a random integer in the range 1 through 9 inclusive, this is the correct way to do it:
var randomnumber = Math.floor( Math.random() * 9 ) + 1;
Why is this? It's important to understand that Math.random() produces a value that is greater than or equal to 0, and less than (but never equal to) 1.
So Math.random() * 9 gives a value that is always less than 9 (and never equal to 9). If you take Math.floor() on that, you now have an integer in the range 0 through 8 inclusive.
Add 1 to that, and you have your desired range of 1 through 9.
Many JavaScript references fail to describe Math.random() clearly. Just keep in mind that its result is in the range 0 <= Math.random() < 1.
So, what could go wrong if you used Math.ceil()? Going back to the original example:
var randomnumber = Math.ceil( Math.random() * 9 );
What this code actually does is generates a number in the range 0 through 9, not 1 through 9. Now the chance of getting a 0 result is extremely small: it would be fairly rare for Math.random() to return 0, but it can happen. By using Math.floor() instead, you insure that the result is always in the desired range.
That said, as suvroc points out, you're (eventually) using this value as an index into an array of 9 elements, therefore the range you want is actually 0 through 8. So the code should be:
var randomnumber = Math.floor( Math.random() * 9 );
It is, because the random number generator can generate the number 9, but your jsonfile has only 9 elements, so the last index is 8.
First, as others said the random number generated as to be :
Math.floor(Math.random()*9)
Then I reviewed the code to be sure of synchronicity :
http://jsfiddle.net/dbqw79j4/6/
I did a recursive function who calls logs on arr.length >= 3 and add a random number if it doesn't exists on arr.

Making Math.random() more random?

I am slowly working my way through JavaScript and have run into an issue with Math.random().
I read on MDN that Math.random() is always seeded with the current Date - and I "believe" what is happening is that my two Math.random() calls are happening so quickly that they are returning basically the same value. Here is the code, this is the beginnings of a card game.
var cards = new Array(null);
cards[0] = new Array("Ace of Spades","ace","A","spades","black");
cards[1] = new Array("Two of Spades","two","2","spades","black");
cards[2] = new Array("Three of Spades","three","3","spades","black");
// you get the idea ... full code on the JSFiddle below
cards[51] = new Array("King of Hearts","king","K","hearts","red");
// initialize variables
var cardsInHand = 5;
// variables to make the card drawing decrement work without touching original array
var restOfDeck = new Array(null);
restOfDeck = cards;
// initialize hands as a doubly-nested Arrays
var player1Hand = new Array(null);
var player2Hand = new Array(null);
// function to randomly "draw" the cards, decrementing the deck each time
for (var k = 0; k < cardsInHand; k++) {
player1Hand[k] = restOfDeck[(Math.floor(Math.random() * restOfDeck.length))];
restOfDeck.splice(restOfDeck.indexOf(player1Hand[k]), 1);
console.log((k + 1) + ' cards to Player 1');
player2Hand[k] = restOfDeck[(Math.floor(Math.random() * restOfDeck.length))];
restOfDeck.splice(restOfDeck.indexOf(player2Hand[k]), 1);
console.log((k + 1) + ' cards to Player 2');
}
// display hand
document.write('Player 1\'s Hand\: <br />');
for (var i = 0; i < cardsInHand; i++) {
document.write(player1Hand[i][0] + "<br />");
}
document.write('<br /><br />Player 2\'s Hand\: <br />');
for (var j = 0; j < cardsInHand; j++) {
document.write(player2Hand[j][0] + "<br />");
}
What is happening that among the 10 cards "dealt", there are always two cards "in order" (Two of Hearts, then Three of Hearts). Always. I believe it has something to do with the two Math.random() calls in the draw/decrement function, but I am not sure how to rework. Thanks in advance!
JSFiddle here: http://jsfiddle.net/bjVCL/
This is just simple probability at work. The only time you won't see adjacent pairs is if the card values are distributed such that ...
1st card - can be any value
2nd card - must be one of 11 values not adjacent to previous card (11:13 odds)
3rd card - must be one of 9 values not adjacent to previous two
4th card - must be one of 7 values not adjacent to previous three
5th card - must be one of 5 values not adjacent to previous four
Thus, the odds of getting a hand with no adjacent values is (13/13) * (11 * 13) * (9 * 13) * (7 / 13) * (5 / 13) = a paltry 12%
(Note: 'my stastics is a little rusty so I may not have this quite right, but it's close enough to give a sense of the effect you're seeing.)

Remember last n items in jQuery

I have some code to randomly highlight one name from a list (this works - see this fiddle):
function pickRandom() {
var random = Math.floor(Math.random() * 6);
$('.stname').css('background','none').eq(random).css('background','yellow');
}
But I'd like to make sure that the same names don't come up over and over. So I intend to remember the last 3 chosen indexes as a blacklist:
var recentlyAsked = new Array();
function pickRandom() {
var random;
do {
random = Math.floor(Math.random() * 6);
} while ($.inArray(random,recentlyAsked));
recentlyAsked.push(random);
if (recentlyAsked.length >= 4) recentlyAsked.shift();
$('.stname').css('background','none').eq(random).css('background','yellow');
}
This is not working; see this fiddle. Warning: it causes the browser to hang.
Any suggestions, please?
do {
random = Math.floor(Math.random() * 6);
} while ($.inArray(random,recentlyAsked));
Runs forever because inArray returns -1 when an item is not found in the array, which is a truthy value. 0 is the only number that is a falsy value. Your array is initially empty so nothing is found.
Fix it with :
do {
random = Math.floor(Math.random() * 6);
} while ($.inArray(random,recentlyAsked) > -1);
This will stop when it returns -1(not found)
var ids=['a','b','c'];
var old=['d','e','f'];//At the beginning this will need populated with 3 random values
var ran=Math.floor(Math.random() * ids.length);
var ele=ids.splice(ran,1);
old.push(ele);
ids.push(old.shift());
highlight(ele);
Here is a slightly alternative way to do what you want. The idea is just remove chosen elements and then add it back in to the original array.
Just thought I would throw my code out there:
var randomArray = new Array(0, 1, 2, 3, 4, 5);
var pastArray = new Array();
function pickRandom() {
var random = Math.floor(Math.random() * randomArray.length);
$('.stname').css('background', 'none').eq(randomArray[random]).css('background', 'yellow');
if (pastArray.length < 3) {
pastArray.unshift(randomArray[random]);
randomArray.splice(random, 1);
} else {
pastArray.unshift(randomArray[random]);
randomArray.splice(random, 1, pastArray.pop());
}
console.log("possible values: [" + randomArray + "]");
console.log("past values: [" + pastArray + "]");
}
Values are moved back and forth from the current and past values. There is no need to prepopulate values as 'past', so it starts out truly random.

Categories

Resources