XNA Game-Themed CS1 Examples (XGC1) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.5.RepetitionStructures
Example: Ex_18.NestedForLoops

# Repetition: Nested For Loops

References:

• Pre-requisite: it is assumed that you have read through the prior tutorials, and are familiar with the concepts covered in those tutorials.

Goals:

• In this tutorial, we will:
• Examine a nested for loop, which consists of one for loop nested inside another for loop.  Nested for loops are useful for, amongst other things, dealing with two-dimensional situations and data.

1. Obtain the example code

Download and unzip the zip file and you will see an ExampleProgram folder. Open the ExampleProgram folder, the EXE folder contains the compiled program and you can double click on the .sln file to work with the source code.

When the game starts, you'll see a screen that looks similar to this:

By using the left thumbstick, you can adjust both the horizontal space between the circles, and the vertical space between the circles.

2. Examining The Program:

Let's examine the C# source code that produces the behavior we see on-screen

• CreateABallAt(): /// /// Allocates and initialize the memory for a ball at /// xPos and xPos and with aTex texture /// /// x-location to create the new ball /// y-location to create the new ball private void CreateABallAt(float xPos, float yPos) {     XNACS1Circle aBall = new XNACS1Circle();     aBall.Center = new Vector2(xPos, yPos);     aBall.Radius = BALL_RADIUS;     // aBall.Label = xPos +"/" + yPos;     aBall.Label = String.Format("{0:0},{1:0}", xPos, yPos); }
• This method creates a new circle object, so that the new circle's center is at (xPos, yPos).  Just about everything should be extremely familiar, except for the code that formats the numbers that are being displayed on the circles:
aBall.Label = String.Format("{0:0},{1:0}", xPos, yPos);
• If you replace this line with the commented-out line above it, you'll notice that the numbers are displayed exactly on the screen. Since each number tends to have a lot of digits after the decimal place, this means that you commonly get labels like "4.899999,6.200001",  which tend to overlap with the circles on either side of the one that has the label.
• By using the String.Format method, and formatting strings like {0:0}, we can specify not only which parameter we want (the zeroth one - xPos), but the number after the semi-colon tells us what sort of format to use (in this case, the :0 means just the digits to the left of the decimal point, and no digits after)
• UpdateWorld(): RemoveAllFromDrawSet(); // remove previously created balls     // update x/y space with left thumbstick     m_XSpace += GamePad.ThumbSticks.Left.X * SCALE_FACTOR;     m_YSpace += GamePad.ThumbSticks.Left.Y * SCALE_FACTOR;     // here are the nested for loops     for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)     {         for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)         {             CreateABallAt(xPos, yPos);         }     }
• The relevant, new code is shown above.  This program uses the same two-step approach that most of the other tutorials in this chapter have used: first remove everything from the screen, then create new objects on the screen (as a hack that allows us to avoid using arrays, yet still create dynamic programs).  The following line accomplishes that first step by removing all prior objects:
RemoveAllFromDrawSet(); // remove previously created balls
• After having removed all the current circles from the screen, the program then goes on to adjust the horizontal distance between circles (m_XSpace), and the vertical distance (m_YSpace), based on user input.
// update x/y space with left thumbstick

• Having done that, the program then proceeds onwards to the nested loops.  While these may look tricky, they're essentially just a composition of a for loop (which you've seen, and understand) inside another for loop.  Nothing about how the for loops execute changes, just because one is composed with another.
• In order to work through this concept, it may help to think of the inner loop as a 'black box' that does work, and is merely repeated by the outer loop.  For example, in the following picture, we've surrounded the entire, interior loop with the green box, in order to indicate that no whatever the inner loop does, the outer loop will simply ensure that the inner loop is repeated.
• The primary benefit to thinking about nested loops in this way is that it will help you to figure out what the inner loop (in green) does independently of what the outer loop (in blue) does, and then to (somewhat) separately figure out what the outer loop does.
• Looking at the inner loop, we can see that it creates a vertical column of circles.
• You'll see that the inner loop calls the CreateABallAt method, passing in the xPos and yPos values. After you've examined the CreateABallAt method, you can see that it creates a new circle whose circle is at the location (xPos,yPos).
• The inner loop starts yPos at zero, and then increases as the inner loop iterates.
• xPos will have some value (determined by the outer loop), but since the inner loop doesn't change the value of xPos, xPos may as well be a constant value (as far as the inner loop is concerned).
• Thus, at a given value of xPos (say, 10.0f), the inner loop will create circles at (say)  (10.0f, 0.0f), then (10.0f, 7.0f), then (10.0f, 14.0f), etc, etc.
• Therefore, we can see that the inner loop will create a vertical column of circles
• Looking at the outer loop, we can see that it repeatedly increases the value of xPos, and then executes the inner loop.
• Since the inner loop used xPos to figure out where to put the vertical column, we can see that the inner loop creates all the circles for one vertical column, and the outer loop repeatedly 'moves the column over' by increasing xPos.
• Let's examine the details of how the for loop operates by tracing through the execution of the first couple of iterations in detail
1. Just like we did for the normal for loop, the initialization part of the outer for loop is executed exactly once.  It is always executed, and it it is executed before the first check of the condition:
for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)
 Variable Name Value xPos 0.0f
2. Next, the first iteration of the outer loop begins:
1. At the start of each iteration of the loop, the condition is checked:
for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)

This asks "Is the current value of xPos (which is 0.0f) less than 100.0f?"  This is true, so the program will execute the body of the loop.

2. The body of the loop consists of the following lines of code:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

{

CreateABallAt(xPos, yPos);

}

Let's examine the details of how the inner for loop operates by tracing through the execution of the first couple of iterations in detail

1. Just like we did for the normal for loop, the initialization part of the inner for loop is executed before the loop iterates.  Technically, it is executed only once, except that when the outer loop causes this code to be run again, that second time will count as a "new loop", and therefore will be executed again.

for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

 Variable Name Value xPos 0.0f yPos 0.0f
2. Next, the first iteration of the inner  loop begins:
1. At the start of each iteration of the loop, the condition is checked:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

This asks "Is the current value of yPos (which is 0.0f) less than 100.0f?"  This is true, so the program will execute the body of the inner loop.

2. The body of the loop consists of the following line of code:
CreateABallAt(xPos, yPos);

The above code will place a soccer ball on the screen, so that it's center point is (0.0f, 0.0f).

3. After the body of the loop has finished executing, then the counting expression is executed
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

which increase the value stored in yPos by m_YSpace (which is 5.0f, unless the user has changed it):

 Variable Name Value xPos 0.0f yPos 5.0f
3. Next, the second  iteration of the inner  loop begins:
1. At the start of each iteration of the loop, the condition is checked:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)
This asks "Is the current value of yPos (which is 5.0f) less than 100.0f?"  This is true, so the program will execute the body of the inner loop.
2. The body of the loop consists of the following line of code:
CreateABallAt(xPos, yPos);
The above code will place a soccer ball on the screen, so that it's center point is (0.0f, 5.0f).  As you can see, the Y value is higher up than the circle we created in the previous loop.  This is as we predicted - the inner loop is creating a vertical column of circles.
3. After the body of the loop has finished executing, then the counting expression is executed
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)
which increase the value stored in yPos by m_YSpace (which is 5.0f, unless the user has changed it):
 Variable Name Value xPos 0.0f yPos 10.0f
4. The loop continues to iterate, until the vertical column has gone all the way to the top of the screen, at which point the inner loop stops repeating.
3. After the inner loop (which is body of the outer loop) has finished executing, then the counting expression is executed
for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)

which increases the value stored in xPos by the value stored in m_XSpace (7.0f, unless the user has changed it)

 Variable Name Value xPos 7.0f yPos < inaccessible >
• You'll notice that yPos is now marked as being < inaccessible >.  This is because the yPos variable is declared inside the inner loop, and therefore cannot be accessed except by code inside the inner loop.
3. Next, the SECOND  iteration of the outer loop begins:
1. At the start of each iteration of the outer loop, the condition is checked:
for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)

This asks "Is the current value of xPos (which is 7.0f) less than 100.0f?"  This is true, so the program will execute the body of the loop.

2. The body of the loop consists of the following lines of code:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

{

CreateABallAt(xPos, yPos);

}

Let's examine the details of how the inner for loop operates by tracing through the execution of the first couple of iterations in detail

1. Just like we did for the normal for loop, the initialization part of the inner for loop is executed before the loop iterates. We will execute this code again, because we need to reinitialize the code for this particular run of the loop.  We will execute it only once for this set of iterations, just like we only executed it once for the previous time we ran the inner loop:

for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

 Variable Name Value xPos 7.0f yPos 0.0f
2. Next, the first iteration of the inner  loop begins:
1. At the start of each iteration of the loop, the condition is checked:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

This asks "Is the current value of yPos (which is 0.0f) less than 100.0f?"  This is true, so the program will execute the body of the inner loop.

2. The body of the loop consists of the following line of code:
CreateABallAt(xPos, yPos);

The above code will place a soccer ball on the screen, so that it's center point is (7.0f, 0.0f).

3. After the body of the loop has finished executing, then the counting expression is executed
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)

which increase the value stored in yPos by m_YSpace (which is 5.0f, unless the user has changed it):

 Variable Name Value xPos 7.0f yPos 5.0f
3. Next, the second  iteration of the inner  loop begins:
1. At the start of each iteration of the loop, the condition is checked:
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)
This asks "Is the current value of yPos (which is 5.0f) less than 100.0f?"  This is true, so the program will execute the body of the inner loop.
2. The body of the loop consists of the following line of code:
CreateABallAt(xPos, yPos);
The above code will place a soccer ball on the screen, so that it's center point is (7.0f, 5.0f).  As you can see, the Y value is higher up than the circle we created in the previous loop.  Further, since the outer loop moved xPos over by 7.0f, the new circles are just to the right of the first column.  We can see that the inner loop does, indeed create a column of circles, and the outer loop does, indeed, ensure that each column has been 'moved over' from the previous column.
3. After the body of the loop has finished executing, then the counting expression is executed
for (float yPos = 0; yPos < World.WorldMax.Y; yPos += m_YSpace)
which increase the value stored in yPos by m_YSpace (which is 5.0f, unless the user has changed it):
 Variable Name Value xPos 7.0f yPos 10.0f
4. The loop continues to iterate, until the vertical column has gone all the way to the top of the screen, at which point the inner loop stops repeating.
3. After the inner loop (which is body of the outer loop) has finished executing, then the counting expression is executed
for (float xPos = 0; xPos < World.WorldMax.X; xPos += m_XSpace)

which increases the value stored in xPos by the value stored in m_XSpace (7.0f, unless the user has changed it)

 Variable Name Value xPos 14.0f yPos < inaccessible >
4. The THIRD iteration of the outer loop will then cause the third column to be drawn on the screen.
5. The outer (and inner) loops will be executed a substantial number of times; we will not trace through the entire execution here.

FURTHER EXERCISES::

1. Start from a blank starter project (1000.201, if you need it), and re-do the code from memory as much as possible.  On your first try, do what you can, and keep the above code open so that when you get stuck, you can quickly look up what you forgot (and that after you finish a line, so that you can compare your line to the 'correct' line).  On the next try, do the same thing, but try to use the finished code less.  Repeat this until you can type everything, without refering the tutorial's code.
• Repeat this exercise daily for several days, so that you really get the hang of this.  As you go on, periodically review this by re-doing this exercise.
2. Finding & Fixing Bugs In The Program
For this exercise, you should use the same project that was explained in the above tutorial.
What happens if you keep telling the program to decrease the horizontal (or vertical) space with left thumb stick?  Why is this happening?  How can you fix this problem from happening?
3. Practicing With Nested Loops: Reimplement the inner loop as a normal while loop
For this exercise, you should use the same project that was explained in the above tutorial.
You've previously seen that you can take any for loop, and transform it back into a while loop.  You should do this with the inner loop, so that you have a while loop nested inside a for loop.
4. Practicing With Nested Loops: Reimplement the inner loop as a do while loop
For this exercise, you should use the same project that was explained in the above tutorial.
You've previously seen that you can take any for loop, and transform it back into a while loop.  From there, you can transform it into a do...while loop that will work correctly for this particular program.  You should do this with the inner loop, so that you have a do while loop nested inside a for loop.
5. Practicing With Nested Loops: Display the row and column, instead of the location
For this exercise, you should use the same project that was explained in the above tutorial.
Modify the provided, example code so that the labels shows the row and column, instead of the location.  Once you're done, the lower left ball will show 0,0, and all balls on the second column will show 1,0, then 1,1, then 1,2, etc.