Javascript challenge - which basket contains the last apple? - javascript

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.

Related

JavaScript game starts fast and slows down over time

My simple JavaScript game is a Space Invaders clone.
I am using the p5.js client-side library.
I have tried many things to attempt at speeding up the game.
It start off fast, and then over time it get slower, and slower, it isn't as enjoyable.
I do not mean to show every bit of code I have. I am not showing every class, but I will show the main class where everything is happening.
Could someone eyeball this and tell me if you see anything major?
I am new to JS and new to making games, I know there is something called update()
that people use in scripting but I am not familiar with it.
Thank you.
var ship;
var flowers = []; // flowers === aliens
var drops = [];
var drops2 = [];
function setup() {
createCanvas(600, 600);
ship = new Ship();
for (var i = 0; i < 6; i ++) {
flowers[i] = new Flower(i * 80 + 80, 60);
}
flower = new Flower();
}
function draw() {
background(51);
ship.show();
ship.move();
shipDrops();
alienDrops();
dropsAndAliens();
dropDelete();
drop2Delete();
}
// if 0 drops, show and move none, if 5, etc..
function shipDrops() {
for (var i = 0; i < drops.length; i ++) {
drops[i].show();
drops[i].move();
for (var j = 0; j < flowers.length; j++) {
if(drops[i].hits(flowers[j]) ) {
flowers[j].shrink();
if (flowers[j].r === 0) {
flowers[j].destroy();
}
// get rid of drops after it encounters ship
drops[i].evaporate();
}
if(flowers[j].toDelete) {
// if this drop remove, use splice function to splice out of array
flowers.splice(j, 1); // splice out i, at 1
}
}
}
}
function alienDrops() {
// below is for alien/flower fire drops 2
for (var i = 0; i < drops2.length; i ++) {
drops2[i].show();
drops2[i].move();
if(drops2[i].hits(ship) ) {
ship.shrink();
drops2[i].evaporate(); // must evap after shrink
ship.destroy();
if (ship.toDelete) {
delete ship.x;
delete ship.y;
} // above is in progress, deletes after ten hits?
}
}
}
function dropsAndAliens() {
var randomNumber; // for aliens to shoot
var edge = false;
// loop to show multiple flowers
for (var i = 0; i < flowers.length; i ++) {
flowers[i].show();
flowers[i].move();
// ******************************************
randomNumber = Math.floor(Math.random() * (100) );
if(randomNumber === 5) {
var drop2 = new Drop2(flowers[i].x, flowers[i].y, flowers[i].r);
drops2.push(drop2);
}
//**************** above aliens shooting
// below could be method, this will ensure the flowers dont
//go offscreen and they move
//makes whtever flower hits this space become the farther most
//right flower,
if (flowers[i].x > width || flowers[i]. x < 0 ) {
edge = true;
}
}
// so if right is true, loop thru them all again and reset x
if (edge) {
for (var i = 0; i < flowers.length; i ++) {
// if any flower hits edge, all will shift down
// and start moving to the left
flowers[i].shiftDown();
}
}
}
function dropDelete() {
for (var i = drops.length - 1; i >= 0; i--) {
if(drops[i].toDelete) {
// if this drop remove, use splice function to splice out of array
drops.splice(i, 1); // splice out i, at 1
}
}
}
function drop2Delete() {
for (var i = drops2.length - 1; i >= 0; i--) {
if(drops2[i].toDelete) {
// if this drop remove, use splice function to splice out of array
drops2.splice(i, 1); // splice out i, at 1
}
}
}
function keyReleased() {
if (key != ' ') {
ship.setDir(0); // when i lift the key, stop moving
}
}
function keyPressed() {
// event triggered when user presses key, check keycode
if(key === ' ') {
var drop = new Drop(ship.x, height); // start ship x and bottom of screen
drops.push(drop); // when user hits space, add this event to array
}
if (keyCode === RIGHT_ARROW) {
// +1 move right
ship.setDir(1);
} else if (keyCode === LEFT_ARROW) {
// -1 move left
ship.setDir(-1);
} // setir only when pressing key, want continuous movement
}
Please post a MCVE instead of a disconnected snippet that we can't run. Note that this should not be your entire project. It should be a small example sketch that just shows the problem without any extra code.
But to figure out what's going on, you need to debug your program. You need to find out stuff like this:
What is the length of every array? Are they continuously growing over time?
What is the actual framerate? Is the framerate dropping, or does it just appear to be slower?
At what point does it become slower? Try hard-coding different values to see what's going on.
Please note that I'm not asking you to tell me the answers to these questions. These are the questions you need to be asking yourself. (In fact, you should have all of these answers before you post a question on Stack Overflow!)
If you still can't figure it out, then please post a MCVE in a new question post and we'll go from there. Good luck.

Random lottery number generator

What I'm trying to do is generate 6 random numbers, five in a range of 1-45 and one in a range of 1-25 for a Greek lottery game (Tzoker). The first 5 numbers should be unique. By pressing a button, I want to add these numbers to a div using jQuery (I have some working code for this part).
I thought it would be pretty easy using a loop, but I've found myself unable to check if the number generated already exists. The loop would only contain the first 5 numbers, because the last number can be equal to one of the other 5.
Let me propose you some simpler solution.
Make a list of all numbers from 1 to 45.
Sort the list using Math.random (plus minus something, read the docs of Array.sort to find out) as the comparison function. You will get the list in random order.
Take 5 first items from the list.
Then, when you already have the numbers, put them all into your div.
This way you don't mix your logic (getting the numbers) with your presentation (putting stuff into the DOM).
I leave the implementation as an exercise for the reader. :)
Like this?
$(function() {
$('button').on('click', function(e) {
e.preventDefault();
var numArray = [];
while( numArray.length < 5 ) {
var number = Math.floor((Math.random() * 45 ) + 1);
if( $.inArray( number, numArray ) == -1 ) {
numArray.push( number );
}
}
numArray.push( Math.floor((Math.random() * 25 ) + 1) );
$('div').html( numArray.join("<br />") );
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Generate</button>
<div></div>
While this might be not exactly what you were asking for, if you would use lodash, this would be as simple as:
_.sample(_.range(1, 46), 5) // the 5 numbers from 1..45
_.random(1, 26) // one more from 1..25
This is why functional programming is so cool. You can read for example Javascript Allonge to find out more.
http://jsfiddle.net/015d05uu/
var tzoker = $("#tzoker");
var results = $("#results");
tzoker.click(function() {
results.empty();
var properResults = [];
var rand = 0;
var contains = false;
for (i = 1; i < 7; i++) {
do
{
(rand = Math.floor((Math.random() * (i != 6 ? 45 : 25)) + 1));
contains = properResults.indexOf(rand) > -1;
} while(contains)
results.append("<br />", rand, "<br />");
properResults.push(rand);
}
});
Here is the main idea of a solution. You can define the max value as a parameter for the random.
Then, check the existence of the item in a simple array with only the data you want.
You may use a general function which generates random numbers from 1 to maxValue, and adds them to an array only if they don't exist. Then, to display, cycle through the array items and append them to #randomNumbers.
HTML
<div id="randomNumbers"></div>
JS (with jQuery)
var randomNumbersArray = [];
$(function() {
generateRandomNumbers();
displayRandomNumbers();
});
function generateRandomNumbers() {
for (i = 0; i < 5; i++) {
generateRandomNumberFrom1To(45);
}
generateRandomNumberFrom1To(25);
}
function generateRandomNumberFrom1To(maxValue) {
var randomNumber;
do {
randomNumber = Math.ceil(Math.random() * maxValue);
} while ($.inArray(randomNumber, randomNumbersArray) > -1);
randomNumbersArray.push(randomNumber);
}
function displayRandomNumbers() {
for (i in randomNumbersArray) {
$("#randomNumbers").append(randomNumbersArray[i] + "<br>");
}
}

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.

Uncontrolled iteration in loop

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.

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.

Categories

Resources