• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Is there a limit to how many "For" loops you can have

W

WimpyLlama

Guest
I was working on a placing system for blocks, and I needed to make it so you couldn't place certain ID numbers, which were items. The placing system already had 2 "for" loops, but when I added the 3rd for the non-placing of items, it did something strange. I could still place down the items, which was bad, so to debug I put a debug message in the for loop that went through 2 numbers. The debug message would tell me the two number.s It said 10101010 which is what I wanted, so when I tried and placed the debug message in the next for loop, it said only 000000. I don't know why it changed. It should have not. I can get past all this just by making some && and if statements every time I add a new item, but if I did, this script would get rather large. And I wanted to make it as simple as possible to make new items because I was thinking of making an in-game item adder for the players to basically make mini-mods. I can't seem to figure this out. Please help.
 

RollyBug

Member
There is a limit, I don't really know what it is, but it is there to prevent you from overloading the program. For-loops don't run with your program, they run until they finish and then everything else resumes. If you have a very large for-loop, or lots of stacked for-loops, the compiler may panic. And it has some reason to. Suppose you tried to run:
Code:
for(i = 0; i < 999, i++) {
for(ii = 0; ii < 999, ii++) {
for(iii = 0; iii < 999; iii++) {
//code
}}}
If I did my quick math right that would run almost one billion times. Not a very efficient way of doing things. So long story short, try not to stack for-loops unless it's necessary, and if you have to make sure they are within reasonable bounds.
 
W

WimpyLlama

Guest
I am stacking three for loops but if I did my math right, it should only loop 42 times. Which is a lot but I am shocked it wouldn't work. Is there any way I might be able to work around this so I can do what I said before. If there is no possible way, it's fine, but if there is a way, I want to do it.
 

FrostyCat

Redemption Seeker
If you calculated that it's 42 times and it ran more or fewer than 42 times, then you didn't do your math right.

Aside from simple miscounting or variable typos, the other most common cause of unexpected iterations is improper scoping of looping variables. People who never use var are particularly vulnerable to this. Putting everything in only instance and global scope could allow code outside the current piece of code or inside a subroutine to interfere with the looping variable mid-iteration, producing an infinite or thrown-off loop that isn't immediately obvious.
 
W

WimpyLlama

Guest
I do use "var" when making a for loop. So is there no way I can get around this issue?
 

TheouAegis

Member
Is there really a limit? for loops aren't added to the stack, so there is no risk of stack overflow. Personally I would be surprised if there is a limit to the number for Loops you could have because there's nothing that says game maker cannot freeze the system. (except except on phones)

edit: original post had a typo saying "loops are added". meant to be "loops aren't added".
 
Last edited:

samspade

Member
I do use "var" when making a for loop. So is there no way I can get around this issue?
You should show your code. The short answer to your original question is no, certainly not a limit of three. I've had for loops 4 deep regularly and I'm sure I've gone farther than that when taking into account loops embedded in scripts and so on. If you're running into a problem it is something else.

As a completely random stab in the dark you might be overwriting a variable in one of the loops, re using i or j or perhaps copying an array like this array1 = array2; I mention these because they're all things I've done to mess up repeated for loops.
 

RollyBug

Member
Is there really a limit? for Loops are added to the stack, so there is no risk of stack overflow. Personally I would be surprised if there is a limit to the number for Loops you could have because there's nothing that says game maker cannot freeze the system. (except except on phones)
I might be working off of outdated / misinformed knowledge then.
 
W

WimpyLlama

Guest
Here is my code. This is in a step event. Basically, the problem is where it says "global.inventory != global.itemID" I don't see why this won't work. If I remove the var b for loop and what I just said it works fine. Even with the code, it works fine, but then I can place items, which is not what I want.

Code:
//place block
if (mouse_right) {

    for (var b = 0; b < itemCount; b++) {
    
        for (var v = 0; v < IDCount; v++) {
    
            for (var i = 0; i < invSize; i++) {
        
                if (global.inventory[i] != -1 && collision_circle(x,y,32,global.ID[v],false,true) && global.inventorySelected[i] == 1 && global.inventory[i] != global.itemID[b]) {

                    instance_create_layer(x,y,"BI",global.ID[global.inventory[i]]);
                    invDrop(global.inventory[i]);
            
                }
    
            }   

        }
    
    }

}
 
Z

zendraw

Guest
if you do more then 2 for loops then you shuld rethink how you are approaching the case. espetially if a loop is running every step. loops are hard work, logic and formulas intelligent work.
 

samspade

Member
While you've done a good job naming things it's still a little hard for me to follow exactly what is happening at each point as I can only guess at things. But here are a few suggestions:

1. is there any possibility that invDrop messes up one of the variables? This would include doing something to change not just b, v, and i, but something that changes any other of the global variables used.
2. global.itemID seems odd to me. Again, I don't know what your variables actually are, but you have b iterating < itemCount and v iterating < IDCount but then the only time you use b is with global.itemID.
3. Assuming no issues with the first two, I would assume the code is doing exactly what you told it to do and that the problem is somewhere you've told it to do something you didn't mean too. I would debug this by putting the following line

var break = 0;

after invDrop and then putting a break point on it and running the debugger (the reason for the extra line is because you need a line for a break point I believe and you want the break point to be after all of the code) and then as painstaking as it is, check each value each iteration and every point in the conditional figure out exactly what the code is doing. Given that the amount of loops is item_count 8 IDcount * invSize you might want to artificially limit those numbers as that could be in the 100s or higher pretty quick.
 
W

WimpyLlama

Guest
What I did was I followed Shaun Spalding's inventory tutorial which uses arrays. Then I set up a second array for if a slot of the inventory is selected. global.inventorySelected == 1 tests if that slot is selected. Then I set up another array called global.ID which had all the blocks and item ID's in it, such as, global.ID[0] = oGrass; Then I made the placement system for blocks. Now I am trying to make it so you can't place items, what I have done is I have made another array called global.itemID. It sets things like this, global.itemID[0] = 6; The 6 is the items ID in the inventory array. So every time I make a new item, I add to the array, the item's ID. Now I am trying to make it so the placing system only works if global.inventory (Which is the slot we have selected) == global.itemID (Which is the items ID)
 
W

WimpyLlama

Guest
Sorry, I forgot. All that invDrop does is that it removes whatever item id you put in from your inventory. It's a pretty basic script.
 
R

rpsrosario

Guest
You could try to make your logic short-circuit as soon as possible (I apologize if mentioned earlier / not aligned with the problem, didn't read everything):
Code:
//place block
if (mouse_right) {
    for (var i = 0; i < invSize; i++) {
        if (global.inventory[i] == -1 || global.inventorySelected[i] != 1) {
            continue;
        }
    
        for (var b = 0; b < itemCount; b++) {
            if (global.inventory[i] == global.itemID[b]) {
                continue;
            }
        
            for (var v = 0; v < IDCount; v++) {
                if (collision_circle(x, y, 32, global.ID[v], false, true)) {
                    instance_create_layer(x, y, "BI", global.ID[global.inventory[i]]);
                    invDrop(global.inventory[i]);
                }
            }
        }
    }
}
DISCLAIMER: I'm slightly drunk. Code / reasoning might be off.
 
Last edited by a moderator:
R

rpsrosario

Guest
You mentioned your script removes items from the inventory. Could that be affecting the global array you're querying? It could explain why the IDs behave inconsistently, if I understood your post correctly.
 
W

WimpyLlama

Guest
ID's don't behave inconsistently. The only problem is that when I do if (global.inventory != global.itemID) it doesn't work. There is no reason for it not to work, so I don't know the problem.
 

RollyBug

Member
Whenever I'm in a tough spot and feel like something should work but isn't I throw in a ton of show_debug_message()'s to print out all pertinent variable values. Obviously here you'd need to pick and choose what to look at as you wouldn't want to loop print functions. I couldn't spot an issue in the code so maybe give that a try.
 

Roderick

Member
Nesting multiple loops creates a potentially messy block of code, dramatically increasing the chance of himan errors.

Start with the innermost loop. Create a script that does the same thing. Do not call it from your loops yet. Test it independently, and once it's working, write another script that does the next layer, but instead of nesting a loop imside it, call the script we created before. Again, thorougly test it before linking it to the next layer.

An additional benefit to this is that you can reuse those scripts elsewhere, instead of having to copy large blocks of code, should you find yourself doing the same thing again.
 
D

drowned

Guest
Could you expand upon what "it doesn't work" means? Also, what do the contents of the global.inventory and global.itemID arrays look like?
 
E

Ephemeral

Guest
There is no reason for it not to work
There is always a reason for it not to work. Even when it works. Calm down and think it through, and stop expecting us to read your mind or be able to see your screen.
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
There is always a reason for it not to work. Even when it works. Calm down and think it through, and stop expecting us to read your mind or be able to see your screen.
@Ephemeral is right, man.. you have to give us more information for us to help!

What is the error you're getting, if any... try using "show_debug_message" to print the values of "global.inventory" and "global.itemID"-
what is global.inventory? an array, a number?! if it is an array, and itemID is an number
then:

Code:
global.inventory != global.itemID
will be always true.
 
W

WimpyLlama

Guest
I have been doing the show debug message, but it's only showing 0. If you want to know what global.inventory is, then look at what I have been saying. I followed Shaun Spalding's inventory tutorial, that's what global.inventory is. All I am trying to do is test if one array = another array. I don't know why it won't work. Sorry if I have been a little mean about it, but I have had this problem for a week now and I can't get past it. I can't continue development on my game unless I get this item system to work.
 
W

WimpyLlama

Guest
And also I made it so global.itemID[0] = 5 and global.itemID[1] = 6; but when I show_debug_message(string(global.itemID[0])); it outputs 6. There is no reason for it to do this. I don't know what's happening.
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
I have been doing the show debug message, but it's only showing 0. If you want to know what global.inventory is, then look at what I have been saying. I followed Shaun Spalding's inventory tutorial, that's what global.inventory is. All I am trying to do is test if one array = another array. I don't know why it won't work. Sorry if I have been a little mean about it, but I have had this problem for a week now and I can't get past it. I can't continue development on my game unless I get this item system to work.
if:

Code:
a = [1, 2, 3];
b = [1, 2, 3];

(a == b)  => is false!!
even if the contents are the same the array reference is not...
to know if it is the same array you have to go through all items in the array and check.
lets go one step forward:

Code:
a = [1, 2, 3];
b = a;

b[0] = 0;
a[0] = 0;

(a == b) => is still false;
once you edit the b array it gets copied and its reference changes

for a last example:
Code:
a = [1,2,3]
b= a;

b[@ 0] = 0;

(a==b) => is true!!
 
W

WimpyLlama

Guest
I don't really understand what you're trying to say. Here is what I did. all this code does is only do the placing thing is the if statement is false. There is no reason for the if statement to be false when I do certain things in the game, but it comes out as true. I don't know why.

Code:
//CREATE
global.itemID[0] = 1;
global.itemID[1] = 2;

invSize = 3;
itemSize = 2; //it = the size of the itemID array

for (var i = 0; i < invSize; i++) global.inventory[i] = -1;

//-1 is empty in the inventory. this create event just creates variables, not the code that I am having a problem with.

//STEP EVENT
if (mouse_right//which is a variable that = right click. The variable is set in a different object) {

     for (var i = 0; i < invSize; i++) {

             for (var b = 0; b < itemSize; b++) {

                       if (global.inventory[i] != global.itemID[b]) {
                                
                            //do my placing thing.

                    }

             }

      }

}
 
W

WimpyLlama

Guest
By the way. this is just a sloppy recreation of my code. It's not the real thing.
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
By the way. this is just a sloppy recreation of my code. It's not the real thing.
what's the "do the placing" thing?! You might be changing data inside the global.inventory and if so... you need to use:

Code:
global.inventory[@ i] = .....
with the "@" sign
 

samspade

Member
I don't really understand what you're trying to say. Here is what I did. all this code does is only do the placing thing is the if statement is false. There is no reason for the if statement to be false when I do certain things in the game, but it comes out as true. I don't know why.

Code:
//CREATE
global.itemID[0] = 1;
global.itemID[1] = 2;

invSize = 3;
itemSize = 2; //it = the size of the itemID array

for (var i = 0; i < invSize; i++) global.inventory[i] = -1;

//-1 is empty in the inventory. this create event just creates variables, not the code that I am having a problem with.

//STEP EVENT
if (mouse_right//which is a variable that = right click. The variable is set in a different object) {

     for (var i = 0; i < invSize; i++) {

             for (var b = 0; b < itemSize; b++) {

                       if (global.inventory[i] != global.itemID[b]) {
                              
                            //do my placing thing.

                    }

             }

      }

}
This sentence: " There is no reason for the if statement to be false when I do certain things in the game, but it comes out as true." makes no sense. If there is no reason for it to be false, then it should come out as true. If this sentence is worded correctly then it seems like you're not experiencing any problem.

This code makes sense, are you saying this simplified code does work or does not work? Or that it is just an example of what you hope would happen and you haven't actually tested it?

Have you posted the code for invDrop() yet?

Did you run through the for loop in the complier as I suggested and check the values for each iteration of the loop?

The problem in general and this particular sentence specifically: "global.itemID[0] = 5 and global.itemID[1] = 6; but when I show_debug_message(string(global.itemID[0])); it outputs 6" again make me think you have an array assignment error somewhere.

Finally, you could upload the project source file. I'd take a look at it.
 
W

WimpyLlama

Guest
Here is the project file https://drive.google.com/open?id=1Oi5fbWpxZ_db4p_EjZM1xiAZBTkYaE6b Basically the problem is this. In the create event is this global.inventory[0] = 1; global.itemID[0] = 1; Then in the step event I test this: if (global.inventory[0] != global.itemID[0]) it outputs true for some reason. I did not change any of the values anywhere but it still outputs true. I don't understand why. This is not the full code, but it is a very simple version of it. In the real code, I don't change any values in a way that would affect this.
 
E

Ephemeral

Guest
I followed Shaun Spalding's inventory tutorial, that's what global.inventory is.
Would you stop saying that like it is a magic spell that ought to magically make your code work.

All I am trying to do is test if one array = another array.
You can't do that with operators like == != etc. You have to use array_equals() to do it. As xD just explained. But it doesn't actually look like you're comparing two arrays, just two values from arrays, so your problem is probably something else.
 
W

WimpyLlama

Guest
Sorry about saying that a lot. English is not my first language. Grammarly helps a lot.
 

samspade

Member
Here is the project file https://drive.google.com/open?id=1Oi5fbWpxZ_db4p_EjZM1xiAZBTkYaE6b Basically the problem is this. In the create event is this global.inventory[0] = 1; global.itemID[0] = 1; Then in the step event I test this: if (global.inventory[0] != global.itemID[0]) it outputs true for some reason. I did not change any of the values anywhere but it still outputs true. I don't understand why. This is not the full code, but it is a very simple version of it. In the real code, I don't change any values in a way that would affect this.
Might be something on my end, but when I opened that up, it was a completely blank file.

EDIT: never mind, figured out what I did wrong.
 
Last edited:

samspade

Member
Okay, I've got it up and running, now that it is in front of me, what in game terms, is happening when it should: e.g. tell me what buttons I have to push to get a wrong outcome and what that wrong outcome looks like.
 

samspade

Member
First, I doubt you followed my advice to add breaks and step through your for loop as doing so immediately showed the problem (I think it took me three minutes, and most of that was just figuring out where things were in your code since I didn't write it and what was happening and what was not supposed to be happening). To be honest, I'm tempted to not tell you what it is so that you have to learn how to go through this debugging processes yourself.

There is nothing wrong with the for loops. They are acting exactly as they should. The problem is that they way you have it set up there is no possible way for the final if statement to not be true at some point. The reason for this is very simple - you have two itemID. So in your example oSword is 5 and oTest is 6. So, let's say you're holding a sword - the loop checks to see if global.inventory != global.itemID. It does as oSword is 5 == itemdID[0] which is also 5. HOWEVER, you've put it in a for loop so it loops again and now it checks oSword against itemID[1] which is 6. These aren't equal so it evaluates as true and drops the item. Again, this was very, very easy to see with the debugger and took a single test run once I figured out what I was testing to debug.

A simple fix to see that this is true is to change both itemIDs to 5. You can now no longer drop the sword.

In short, you need to find a different way to check if the inventory item is droppable or not. There are lots of ways to do this. Working with your original idea, though this isn't very optimized it shouldn't matter much, is move the itemID checking inside of the last for loop and iterate through all of the items there. For example in psudo code

Code:
if (you think you should drop it) {
    if (global.iventory[I] == any global.itemID) { //either use an or statement for every itemID or use the for loop here
        break; //e.g. don't drop
    }
}
Or something like that.

Edit: as a side note, I did think the little portion of the game I played was interesting. Or rather that the mechanic of digging and building was simple and cool.
 
Last edited:
W

WimpyLlama

Guest
Thank you sooooo much for the help. I have never been very good a debugging so I must not have seen the problem. Thank's you everyone who helped!
 
W

WimpyLlama

Guest
But where would I put the code that would place the block if it does not fail?
 

samspade

Member
But where would I put the code that would place the block if it does not fail?
Immediately after the sub if statement in the code I posted. Though perhaps a better way to do it for the moment would be to use your original if statement but for the final conditional change it to if global.inventory < num where num in your current project == 5 or whatever becomes the lowest droppable item. Then so long as items are ordered so that droppable are lower and non-droppable are higher it is fairly easy to expand the list.

E.G.

Code:
if (global.inventory[i] != -1 && collision_circle(x,y,32,global.ID[v],false,true) && global.inventorySelected[i] == 1 && global.inventory[i] < num) {

    instance_create_layer(x,y,"BI",global.ID[global.inventory[i]]);
    invDrop(global.inventory[i]);
           
}
 
W

WimpyLlama

Guest
This works but if I added another block to the game that had the ID of 7 then you wouldn't be able to place that block. I was thinking of setting up a whole separate ID system just for the items but it would get very complex and it wouldn't work very well. I could just make it so every time I add a new block it would be less than the item IDs, but then I would have to change every item ID every time I made a new item.
 

samspade

Member
This works but if I added another block to the game that had the ID of 7 then you wouldn't be able to place that block. I was thinking of setting up a whole separate ID system just for the items but it would get very complex and it wouldn't work very well. I could just make it so every time I add a new block it would be less than the item IDs, but then I would have to change every item ID every time I made a new item.
Yes, that is essentially correct with that idea. A more complex but flexibly method would be to have each item itself be an array where that sub array contains information such as whether it is droppable or not - or to use a grid to accomplish the same thing. You could also use a ds_map.
 
W

WimpyLlama

Guest
I have looked at tutorials for ds_map stuff for inventory's, but none of them do what I want to do. Do you have any tutorial suggestions? I have been thinking of redoing the ID system anyways.
 

samspade

Member
ds maps are the easiest to understand conceptually. For example:

Code:
//create a map and add all the things too it
can_drop_map = ds_map_create();
ds_map_add(can_drop_map, item.dirt, true); //or however you were identifying items I can't remember
...
ds_map_add(can_drop_map, item.sword, false);

//example use
if ( can_drop_map[? global.inventory[i]]) {
    drop item;
}
The same idea applies to nested arrays and 2d arrays or grids. But that you'd really just have to learn yourself.
 
W

WimpyLlama

Guest
Thanks for the help. I guess I will have to look into it more to start a new id system.
 
Top