Why does this javascript while loop hang? - javascript

I was trying a very simple thing with javascript, to create a minesweeper grid.
gridsize=9;
//grid initialisation
var grid=Array(gridsize).fill(Array(gridsize).fill(null));
// this comes from <https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range>
function randint(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
nbombs=20;
insertedbombs=0;
while (insertedbombs<nbombs){
rx=randint(0,gridsize-1);
ry=randint(0,gridsize-1);
if (grid[rx][ry] == null){
insertedbombs++;
grid[rx][ry]='b';
}
}
The while loop hangs, both in Chrome and Firefox consoles, all the values of the grid are filled, not just 20, i've no idea why, i guess i have some wrong understanding of javascript language, because the same code in python works.
Working python code:
grid= [[None for i in range(9)]for i in range(9)]
nbombs=20;
insertedbombs=0;
while (insertedbombs< nbombs):
rx=random.randint(0,8)
ry=random.randint(0,8)
if (grid[rx][ry]== None):
grid[rx][ry]='b'
insertedbombs+=1

The issues is that Array#fill doesn't do what you think it does.
In your example you create a SINGLE array Array(gridsize).fill(null) and then you put that array at each index of the outer array. Meaning that your grid actually contains the same array, 9 times over. So when you assign grid[0][0] you actually assign grid[0][0], grid[1][0], grid[2][0], grid[3][0], etc... all at once (ish).
const gridSize = 9;
//grid initialisation
const grid = Array(gridSize).fill(Array(gridSize).fill(null));
console.log(grid[0] === grid[1]);
grid[0][0] = 10;
console.log(grid[0][0], grid[0][0] === grid[1][0]);
What you want to do, is first fill the array with some dummy value, like null. Then map over the array and replace each entry with its own copy of Array(gridsize).fill(null).
const gridSize = 9;
//grid initialisation
const grid = Array(gridSize)
.fill(null)
.map(() => Array(gridSize)
.fill(null)
);
const randInt = (min, max) =>
Math.floor(Math.random() * (max - min + 1)) + min;
const nbombs = 20;
let insertedbombs = 0;
while (insertedbombs < nbombs){
const rx = randInt(0, gridSize - 1);
const ry = randInt(0, gridSize - 1);
if (grid[rx][ry] === null){
insertedbombs += 1;
grid[rx][ry] = 'b';
}
}
console.log(grid);

Related

Why not unique numbers are still coming?

I need to generate the unique numbers, join it with the letter and push to array.
arr = []
for (var i = 0; i < 5; i++) {
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
var randomNum = generateRandomNumber(1, 15);
if (!arr.includes(randomNum)) {
arr.push('B' + randomNum.toString())
} else {
if (arr.length < 5) {
return generateRandomNumber(min, max)
} else break
}
}
I have provisioned uniqueness check, however the same values are still coming.
Condition only checked once in the loop.
Also if the condition is in the else state, there is a chance that there will also be same number as previous.
You can use this approach
let arr = []
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function add(){
var randomNum = generateRandomNumber(1, 15);
if(arr.length < 5){
if (!arr.includes('B' + randomNum.toString())) {
arr.push('B' + randomNum.toString())
}
add()
}
}
add()
console.log(arr)
I have provisioned uniqueness check, however the same values are still coming.
The uniqueness check is incorrect.
The array contains only strings starting with letter 'B' (added by the line arr.push('B' + randomNum.toString())) while the uniqueness check searches for numbers.
The call arr.includes(randomNum) always returns false. Each and every generated value of randomNum is prefixed with 'B' and pushed into the array, no matter what the array contains.
Try a simpler approach: generate numbers, add them to the list if they are not already there, stop when the list is large enough (five items).
Then run through the list and add the 'B' prefix to each item.
Like this:
function generateRandomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// Generate 5 unique random numbers between 1 and 15
let arr = []
while (arr.length < 5) {
let num = generateRandomNumber(1, 15);
// Ignore a value that has already been generated
if (!arr.includes(num)) {
arr.push(num);
}
}
// Add the 'B' prefix to the generated numbers
arr = arr.map((num) => `B${num}`);
console.log(arr);

Looking for more elegant way to solve this simple logic task

This is part of a bigger problem I try to solve in an exercise. It looks like this:
x is 10 times more likely to appear than y.
z appears 2x less often than y.
I solved this by calculating a single unit like this:
const x = 100;
const y = 10;
const z = 5;
const unit = 100 / (x + y + z);
unit equals 0.87
So when I do (0.87) + (0.87 * 10) + (0.87 * 5) I get 100%(almost)
Then I generate a random number between 0 and 1.
const randomNumber = Math.random();
function getValue() {
if (randomNumber <= 0.87) {
console.log('x');
} else if (randomNumber > 0.87 && randomNumber < 95.7) {
console.log('y');
} else console.log('z');
}
getValue();
If value<0.87 then I log out x, if value < 0.87+(0.087*10) I log y etc
Can anyone recommend a more logical and elegant way than this?
Your way looks clean for me except the fact that randomNumber > 0.87 is redundant.
if you store the value x, y and z in an array, you can probably write some cleaner code for example:
let prob = [100, 10, 5];
let sum = prob.reduce((a, b) => a + b, 0);
let normalizedProb = prob.map(p => p / sum);
let cummulativeProb = normalizedProb.map((cummulative => p => cummulative += p)(0));
for (let i = 0; i <= 50; i++) {
let r = Math.random();
console.log(cummulativeProb.filter(p => r >= p).length);
}
Also, you may want to read this post for faster implementation (in python though). However, the code will be more complicated for sure.
Since the weights are small integers, you can duplicate the x, y and z in an array, and just pick one random cell of the array:
let choices = "zyyxxxxxxxxxxxxxxxxxxxx";
console.log(choices[Math.floor(Math.random() * 23)]);
Here the magic number 23 is the number of choices, 1+2+20; and Math.floor(Math.random() * 23) is a random integer uniformly at random in range [0, 22] (both bounds included). See also:
Generating random whole numbers in JavaScript in a specific range?

Getting new colors randomly with loop

I am trying to randomize colors by generating random number, then applying
it to array to get an color array containing font-color and background-color.
At every "skill" I want to have unique color scheme. So each time I loop skill array I loop color array to fetch color scheme. If this color scheme number (which is same as the randomNumber) is already in use I random again. I do this with do/while loop. When color is not found it pushes it to usedColors array and paints the picture.
For some reason I am still getting same colors. I pasted two pictures to the bottom. Console.log image is about usedColors array (the randomly generated numbers)
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) == 0);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
inArray gives position of the matching element. So compare against -1, to know that element is not present in the usedColors array.
var usedColors = [];
$.each(knowledges, (i, knowledge) => {
do {
var r = Math.floor(Math.random() * Math.floor(colors.length)),
rColors = colors[r];
} while ($.inArray(r, usedColors) != -1);
usedColors.push(r);
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": rColors[0], "color": rColors[1]})
);
});
To generate array of unique numbers from certain interval you can do this.
In your case the range will be 0, arr.length - 1.
// function that will generate random unique random numbers between start
// and end, and store already generated numbers in closure
function generateUniqueRandom(start, end) {
const used = [];
function generateInner() {
let r;
while (!r) {
r = Math.floor(Math.random() * (end - start) + 1) + start;
if (used.includes(r)) {
r = null;
} else {
used.push(r);
}
}
return r;
}
return generateInner;
}
const random1 = generateUniqueRandom(0, 20);
const nums1 = [];
for (let i = 0; i < 10; i++) {
nums1.push(random1());
}
console.log(nums1);
const random2 = generateUniqueRandom(0, 20);
const nums2 = [];
for (let i = 0; i < 20; i++) {
nums2.push(random2());
}
console.log(nums2);
But you need to be careful not to generate more numbers that the specified range is, otherwise you will be stuck in an infinite loop.
In your while loop, are you checking if the array is unique? If so, it looks like you may not be using $.inArray correctly.
Put this in your while loop:$.inArray(r, usedColors) !== -1
jQuery.inArray(), how to use it right?
I think your loop method has many interactions, I mean your loop is traveling so much that it only ends until you find the random number that is not in the array (A short performance problem). An alternative method so that the array elements are random:
function shuffleArray(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
const colors = [["black","green"], ["white","blue"], ["pink","white"]];
let usedColors = shuffleArray(colors);
//You can now do this:
$.each(knowledges, (i, knowledge) => {
$("#knowledges div").append(
$("<p />").addClass("knowledge").text(knowledge).css({"background-color": usedColors[i][0], "color": usedColors[i][1]})
);
});

Conflict when trying to generate two separate numbers

I have the following script, which is meant to generate 2 different numbers
<script type="text/javascript">
function GenerateRandomNumber2to6No1() {
var min = 2, max = 7;
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return random;
}
var GenerateRandomNumber2to6No1 = GenerateRandomNumber2to6No1();
$('.GenerateRandomNumber2to6No1').html(GenerateRandomNumber2to6No1);
function GenerateRandomNumber2to6No2() {
var min = 2, max = 7;
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return (random !== GenerateRandomNumber2to6No1) ? random: GenerateRandomNumber2to6No2();
}
var GenerateRandomNumber2to6No2 = GenerateRandomNumber2to6No2();
$('.GenerateRandomNumber2to6No2').html(GenerateRandomNumber2to6No2);
</script>
NUMBER 1: <span class = "GenerateRandomNumber2to6No1"></span>
NUMBER 2: <span class = "GenerateRandomNumber2to6No2"></span>
This usually works fine but occasionally the page will load and no numbers will appear. I'm guessing this is when GenerateRandomNumber2to6No2 happens to generate the same number as GenerateRandomNumber2to6No1. I thought that
return (random !== GenerateRandomNumber2to6No1) ? random: GenerateRandomNumber2to6No2(); accounted for this, but perhaps instead of generating another unique number when there's redundancy, it generates no number at all. Is this the case? If so, how can I fix this?
Thanks!
function getRand(){
var array=[2,3,4,5,6,7]
var newArray=[];
for(var i=0;i<2;i++){
var ran=~~(array.length*Math.random());
newArray[i]=array.splice(ran,1)[0]
}
return newArray
}
var ranArray=getRand();
var ran1=ranArray[0];
var ran2=ranArray[1];
console.log(ran1,ran2)
getRand() is a function to give you an array of distinct numbers.Since you only need two,it only loop two times,and give you an array,which you can make use of it.
Back to your code,from what I see,Gno2 should keep running until it got another number,I don't know why.It's the time you try to debug.
First I will try to change the max=2 to max=3,if the code may not work for some number,then you can quickly realize and found out the problem is in these two functions.
Then try to log,console.log(Gno1,Gno2),see what exactly the numbers are,also help you to know where is the problem.
I think this piece of code can help you
var rand1,rand2;
var min = 3;
var max = 6;
do {
rand1 = Math.floor(Math.random() * (max - min)) + min;
rand2 = Math.floor(Math.random() * (max - min)) + min;
}while(rand1 === rand2);
$('.GenerateRandomNumber2to6No1').html(rand1);
$('.GenerateRandomNumber2to6No2').html(rand2);

Generating Two Numbers With a Specific Sum

I'm trying to generate two numbers with a specific sum. Here is my proposed method:
Edited: http://jsfiddle.net/KDmwn/274/
$(document).ready(function () {
function GenerateRandomNumber() {
var min = -13, max = 13;
var random = Math.floor(Math.random() * (max - min + 1)) + min;
return random;
}
var x = GenerateRandomNumber();
function GenerateRandomNumber2() {
var min2 = -13, max2 = 13;
var random2 = Math.floor(Math.random() * (max2 - min2 + 1)) + min2;
if ((random2 + x) == 0){
return random2};
}
var xx = GenerateRandomNumber2();
There's something wrong with the if ((random2 + x) = 0) line, as the code runs perfectly fine when it's removed. How can I modify this line so that the sum of the two numbers is 0? It would be most helpful if someone could modify the Jsfiddle that I've included. Thanks!
This is invalid:
if ((random2 + x) = 0){
You cannot assign something to an expression.
You probably meant to use the comparison operator (==), like this:
if ((random2 + x) == 0){
Are you trying to make it only output a second number that, when added to the first, equals 0? Because it actually already does that - but only if it gets it on the first try (try hitting refresh at least 30 times.) You need to tell it to keep re-choosing (looping) the second random number while the sum isn't 0:
function GenerateRandomNumber2() {
var min2 = -13,
max2 = 13;
var random2;
while ((random2 + x) !== 0) {
random2 = Math.floor(Math.random() * (max2 - min2 + 1)) + min2;
}
return random2;
}
http://jsfiddle.net/vL77hjp0/
To take this one step further, if I'm reading this right (if not ignore this) it looks like you might want to eventually choose a random sum and have it determine the required second number to be added. To do this, we would replace the 0 in our "while" loop with 'sum'. And 'sum' would have to be defined as a random number with a "max=x+13" and "min=x-13" (otherwise the random number may be too high/low for random2 to ever reach, causing the browser to crash.) [Or just remove the limits from random2.]
http://jsfiddle.net/fkuo54hc/
First, your GenerateRandomNumbers2 function returns undefined value other than in your if statement. So you need to return a value. I updated your fiddle and refactor some of your code.

Categories

Resources