Skip to main content

News

Topic: Nested for loops not working as expected. (Read 1978 times) previous topic - next topic

  • Mooch
  • [*][*][*]
Nested for loops not working as expected.
I'm learning pixel art and wanted to churn out different palettes real quick. I'm using nested for loops to draw a bunch of rectangles, and don't understand why it's not working as expected. Here's the code... (Excuse the whitespace.)

Code: (javascript) [Select]
function drawtest(){
var rows = 8;
var columns = 8;
var size = 16;
var steps = [0,80,160,240];

for (i = 0; i < rows; i++){
for (j = 0; j < columns; j++){
for (r = 0; r < steps.length; r++){
for (g = 0; g < steps.length; g++){
for (b = 0; b < steps.length; b++){
Rectangle(size+j*size,size+i*size,size,size,CreateColor(steps[r],steps[g],steps[b]));
}
}
}
}
}
}

function game(){
SetRenderScript("drawtest();");
MapEngine("m_colortest.rmp", 60);
FlipScreen();
GetKey();
}


It just draws a bunch of white rectangles, which just looks like a giant white square since there's no space inbetween them.

I don't understand why, while iterating between the for loops, it's not drawing each square a different color, given that it's correctly drawing 64 individual squares and placing them correctly (thus the i and j loops are definitely working as I expect).

Furthermore, if I manually change the Rectangle function's RBG parameters to something like (...steps[3],steps[1],steps[2]) it changes the color of the whole field of rectangles accordingly. So the steps array is capable of being accessed properly. The for loop just...isn't.

So why isn't this drawing each rectangle a different color?

(BTW, I've started reading a book on Javascript, to finally properly learn it. That's what got me interested in coding again. So I'll be learning proper theory in the coming weeks and months, so bear with me.)

(Also, I've spent over an hour toying around with the function in a dozen different ways to get the proper result to no avail; I won't tax you by showing everything I've tried.)

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #1
Not sure what you're trying to accomplish there but in your code, you're just drawing a bunch of rectangles over each other for each (i,j), so only the final one, which happens to be white, is visible.
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Nested for loops not working as expected.
Reply #2
I think you need to look closely at what the three for-loops for r, g, and b are doing, and re-evaluate why you chose to implement it that way. I'm myself not sure what you are trying to accomplish either, but I've never seen more than 3 for loops ever in code. 5 for loops, is what we call a code smell and it's indicative of poor logic.

Are you trying to draw a 2D field of rectangles of varying colors (IE: a palette?) Because there are far better ways. My advice: Perhaps run a for loop to create an array of colors then a 2D for loop on drawing them to screen.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Mooch
  • [*][*][*]
Re: Nested for loops not working as expected.
Reply #3

Not sure what you're trying to accomplish there but in your code, you're just drawing a bunch of rectangles over each other for each (i,j), so only the final one, which happens to be white, is visible.


Ohh! Shazbots, that's right. Since the r, g and b things are nested inside the i, j parts, it's going through every single r, g and b for each i, j pair.

I'm trying to make a palette, btw. Like, a bunch of different colored squares arranged into a bigger square. Surprisingly, I could not find a "enter a bunch of step values and churn out a palette" type program online, hence my being here.

I've never appended an array before, so I think what I'll do is create code that generates a variable-length array of Sphere color objects, and then just cycle through those in the rectangle-drawing code.


I think you need to look closely at what the three for-loops for r, g, and b are doing, and re-evaluate why you chose to implement it that way. I'm myself not sure what you are trying to accomplish either, but I've never seen more than 3 for loops ever in code. 5 for loops, is what we call a code smell and it's indicative of poor logic.

Are you trying to draw a 2D field of rectangles of varying colors (IE: a palette?) Because there are far better ways. My advice: Perhaps run a for loop to create an array of colors then a 2D for loop on drawing them to screen.


Yeah, I see that now. I have a big problem, though, that I've never been able surmount. Maybe I'll learn how as I properly learn Javascript, but the problem is not being able to make a variable amount of variables.

Basically, what I wanna do with this program, is input a variable-length series of "steps." For example, [0, 63, 127, 191, 255] or perhaps [0, 15, 31, 47, ... 255], and have the program create one colored square for every possible combination of those values applied to the red, green and blue of the Rectangle function.

I can hard code values, like...

Code: (javascript) [Select]
var pal = [[0,0,0],[0,0,63],[0,0,127],...]


...but I don't know how to automate that so I can just plug in the "steps" values and have the array automatically generated. Because the thing is, the number of steps varies, so the size of the array will vary, and then there's the sticky issue of getting the correct values into the correct slots. It's dizzying.

I've googled, and I can find things like permutators where you could input, for example, [1,2,3,4] and get 1234, 1243, 1423, 1432, etc. But of course that's not what I want. No matter how many values I input, I always want them combined into every possible combination of three and only three. Because I've found other code that'd give: 1, 2, 3, 4, 11, 12, 13, 14, 21, 22, 23, 24, etc. which is also not what I want.

I'm sure it's doable and probably easy, but I haven't figured it out yet.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #4
Let me just say that you had the right idea in the original code, where you tripped up was in using a for loop for the x and y coordinates.  Rather than enumerating those, you should calculate them from your position in the palette.  How it would come together is something like this:

Code: (javascript) [Select]

var ra = [ 0, 96, 192, 255 ];
var ga = [ 0, 96, 192, 255 ];
var ba = [ 0, 96, 192, 255 ];
for (var ib = 0; ib < ba.length; ++ib) {
for (var ig = 0; ig < ga.length; ++ig) {
for (var ir = 0; ir < ra.length; ++ir) {
var x = ig * 8;
var y = ib * 8;
var i = 32 * (ir % 2);
var j = 32 * Math.floor(ir / 2);  // round toward zero
Rectangle(i + x, j + y, 8, 8, CreateColor(ra[ir], ga[ig], ba[ib]));
}
}
}
FlipScreen();


Look over that code, and see if you can figure out what it's doing and why.
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #5
Hint: The RGB color space is 3-dimensional, but the palette needs to be projected into 2D for display.  Based on your posts above, I suspect the 3D->2D projection is what's actually tripping you up and not anything programming-related.
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan

  • Mooch
  • [*][*][*]
Re: Nested for loops not working as expected.
Reply #6
Oh. You can .push arrays into an array.

Code: (javascript) [Select]
var steps = [0,80,144,255]

ColorArraySize = Math.pow(steps.length, 3);
ColorArray = new Array();
for (i = 0; i < ColorArraySize; i++){
ColorArray.push([0,0,0]);
}


That works to dynamically create the correct-sized array. I just put 0 in all the nested array slots so I could use DrawText to check that something was in there. Next step will be to permute the contents of 'steps' into ColorArray, then use that data to create Sphere Color objects that I could use when drawing the swatches.


Let me just say that you had the right idea in the original code, where you tripped up was in using a for loop for the x and y coordinates.  Rather than enumerating those, you should calculate them from your position in the palette.  How it would come together is something like this:

Code: (javascript) [Select]

var ra = [ 0, 96, 192, 255 ];
var ga = [ 0, 96, 192, 255 ];
var ba = [ 0, 96, 192, 255 ];
for (var ib = 0; ib < ba.length; ++ib) {
for (var ig = 0; ig < ga.length; ++ig) {
for (var ir = 0; ir < ra.length; ++ir) {
var x = ig * 8;
var y = ib * 8;
var i = 32 * (ir % 2);
var j = 32 * Math.floor(ir / 2);  // round toward zero
Rectangle(i + x, j + y, 8, 8, CreateColor(ra[ir], ga[ig], ba[ib]));
}
}
}
FlipScreen();


Look over that code, and see if you can figure out what it's doing and why.


I'm pouring over it now. I'm not great at math so I'm mainly trying to figure out how the i and j work. Happily, I'm also (re-)learning college math in tandem with properly learning Javascript, because I want to eventually be able to do Ben Heck-esque hacks and trig and calculus and such are prerequisites to electronics.

BTW, I did not think that something like "ra[ir]" would be accepted within the color parameter of the Rectangle function. Function parameters tend to be tempermental. I tend to have pretty bad luck cramming variables into parameters like that. Good to know it'll work.

Thanks for the code ^_^

Once I fully figure out what it's doing, I'm gonna use it to try to spurt out the swatches with my externally-created two-dimensional array. That way, I can eventually add sorting functions! Like, make it arrange by highest red-value first, or sort all the colors where blue is higher than green, or what have you.

Ooh, and I'll have to add drag-and-drop, and auto-arrange, and text display. Imma make this thing legit :)

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Nested for loops not working as expected.
Reply #7

BTW, I did not think that something like "ra[ir]" would be accepted within the color parameter of the Rectangle function. Function parameters tend to be tempermental. I tend to have pretty bad luck cramming variables into parameters like that. Good to know it'll work.


Of course it would? Remember, if you look at the code, "ir" and "ra" are named variables like anything else. Their names are a bit compact so they could be confusing at first, but it's no different than "red_array[iterator_red]". I hope you are not thinking it was the variable "r" smashed next to a variable called "i" and "a", because that's not how programming works.

Code: (javascript) [Select]

var red_array = [ 0, 96, 192, 255 ];
var green_array = [ 0, 96, 192, 255 ];
var blue_array = [ 0, 96, 192, 255 ];
for (var iter_b = 0; iter_b < blue_array.length; ++iter_b) {
        for (var iter_g = 0; iter_g < green_array.length; ++iter_g) {
                for (var iter_r = 0; iter_r < red_array.length; ++iter_r) {
                        var x = iter_g * 8;
                        var y = iter_b * 8;
                        var i = 32 * (iter_r % 2);
                        var j = 32 * Math.floor(iter_r / 2);  // round toward zero
                        Rectangle(i + x, j + y, 8, 8, CreateColor(red_array[iter_r], green_array[iter_g], blue_array[iter_b]));
                }
        }
}
FlipScreen();
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Nested for loops not working as expected.
Reply #8
BTW, I was able to get a version of this looking decent using Link, the logic looks much simpler too! A good use of the new cross product method:

Code: (javascript) [Select]

var ra = [ 0, 96, 192, 255 ];
var ga = [ 0, 96, 192, 255 ];
var ba = [ 0, 96, 192, 255 ];
var row = 0;
Link(ba).cross(ga).cross(ra).each(function(item, i) {
if (i % 4 === 0) row++;
Rectangle(i * 8, row * 8, 8, 8, CreateColor(item.C, item.B, item.A));
});


The reason it looks different is because Link has a tendency to flatten arrays. We lose some information. BTW, currently this is due to the fact i is looping between the range  [0, 3]. I'm still working on the indexer for the cross product. Because I could also make i the range of the values.

Mooch: BTW the "permutator" you are looking for is called the "set cross product". The idea is you take all the things of an array, A and pair them with all of the things of an array, B, thus creating all of the combinations. Defined as: { A × B = (x ,y) | x ∈ A, y ∈ B }

[1, 2] cross [3, 4] is: {(1, 3), (1, 4), (2, 3), (2, 4)}.


Edit: For posterity the new link version for this would look like:
Code: (javascript) [Select]

var ra = [ 0, 96, 192, 255 ];
var ga = [ 0, 96, 192, 255 ];
var ba = [ 0, 96, 192, 255 ];
var row = -1, col = 0;

Link(ba).cross(ga).cross(ra).each(function(item, i) {
if (i % 4 === 0) row++;
if (row === 8) { row = 0; col = 32; }
Rectangle(col + i * 8, row * 8, 8, 8, CreateColor(item[2], item[1], item[0]));
});

  • Last Edit: July 27, 2016, 01:48:40 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #9
So that's what .cross does, it's a replacement for nested for loops.  That is pretty nifty, is that in the version of Link on GitHub?
  • Last Edit: July 24, 2016, 11:49:44 pm by Lord English
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Nested for loops not working as expected.
Reply #10

So that's what .cross does, it's a replacement for nested for loops.  That is pretty nifty, is that in the version of Link on GitHub?


Not yet. I've been using this to test it out. :P
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Mooch
  • [*][*][*]
Re: Nested for loops not working as expected.
Reply #11
Of course it would? Remember, if you look at the code, "ir" and "ra" are named variables like anything else. Their names are a bit compact so they could be confusing at first, but it's no different than "red_array[iterator_red]". I hope you are not thinking it was the variable "r" smashed next to a variable called "i" and "a", because that's not how programming works.


No, I understood that. I've just tried using variables as parameters before and gotten errors.

Quote from: Radnen
Code: [Select]
    var ra = [ 0, 96, 192, 255 ];
    var ga = [ 0, 96, 192, 255 ];
    var ba = [ 0, 96, 192, 255 ];
    var row = 0;
    Link(ba).cross(ga).cross(ra).each(function(item, i) {
            if (i % 4 === 0) row++;
            Rectangle(i * 8, row * 8, 8, 8, CreateColor(item.C, item.B, item.A));
    });


ReferenceError: Link is not defined. I'm using the standard Sphere, though. I assume it works in yours and possibly TurboSphere (if that's still a thing).

That's freaking awesome, though. While grappling with this for-loop razzmatazz, I was thinking, "why isn't there just a thing that lets you essentially do nested for loops without all the rigamarole."

Quote from: Radnen
Mooch: BTW the "permutator" you are looking for is called the "set cross product". The idea is you take all the things of an array, A and pair them with all of the things of an array, B, thus creating all of the combinations. Defined as: { A × B = (x ,y) | x ∈ A, y ∈ B }


Ah, nice. Wonder if that'll be in any of my upcoming math...

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #12
@Mooch: Link is not a standard part of Sphere (it's an external library), so that's why you got the error.  Even then, the .cross function Radnen mentioned is not in the current version.

minisphere 4.0+ will include Link as part of the standard library (a module you pull in using require()), because it's so damn useful as you point out.  I use it a ton in my battle engine for Spectacles, I've found it's especially great when programming enemy AIs.
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan

  • Mooch
  • [*][*][*]
Re: Nested for loops not working as expected.
Reply #13
Well, it works. I wanted to create an array with Color objects, to allow for advanced messing-around later on, as I add more features.

Code: (javascript) [Select]
function game(){

var ColorLevels = [0, 63, 127, 191, 255];
var ColorArray = new Array();
var SwatchSize = 32;

for (var ir = 0; ir < ColorLevels.length; ir++) {
for (var ig = 0; ig < ColorLevels.length; ig++) {
for (var ib = 0; ib < ColorLevels.length; ib++) {
ColorArray.push(CreateColor(ColorLevels[ir], ColorLevels[ig], ColorLevels[ib]));
}}}

var SwatchX = SwatchSize;
var SwatchY = SwatchSize;

for (i = 0; i < ColorArray.length; i++) {
if(SwatchX < (GetScreenWidth()-SwatchSize*2)) {
SwatchX += SwatchSize;
}
else {
SwatchX = SwatchSize;
SwatchY += SwatchSize;
}
Rectangle(SwatchX, SwatchY, SwatchSize, SwatchSize, ColorArray[i]);
}

FlipScreen();
GetKey();}


Added a little border, too. Not exactly efficient, but my M.O. is to get the thing working, then tidy it up.

I noticed after I was done that my doing things this way was based on my TOTALLY unfounded assumption that you can read and manipulate Sphere "Color" objects. I wanna do things like be able to arrange the swatches in various ways; highest red first, green lower than blue first, what have you. And of course have text to show the RGB of each.

@Lord English: Coolio. I haven't tried any of the other versions of Sphere. Been meaning to, but I'm so comfy in vanilla I'm worried it'll mess me up.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: Nested for loops not working as expected.
Reply #14
For what it's worth, minisphere 3.3 is almost 100% compatible with Sphere 1.5.  It's actually recommended to use it over vanilla now, 1.5 is basically deprecated.  Also if you use Sphere Studio you get a neat single-stepping debugger. :)

And you CAN manipulate Colors after creation:

Code: (javascript) [Select]

color = CreateColor(0, 0, 0);  // black
color.red = 255;
color.green = 255;
color.blue = 255;
// now it's white
miniSphere 5.0b2 (stable: 4.8.8) - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enormous man-eating pigs ~Rhuan