Uncontrolled iteration in loop - javascript

Thats my code:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
if(plants.length != 0) {
for (var i=0; i<plants.length; i++) {
if (plants[i].left != xCord && plants[i].top != yCord) {
plants.push({"top": yCord, "left": xCord});
}
}
} else {
plants.push({"top": yCord, "left": xCord});
}
};
var multiplayer = function(func, endIteration, cells) {
for (var i=0; i<endIteration; i++) {
func(cells);
};
};
multiplayer(randomCoord, 5, 10) // will iterate diferent times
Function, multiplayer have to run "randomCoords" 5 times, but it's not working. Why quantity of iteration is uncontroled? How can I fix it?

It looks like your for loop in randomCoord() is supposed to be only pushing an entry into the array if the coordinates don't already exist in the array, but that isn't how your logic works. Instead, you check each and every item in the array and if it's not equal to that item in the array, you push it and you do that for each item in the array so you end up with lots of duplicates in the array (exactly what you're trying to prevent).
So, the first time you call randomCoord, you get one item. The next time you call it, you get two items. The third time you call it you get 4 items, then 8, then 16. This is a fairly simple logic error.
If you just want to add one unique item each time you call randomCoord, then you could use logic like this:
var randomCoord = function(cells) {
var step = $('.workplace').innerWidth()/cells;
var xCord = (Math.floor(Math.random() * (cells+1)))*step;
var yCord = (Math.floor(Math.random() * (cells+1)))*step;
var found = false;
for (var i=0; i<plants.length; i++) {
if (plants[i].left == xCord && plants[i].top == yCord) {
found = true;
break;
}
}
if (!found) {
plants.push({"top": yCord, "left": xCord});
}
};
Note, you don't need the separate if (plants.length != 0) because the for loop already checks that and our new found variable handles the case where the array is initially empty.
If you happen to generate a coordinate conflict, this will add no item on that function call though the odds of generating two conflicting random values are fairly low as long as cells*step is a decent size number (the range of your random number generator). If you want to try it again in that case, then you need another loop to try again if a conflict is found.

Related

How do I check if 2 numbers are the same from Math.random [duplicate]

Can't seem to find an answer to this, say I have this:
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
How do I make it so that random number doesn't repeat itself. For example if the random number is 2, I don't want 2 to come out again.
There are a number of ways you could achieve this.
Solution A:
If the range of numbers isn't large (let's say less than 10), you could just keep track of the numbers you've already generated. Then if you generate a duplicate, discard it and generate another number.
Solution B:
Pre-generate the random numbers, store them into an array and then go through the array. You could accomplish this by taking the numbers 1,2,...,n and then shuffle them.
shuffle = function(o) {
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var randorder = shuffle([0,1,2,3,4,5,6]);
var index = 0;
setInterval(function() {
$('.foo:nth-of-type('+(randorder[index++])+')').fadeIn(300);
}, 300);
Solution C:
Keep track of the numbers available in an array. Randomly pick a number. Remove number from said array.
var randnums = [0,1,2,3,4,5,6];
setInterval(function() {
var m = Math.floor(Math.random()*randnums.length);
$('.foo:nth-of-type('+(randnums[m])+')').fadeIn(300);
randnums = randnums.splice(m,1);
}, 300);
You seem to want a non-repeating random number from 0 to 6, so similar to tskuzzy's answer:
var getRand = (function() {
var nums = [0,1,2,3,4,5,6];
var current = [];
function rand(n) {
return (Math.random() * n)|0;
}
return function() {
if (!current.length) current = nums.slice();
return current.splice(rand(current.length), 1);
}
}());
It will return the numbers 0 to 6 in random order. When each has been drawn once, it will start again.
could you try that,
setInterval(function() {
m = Math.floor(Math.random()*7);
$('.foo:nth-of-type(' + m + ')').fadeIn(300);
}, 300);
I like Neal's answer although this is begging for some recursion. Here it is in java, you'll still get the general idea. Note that you'll hit an infinite loop if you pull out more numbers than MAX, I could have fixed that but left it as is for clarity.
edit: saw neal added a while loop so that works great.
public class RandCheck {
private List<Integer> numbers;
private Random rand;
private int MAX = 100;
public RandCheck(){
numbers = new ArrayList<Integer>();
rand = new Random();
}
public int getRandomNum(){
return getRandomNumRecursive(getRand());
}
private int getRandomNumRecursive(int num){
if(numbers.contains(num)){
return getRandomNumRecursive(getRand());
} else {
return num;
}
}
private int getRand(){
return rand.nextInt(MAX);
}
public static void main(String[] args){
RandCheck randCheck = new RandCheck();
for(int i = 0; i < 100; i++){
System.out.println(randCheck.getRandomNum());
}
}
}
Generally my approach is to make an array containing all of the possible values and to:
Pick a random number <= the size of the array
Remove the chosen element from the array
Repeat steps 1-2 until the array is empty
The resulting set of numbers will contain all of your indices without repetition.
Even better, maybe something like this:
var numArray = [0,1,2,3,4,5,6];
numArray.shuffle();
Then just go through the items because shuffle will have randomized them and pop them off one at a time.
Here's a simple fix, if a little rudimentary:
if(nextNum == lastNum){
if (nextNum == 0){nextNum = 7;}
else {nextNum = nextNum-1;}
}
If the next number is the same as the last simply minus 1 unless the number is 0 (zero) and set it to any other number within your set (I chose 7, the highest index).
I used this method within the cycle function because the only stipulation on selecting a number was that is musn't be the same as the last one.
Not the most elegant or technically gifted solution, but it works :)
Use sets. They were introduced to the specification in ES6. A set is a data structure that represents a collection of unique values, so it cannot include any duplicate values. I needed 6 random, non-repeatable numbers ranging from 1-49. I started with creating a longer set with around 30 digits (if the values repeat the set will have less elements), converted the set to array and then sliced it's first 6 elements. Easy peasy. Set.length is by default undefined and it's useless that's why it's easier to convert it to an array if you need specific length.
let randomSet = new Set();
for (let index = 0; index < 30; index++) {
randomSet.add(Math.floor(Math.random() * 49) + 1)
};
let randomSetToArray = Array.from(randomSet).slice(0,6);
console.log(randomSet);
console.log(randomSetToArray);
An easy way to generate a list of different numbers, no matter the size or number:
function randomNumber(max) {
return Math.floor(Math.random() * max + 1);
}
const list = []
while(list.length < 10 ){
let nbr = randomNumber(500)
if(!list.find(el => el === nbr)) list.push(nbr)
}
console.log("list",list)
I would like to add--
var RecordKeeper = {};
SRandom = function () {
currTimeStamp = new Date().getTime();
if (RecordKeeper.hasOwnProperty(currTimeStamp)) {
RecordKeeper[currTimeStamp] = RecordKeeper[currTimeStamp] + 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
else {
RecordKeeper[currTimeStamp] = 1;
return currTimeStamp.toString() + RecordKeeper[currTimeStamp];
}
}
This uses timestamp (every millisecond) to always generate a unique number.
you can do this. Have a public array of keys that you have used and check against them with this function:
function in_array(needle, haystack)
{
for(var key in haystack)
{
if(needle === haystack[key])
{
return true;
}
}
return false;
}
(function from: javascript function inArray)
So what you can do is:
var done = [];
setInterval(function() {
var m = null;
while(m == null || in_array(m, done)){
m = Math.floor(Math.random()*7);
}
done.push(m);
$('.foo:nth-of-type('+m+')').fadeIn(300);
}, 300);
This code will get stuck after getting all seven numbers so you need to make sure it exists after it fins them all.

Code review --- Random link with memory -- Javascript

var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var random = Math.round(Math.random() * 4);
var previous = [];
previous.push(random);
return previous
for (var i = 0; i < previous.length; i++) {
while (previous[i] == random) {
random = Math.round(Math.random() * 4);
}
}
window.location = links[random];
I'm trying to make a code that will be used to lead users to a random site from a set of sites. This will be activated by a button in google sites, I just haven't gotten to the html part. Anyways, when I try to run this code in jsfiddle, the output is just a blank screen. Whats wrong? here's my logic
An array of the set sites.
'random' picks a number between 0 and 4, which corresponds to the sites in the array
an empty array
This pushes 'random's output to the empty array
This for loop checks to see if there is any data in the empty array
While loop says "ok, if random chooses a number already in the array 'previous', I will run random again.
Once an unchosen number is outputted, a new window opens to the chosen site.
Sadly, it's not performing this way. Any tips?
Edit: Jsfiddle
I think a slight reworking of the code might do the trick with particular emphasis on removing the loop:
var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var previous = [];
function showLink() {
if (previous.length !== links.length) {
var random = Math.round(Math.random() * ((links.length - 1) - 0) + 0 );
if (previous.indexOf(links[random]) > -1) {
showLink();
} else {
console.log(links[random], previous)
previous.push(links[random]);
}
} else {
console.log('No more links');
}
}
And at this point just keep calling showLink until you run out of links.
Demo
var links = ["http://www.google.com/", "http://www.cnn.com/", "http://www.bbc.com/", "http://www.nbc.com/"];
var random = Math.round(Math.random() * 4);
var previous = [];
previous.push(random);
for (var i = 0; i < previous.length; i++) {
while (previous[i] == random) {
random = Math.round(Math.random() * 4);
}
}
window.location = links[random];
You were missing a bracket, and you had a return statement before your for loop. So of course it wasn't working since the code under the return statement was unreachable the way you had it written.

Small Straight (Yahtzee) Algorithm

I have created a working javascript function to check an array of 5 numbers for a small straight, in a Yahtzee game I'm making. I've tested it to no end and I'm confident it works 100% of the time, but it is also probably the worst algorithm of all time in terms of being efficient. Here is what it looks like:
function calcSmstraight() {
var sum = 0;
var r = new Array();
var r2 = new Array();
var counter = 0;
var temp;
var bool = false;
var bool2 = false;
r[0] = document.getElementById('setKeep1').value;
r[1] = document.getElementById('setKeep2').value;
r[2] = document.getElementById('setKeep3').value;
r[3] = document.getElementById('setKeep4').value;
r[4] = document.getElementById('setKeep5').value;
// Move non-duplicates to new array
r2[0] = r[0];
for(var i=0; i<r.length; i++) {
for(var j=0; j<r2.length; j++) {
if(r[i] == r2[j]) {
bool2 = true; // Already in new list
}
}
// Add to new list if not already in it
if(!bool2) {
r2.push(r[i]);
}
bool2 = false;
}
// Make sure list has at least 4 different numbers
if(r2.length >= 4) {
// Sort dice from least to greatest
while(counter < r2.length) {
if(r2[counter] > r2[counter+1]) {
temp = r2[counter];
r2[counter] = r2[counter+1];
r2[counter+1] = temp;
counter = 0;
} else {
counter++;
}
}
// Check if the dice are in order
if(((r2[0] == (r2[1]-1)) && (r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)))
|| ((r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)) && (r2[3] == (r2[4]-1)))) {
bool = true;
}
}
if(bool) {
// If small straight give 30 points
sum = 30;
}
return sum;
}
My strategy is to:
1) Remove duplicates by adding numbers to a new array as they occur
2) Make sure the new array is at least 4 in length (4 different numbers)
3) Sort the array from least to greatest
4) Check if the first 4 OR last 4 (if 5 in length) numbers are in order
My question:
Does anyone know a way that I can improve this method? It seems ridiculously terrible to me but I can't think of a better way to do this and it at least works.
Given that you're implementing a Yahtzee game you presumably need to test for other patterns beyond just small straights, so it would be better to create the array of values before calling the function so that you can use them in all tests, rather than getting the values from the DOM elements inside the small straight test.
Anyway, here's the first way that came to my mind to test for a small straight within an array representing the values of five six-sided dice:
// assume r is an array with the values from the dice
r.sort();
if (/1234|2345|3456/.test(r.join("").replace(/(.)\1/,"$1") {
// is a small straight
}
Note that you can sort an array of numbers using this code:
r2.sort(function(a,b){return a-b;});
...but in your case the values in the array are strings because they came from the .value attribute of DOM elements, so a default string sort will work with r2.sort(). Either way you don't need your own sort routine, because JavaScript provides one.
EDIT: If you assume that you can just put the five values as a string as above you can implement tests for all possible combinations as a big if/else like this:
r.sort();
r = r.join("");
if (/(.)\1{4}/.test(r)) {
alert("Five of a Kind");
} else if (/(.)\1{3}/.test(r)) {
alert("Four of a Kind");
} else if (/(.)\1{2}(.)\2|(.)\3(.)\4{2}/.test(r)) {
alert("Full House");
} else if (/(.)\1{2}/.test(r)) {
alert("Three of a Kind");
} else if (/1234|2345|3456/.test( r.replace(/(.)\1/,"$1") ) {
alert("Small Straight");
} // etc.
Demo: http://jsfiddle.net/4Qzfw/
Why don't you just have a six-element array of booleans indicating whether a number is present, then check 1-4, 2-5, and 3-6 for being all true? In pseudocode:
numFlags = array(6);
foreach(dice)
numFlags[die.value-1] = true;
if(numFlags[0] && numFlags[1] && numFlags[2] && numFlags[3]) return true
//Repeat for 1-4 and 2-5
return false
This wouldn't be a useful algorithm if you were using million-sided dice, but for six-siders there are only three possible small straights to check for, so it's simple and straightforward.
I do not play Yahtzee, but I do play cards, and it would appear the algorithm might be similar. This routine, written in ActionScript (my JavaScript is a bit rusty) has been compiled but not tested. It should accept 5 cards for input, and return a message for either straights greater than 3 cards or pairs or higher.
private function checkCards(card1:int,card2:int,card3:int,card4:int,card5:int):String
{
// Assumes that the 5 cards have a value between 0-12 (Ace-King)
//The key to the routine is using the card values as pointers into an array of possible card values.
var aryCardValues:Array = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
var aryCardNames:Array = new Array("Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King");
var strOutMessage:String;
var intCardCount:int = 0;
var strSeperator:String;
var strHighCard:String;
for (var i:int = 0;i < aryCardValues.length;i++)
{
//Check for runs of three of a kind or greater.
if (aryCardValues[i] >= 2)
{
strOutMessage = strOutMessage + strSeperator + i + "-" + aryCardNames[i] + "s";
strSeperator = " & ";
}
//Check for values in a straight.
if (aryCardValues[i] > 0)
{
intCardCount++;
if (intCardCount > 3)strHighCard = aryCardNames[i];
}
else
{
if (intCardCount < 3)intCardCount = 0;
}
}
if (intCardCount > 3) strOutMessage = intCardCount + " run " + strHighCard + " High."
return strOutMessage;
}
It may not be as concise as the regular expressions used above, but it might be more readable and easily modified. One change that could be made is to pass in an array of cards rather than discrete variables for each card.

Javascript challenge - which basket contains the last apple?

I'm presented with the following challenge question:
There are a circle of 100 baskets in a room; the baskets are numbered
in sequence from 1 to 100 and each basket contains one apple.
Eventually, the apple in basket 1 will be removed but the apple in
basket 2 will be skipped. Then the apple in basket 3 will be removed.
This will continue (moving around the circle, removing an apple from a
basket, skipping the next) until only one apple in a basket remains.
Write some code to determine in which basket the remaining apple is
in.
I concluded that basket 100 will contain the last apple and here's my code:
var allApples = [];
var apples = [];
var j = 0;
var max = 100;
var o ='';
while (j < max) {
o += ++j;
allApples.push(j);
}
var apples = allApples.filter(function(val) {
return 0 == val % 2;
});
while (apples.length > 1) {
for (i = 0; i < apples.length; i += 2) {
apples.splice(i, 1);
}
}
console.log(apples);
My question is: did I do this correctly? What concerns me is the description of "a circle" of baskets. I'm not sure this is relevant at all to how I code my solution. And would the basket in which the remaining apple reside be one that would otherwise be skipped?
I hope someone can let me know if I answered this correctly, answered it partially correct or my answer is entirely wrong. Thanks for the help.
So, ... I got WAY too into this question :)
I broke out the input/output of my last answer and that revealed a pretty simple pattern.
Basically, if the total number of items is a power of 2, then it will be the last item. An additional item after that will make the second item the last item. Each additional item after that will increase the last item by 2, until you reach another item count that is again divisible by a power of 2. Rinse and repeat.
Still not a one-liner, but will be much faster than my previous answer. This will not work for 1 item.
var items = 100;
function greatestPowDivisor(n, p) {
var i = 1;
while(n - Math.pow(p, i) > 0) {
i++;
}
return Math.pow(p, (i - 1));
}
var d = greatestPowDivisor(items, 2)
var last_item = (items - d) * 2;
I believe Colin DeClue is right that there is a single statement that will solve this pattern. I would be really interested to know that answer.
Here is my brute force solution. Instead of moving items ("apples") from their original container ("basket") into a discard pile, I am simply changing the container values from true or false to indicate that an item is no longer present.
var items = 100;
var containers = [];
// Just building the array of containers
for(i=0; i<items; i++) {
containers.push(true);
}
// count all containers with value of true
function countItemsLeft(containers) {
total = 0;
for(i=0; i<containers.length; i++) {
if(containers[i]) {
total++;
}
}
return total;
}
// what is the index of the first container
// with a value of true - hopefully there's only one
function getLastItem(containers) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
return(i);
}
}
// shouldn't get here if the while loop did it's job
return false;
}
var skip = false;
// loop through the items,
// setting every other to false,
// until there is only 1 left
while(countItemsLeft(containers) > 1) {
for(i=0; i<containers.length; i++) {
if(containers[i]) {
if(skip) {
skip = false;
} else {
containers[i] = false;
skip = true;
}
}
}
}
// what's the last item? add one to account for 0 index
// to get a human readable answer
var last_item = getLastItem(containers) + 1;
Needs error checking, etc... but it should get the job done assuming items is an integer.

Random Number with javascript or jquery

I am trying to make a script to pick random number between two numbers . but it picks same number sometimes. i donot want to repeat same number until array is finished .
Here is my code
$(document).ready(function () {
abc();
test = array();
function abc() {
res = randomXToY(1, 10, 0);
$('#img' + res).fadeTo(1200, 1);
//$(this).addClass('activeImg');
//});
setTimeout(function () {
removeClassImg(res)
}, 3000);
}
function removeClassImg(res) {
$('#img' + res).fadeTo(1200, 0.1);
//$('#img' + res).removeClass('activeImg');
abc();
}
function randomXToY(minVal, maxVal, floatVal) {
var randVal = minVal + (Math.random() * (maxVal - minVal));
return typeof floatVal == 'undefined' ? Math.round(randVal) : randVal.toFixed(floatVal);
}
});
Does Anybody have idea about this ...
You'll have to maintain a list of numbers that have already been generated, and check against this list. Re-generate a new number if you find a dupe.
If you do not want the random numbers repeating themselves you have to keep track of the some way.
If you have the range you are dealing with is relatively small, you can create an array with all possible results and simply randomly pick out of it.
function Randomizer(minVal, maxVal, floatVal){
var possible_results = []; // for larger arrays you can build this using a loop of course
var randomization_array = [];
var count = minVal;
var incrementor = floatVal || 1; // set the distance between possible values (if floatVal equals 0 we round to 1)
while (count <= maxVal) {
possible_results.push(count);
count += incrementor;
}
this.run = function(){
// if randomization_array is empty set posssible results into it
randomization_array = randomization_array.length ? randomization_array : $.merge(randomization_array, possible_results);
// pick a random element within the array
var rand = Math.floor(Math.random()*randomization_array.length);
// return the relevant element
return randomization_array.splice(rand,1)[0];
}
}
and in order to use it (it creates a specialized object for each possible range):
rand = new Randomizer(1,10,0);
rand.run();
note that this approach does not work well for very large ranges

Categories

Resources