Javascript large array hangs - javascript
I've been working on a project for my class and I've completed it theoretically but it has some problems where it does not execute as fast as I'd like. The task is as follows: You have a 10x10 board to make moves on. You go up, down, left or right randomly. If a move takes you off the board skip it. Do this until 1,000,000 steps are taken or you reach the top right of the board. We are also supposed to count the max number of steps that a single tile received and the same with the minimum. I have done this using a 2D array and it counts sometimes and will output however it takes multiple button clicks to get an output. I'm not sure if this is a memory allocation error related to how I am accessing the 2D array to keep track of number of steps or not. I am relatively new to javascript so I don't know if my way was all that efficient.
Code
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Path Game</h1>
<!-- Starts Game -->
<button onclick="doGame()">Click Here to start Game</button>
<p id="MaxSteps"></p>
<p id="MinSteps"></p>
<p id="totalSteps"></p>
<p id="reachedSteps"></p>
<p id="reachedSquare"></p>
<p id="reachedBoth"></p>
<!-- JS -->
<script>
function doGame()
{
var gameBoard = [0,0];
var stepCount = 0;
//10x10 array to hold step counts (may be a better way. check back later)
//Cell counter
var cells = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0]
];
while(true)
{
var oneMove = Math.floor(1+(Math.random()*4));
//Conditional checks
//Check if both square and step counts are satisfied
if(gameBoard[0] == 9 && gameBoard[1] == 9 && stepCount == 1000000)
{
document.getElementById("reachedBoth").innerHTML = "Reached 1,000,000 steps and top square";
break;
}
//Reached Top right before 1,000,000 steps
else if(gameBoard[0] == 9 && gameBoard[1] == 9)
{
document.getElementById("reachedSquare").innerHTML = "Reached Top right square";
break;
}
//Reached 1,000,000 steps before top right
else if(stepCount == 1000000)
{
document.getElementById("reachedSteps").innerHTML = "Reached 1,000,000 steps";
break;
}
//Movement on the board
var x = gameBoard[0];
var y = gameBoard[1];
cells[x][y] += 1;
//Move left
if(oneMove == 1)
{
//Initialized at 1 so less than is suitable
if(gameBoard[0] < 0)
{
gameBoard[0] = 0; //Reset
}
else{
gameBoard[0]--;//Goes left
}
}
//Move right
else if(oneMove == 2)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[0] >= 9)
{
gameBoard[0] = 9; //Reset
}
else{
gameBoard[0]++;//Goes right
}
}
//Move up
else if(oneMove == 3)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[1] >= 9)
{
gameBoard[1] = 9; //Reset
}
else{
gameBoard[1]++;//Goes up
}
}
//Move down
else if(oneMove == 4)
{
//Initialized at 1 so less than is suitable
if(gameBoard[1] < 0)
{
gameBoard[1] = 0; //Reset
}
else{
gameBoard[1]--;//Goes down
}
}
stepCount++; //Count the steps
}
var max = 0;
var min = Infinity;
//Find max
for(var i = 0; i < cells.length;i++)
{
for(var j = 0; j < cells[i].length; j++)
{
if(max < cells[i][j])
{
max = cells[i][j];
}
}
}
//Find min
for(var i = 0; i < cells.length;i++)
{
for(var j = 0; j < cells[i].length; j++)
{
if(min > cells[i][j])
{
min = cells[i][j];
}
}
}
//Total Steps print
document.getElementById("MaxSteps").innerHTML = "Max steps were: " + max;
document.getElementById("MinSteps").innerHTML = "Min steps were: " + min;
document.getElementById("totalSteps").innerHTML = "Total steps were: " + stepCount;
}
</script>
</body>
</html>
This block is the one that strikes me as particularly inefficient:
if(oneMove == 1)
{
//Initialized at 1 so less than is suitable
if(gameBoard[0] < 1)
{
gameBoard[0] = 1; //Reset
}
else{
gameBoard[0]--;//Goes left
}
}
//Move right
else if(oneMove == 2)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[0] >= 10)
{
gameBoard[0] = 10; //Reset
}
else{
gameBoard[0]++;//Goes right
}
}
//Move up
else if(oneMove == 3)
{
//If its at the edge, keep it there or keeps from going over
if(gameBoard[1] >= 10)
{
gameBoard[1] = 10; //Reset
}
else{
gameBoard[1]++;//Goes up
}
}
//Move down
else if(oneMove == 4)
{
//Initialized at 1 so less than is suitable
if(gameBoard[1] < 1)
{
gameBoard[1] = 1; //Reset
}
else{
gameBoard[1]--;//Goes down
}
}
This is for a class, so I won't offer you an answer directly, but can you think of a solution that instead of incrementing or decrementing your gameboard counters directly using the random value that was generated?
For example, if I had a simple 1-dimension gameboard like so:
var gameboard = [0,0,0];
var position = 0,
direction = 0;
function move() {
direction = Math.round(Math.random()) * 2 - 1;
position += direction;
}
The only thing that is missing is to account for the possibility that I have moved off the gameboard. If the requirement were to start your marker on the other side of the board (think PacMan), this could also be accomplished using the modulo operator, which in JS is %:
function move() {
direction = Math.round(Math.random()) * 2 - 1;
position += direction;
position = (position + 3) % 3;
}
Unfortunately, given your requirements, I don't see a way around if conditions to stay on the board:
position = position < 0 ? 0 : position;
position = position > 2 ? 2 : position;
Hopefully this should get you going in the right direction. The above three lines of code could actually be combined into one line, though I prefer to make them a bit more readable.
A few more notes:
Storing your x and y positions in a two-element array called
gameBoard is just confusing. Just call them x and y (as you do at the
end of your code).
Though the requirements don't call for it, try
generating the game board and performing all math so that instead of
10 elements, the game could be changed to n elements by changing only
one value in your code. It's good to have the smallest set of
controls as possible.
Good luck with your class!
Related
Counting up and then back down in a for loop javascript
Function oddNumbers(){ Var odds =""; For(index=0; index <=10; index ++){ If(index%2 !=0){odds+=index +"."} } } OddNumbers(); Trying to count all odd counting numbers up then back down again in a for loop. I can get it to go up with the code again. But when I try nested for I cannot get it to go back down again in the same loop. How would I get it to loop up then back down?
Assuming you want to use only one loop definition, you have to define custom conditionExpression and incrementExpression. Here is a quick example. var direction = 1; //REM: What gets added on each iteration? function incrementExpression(){ //REM: Increase or decrease depending on direction return 1*direction } //REM: What gets evaluated on each iteration? function conditionExpression(i){ if(i < 10 && direction === 1){ return true } if(i === 10 && direction === 1){ direction = -1 return true } if(i >= 0 && direction === -1){ direction = -1 return true }; //REM: Returns false if direction downwards and i lower zero return false } for(var i=0; conditionExpression(i); i+=incrementExpression(i)){ console.log(i) }
You can reverse your for loop on your use-case. See code snippet below: function oddNumbers() { var odds = ""; for(index=0; index <=10; index ++){ if(index%2 !=0) { odds+=index +"." console.log(odds); } } for(index = 10; index > 0; index--) { if(index % 2 != 0) { odds+=index +"." console.log(odds); } } } oddNumbers(); Im not sure if that's what you want but that's what I understand on your given code on your question.
Having issues trying to solve N Rook problem . Always get n*n solution and not N factorial
I'm trying to get N ways of solves a N rook problem. The issue I am having is currently, I seem to get n*n solutions while it needs to be N! . Below is my code, I have written it in simple loops and functions, so it's quite long. Any help would be greatly appreciated Note: Please ignore case for n = 2. I get some duplicates which I thought I would handle via JSON.stringify var createMatrix = function (n) { var newMatrix = new Array(n); // build matrix for (var i = 0; i < n; i++) { newMatrix[i] = new Array(n); } for (var i = 0; i < n; i++) { for (var j = 0; j < n; j++) { newMatrix[i][j] = 0; } } return newMatrix; }; var newMatrix = createMatrix(n); // based on rook position, greying out function var collision = function (i, j) { var col = i; var row = j; while (col < n) { // set the row (i) to all 'a' col++; if (col < n) { if (newMatrix[col][j] !== 1) { newMatrix[col][j] = 'x'; } } } while (row < n) { // set columns (j) to all 'a' row++; if (row < n) { if (newMatrix[i][row] !== 1) { newMatrix[i][row] = 'x'; } } } if (i > 0) { col = i; while (col !== 0) { col--; if (newMatrix[col][j] !== 1) { newMatrix[col][j] = 'x'; } } } if (j > 0) { row = j; while (row !== 0) { row--; if (newMatrix[i][row] !== 1) { newMatrix[i][row] = 'x'; } } } }; // checks position with 0 and sets it with Rook var emptyPositionChecker = function (matrix) { for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix.length; j++) { if (matrix[i][j] === 0) { matrix[i][j] = 1; collision(i, j); return true; } } } return false; }; // loop for every position on the board loop1: for (var i = 0; i < newMatrix.length; i++) { var row = newMatrix[i]; for (var j = 0; j < newMatrix.length; j++) { // pick a position for rook newMatrix[i][j] = 1; // grey out collison zones due to the above position collision(i, j); var hasEmpty = true; while (hasEmpty) { //call empty position checker if (emptyPositionChecker(newMatrix)) { continue; } else { //else we found a complete matrix, break hasEmpty = false; solutionCount++; // reinitiaze new array to start all over newMatrix = createMatrix(n); break; } } } }
There seem to be two underlying problems. The first is that several copies of the same position are being found. If we consider the case of N=3 and we visualise the positions by making the first rook placed red, the second placed green and the third to be placed blue, we get these three boards: They are identical positions but will count as 3 separate ones in the given Javascript. For a 3x3 board there are also 2 other positions which have duplicates. The gets the count of unique positions to 9 - 2 - 1 -1 = 5. But we are expecting N! = 6 positions. This brings us to the second problem which is that some positions are missed. In the case of N=3 this occurs once when i===j==1 - ie the mid point of the board. This position is reached: This position is not reached: So now we have the number of positions that should be found as 9 - 2 - 1 - 1 +1; There appears to be nothing wrong with the actual Javascript in as much as it is implementing the given algorithm. What is wrong is the algorithm which is both finding and counting duplicates and is missing some positions. A common way of solving the N Rooks problem is to use a recursive method rather than an iterative one, and indeed iteration might very soon get totally out of hand if it's trying to evaluate every single position on a board of any size. This question is probably best taken up on one of the other stackexchange sites where algorithms are discussed.
Having trouble displaying results correctly after changing the value of an Ace from 11 to 1 in a blackjack simulation
This is the first time I've written anything beyond a few simple lines in javascript. I know similar questions have been asked before because I've looked at a lot of them. I still can't seem to figure out what I'm doing wrong though. I am attempting to make a blackjack game simulation. I've almost got it except I keep running into problems when I need to change the value of an ace from 11 to 1. When I need to change the value of an ace from 11 to 1, it seems to only display the last player's results. Here's the script in it's entirety: function cardShuffle(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } function add(a, b) { return a + b; } function contains(arr, obj) { var i = 0; for (;i < arr.length;i++) { if (arr[i] === obj) { return true; } } return false; } function blackjack() { var spades = "\u2660"; var clubs = "\u2663"; var hearts = "\u2665"; var diamonds = "\u2666"; var dealer = "Dealer"; var player = "Player"; var table = [player, dealer]; var suits = [hearts, clubs, diamonds, spades]; var deck = []; // create a deck of cards x = 0; for (;x < suits.length; x++) { var cards = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"]; i = 0; for (;i < cards.length; i++) { deck.push(cards[i] + suits[x]); } } i = 0; for (;i < table.length; i++) { var players = table[i]; var cardsToDeal = 1; var cardsHeld = []; var sum = []; aces = []; var result; for (;0 < cardsToDeal;) { // get a random card from the deck var index = cardShuffle(0, deck.length - 1); var card = deck[index]; // give the card to the player cardsHeld.push(card); // remove the card from the deck deck.splice(index, 1); // strip the suit from the card and get the value var value = card.substring(0, card.length - 1); if (value == "J" || (value == "Q" || value == "K")) { value = 10; } else { if (value == "A") { value = 11; // put aces into a separate array aces.push(value); } else { value = Number(value); } } // put the value of the card into the sum array sum.push(value); // store the sum of the value in the total variable var total = sum.reduce(add, 0); /* This is where I think the problem is. It works if the last player's hand is greater than 21 and contains an ace. But if the first players hand has an ace and is greater than 21 only those result are shown in console.log. I don't know why. */ if (total > 21 && contains(sum, 11)) { var i = sum.indexOf(11); if (i !== -1) { sum[i] = 1; } total = sum.reduce(add, 0); } if (total >= 17 && total <= 21) { result = Number(total); } if (total >= 17) { cardsToDeal--; } } console.log(players, cardsHeld, sum, total); } } blackjack();
On this line: for (;i < table.length; i++) { ... you use a variable i for a loop. But inside this loop, you redefine that variable, and change its value: var i = sum.indexOf(11); which results in this kind of behavior: If you just change that variable's name to something else, it seems to work fine: if (total > 21 && contains(sum, 11)) { var index = sum.indexOf(11); if (index !== -1) { sum[index] = 1; } total = sum.reduce(add, 0); } Be careful about variable scopes when using the var keyword, they are not limited to blocks -unlike let- but to functions (and be sure to add a var in front of the first definition, to avoid making it global). Also, just a piece of advice. for loops are generally used when you know how many iterations you're going to do. This: for (;0 < cardsToDeal;) { /*...*/ } would be better with a while loop.
When you check for Aces, you declare i again. This changes the value of i in your table loop. Change the the second var i to var ii and you should be good. To better understand this, you can look up more about Javascript scope. if (total > 21 && contains(sum, 11)) { var ii = sum.indexOf(11); // This is what changed if (ii !== -1) { sum[ii] = 1; } total = sum.reduce(add, 0); }
don't let adverts appear near each other jquery
Here is my DEMO jQuery(document).ready(function(){ var insertionTemplate = jQuery(".hiddenAdBox").eq(0).html(), insertionTarget = jQuery('ul'), insertionTargetChildren = insertionTarget.find('li'), insertionFrequency = 2; var random; for (var i = 0; i < insertionFrequency; i++) { random = Math.floor(Math.random() * insertionTargetChildren.length) + 0; insertionTargetChildren.eq(random).after(insertionTemplate); } }); I have list of items, also need to show ads randomly when you refresh page. Now when ads count is 2 (insertionFrequency = 2) there is a time when they appear near each other but it shouldn't be so. How can I detect it and don't let ads appear near each other? Attaching screenshots:
To prevent ads horizontally you can do it as below: for (var i = 0; i < insertionFrequency; i++) { random = Math.floor(Math.random() * insertionTargetChildren.length) + 0; if(insertionTargetChildren.eq(random).next('.placeforad').length || insertionTargetChildren.eq(random).prev('.placeforad').length) //check whether its previous li or next li has ad with class .placeforad { i--; //decrease the count so as to come up with separate random number } else insertionTargetChildren.eq(random).after(insertionTemplate); } DEMO But for vertical prevention since you do not have equal amount of squares in each row like 1st and 2nd rows have 3 blocks and last row has only 2, its very tricky to identify horizontal. If there equal amount of squares then some approach might be taken to achieve that too.. Update Since you agreed to get 3 blocks in a row the below code will avoid getting ads horizontally as well as vertically A small DEMO for (var i = 0; i < insertionFrequency; i++) { random = Math.floor(Math.random() * insertionTargetChildren.length) + 0; var currentElement=insertionTargetChildren.eq(random); var addedElement=$('li.placeforad:visible'); if(currentElement.next('.placeforad').length || currentElement.prev('.placeforad').length) { i--; } else { if(i!=0) { if(addedElement.index() > currentElement.index() && (addedElement.index() - currentElement.index())==3) { i--; } else if(currentElement.index() - (addedElement.index()-1)==3) { i--; } else { insertionTargetChildren.eq(random).after(insertionTemplate); } } else insertionTargetChildren.eq(random).after(insertionTemplate); } }
Javascript Smallest Multiple Keeps Crashing
I am currently going through project euler trying to get better at javascript. I am stuck on one problem that i can't seem to figure out. The question is: 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? So I searched stack and found a few similar topics when i got stuck but everyones code seems to be a bit more complex than what I came up with. My problem is i was able to create the correct program to find the smallest multiple from 1-10 which i was able to get 2520 with. However, when i adjust my program to work for numbers 1-20 it crashes. I can get 1-19 but once i do 20 i keep crashing. Here is my code. If you change it to count != 10 and i <= 10 you can get the answer 2520 but 20 just doesn't seem to work. Any help would be appreciated. <script> var num = 0; var count = 0; while (count != 20) { num++; count = 0; for (var i = 1; i <= 20; i++) { if (num % i == 0) { count++; } } } console.log(num); console.log(count); </script>
It doesn't crash, just takes long time, since your script is inefficient. <script> var num = 0; var count = 0; var numbers=20; while (count != numbers) { num+=numbers; count = 0; for (var i = 1; i <= numbers; i++) { if (num % i == 0) { count++; } else {break;} } } console.log(num); console.log(count); </script>
Yeah, not breaking, just taking a REALLY long time. This should be a much more efficient approach: var num = 1; var isDivisible = false; var startTime = (new Date().getTime()); while (!isDivisible) { for (var i = 20; i > 1; i--) { if (num % i !== 0) { isDivisible = false; num = num + (i + 1); break; } else { isDivisible = true; } } } console.log(num); console.log("Finished in " + (((new Date().getTime()) - startTime) / 1000) + " seconds"); Results: 232792560 Finished in 0.166 seconds Note: above results from JSFiddle . . . finished in 26.57 seconds in Firebug. :)