Pages

7. Arrays, Conditionals, and Loops

I am working on a board game that uses a chess board as its basis.  I want to add items to random squares to represent obstacles. This is a great problem to solve using FreeBASIC and a great problem for us to explore arrays, conditionals, and loops.

Arrays

In earlier posts we worked with variables to hold data. An array is a series of variables that have a common name and can be referenced by an index.  A typical suburban street has a row of houses, each with a numbered letter box. We can specify and exact letter box by giving the street name (variable name) along with its house number (index). The Fender guitar company stamps a unique serial number on each guitar it produces. We can specific the exact guitar by giving the Fender Company (variable name) along with the unique serial number (index).

These are examples of one dimensional arrays.  The index starts at a point and continues from there in one direction only. In FreeBASIC one dimensional arrays are given as variableName(index).

I want to use a grid of squares, ten squares high by ten squares wide. Like the examples above, each square can hold a number or a letter. It's pretty obvious that we have an array however, to specify a square in this grid we need to use two indexes, one for the row and one for the column. If we call the top left square board(1,1), where 'board' is the name of the variable), the next square to the right will be board(1,2), the next will be board(1,3) and so on.  If we now drop down one row from the top left hand square we have board(2,1). 

This is a two dimensional array, it has height and width (a bit like me, though not so much on the height!). Arrays don't stop at two dimensions although it the most common arrays you will find when programming are one and two dimension arrays.

Let's start a new file in FBEdit and enter the following:
/'
======================
Array Test in FreeBasic
Franktic
26 March 2017
======================
'/

Dim As Integer wide = 8
Dim As Integer high = 8

Dim As Integer x = 0
Dim As Integer y = 0


Dim As Integer board( 1 To wide, 1 To high)

After the preamble we have two integer variables that will hold the dimensions of our two dimensional array.  The next two variables will be used as indexes.  I have used x and y for simplicity but you can use whatever you wish.  The last line is where we allocate space for our two dimensional array.  A one dimension array would simply skip the  ', 1 To high' within the brackets.

Random and For Next Loop

I want to put random percentages into each square.  To do that I need to use the Rnd function. Calling this function returns a random number between 0 and 1 so we need to multiply the return of Rnd by 100, as has been done four lines down in the code below:

Randomize, 1

For x = 1 To wide
For y = 1 To high
board(x,y) = Int(Rnd * 100) + 1
Next
Next

Notice that the first line is Randomize, 1.  Without this call FreeBASIC will use a set of random numbers that is repeated with each run.  This is great for testing code but not very helpful if you want a random result each time we run your program. The number after the Randomize sets the algorithm the function uses to generate the random numbers. You can read about it in the FreeBASIC help file. I have set it to 1.

The next bit of code uses two loops to cycle through each column and row and fill in the random numbers. Let's take the second For...Next loop and investigate that first:

For y = 1 To high
board(x,y) = Int(Rnd * 100) + 1
Next

A For..Next loop can be described as:
For an index starting at a point and until it reaches the limit
     Do something
Advance the index

Looking at the code we see that we are looking using the y index starting at 1 and will stop when it reaches the variable 'high', which we set to 8 earlier. Let's walk through a couple of iterations, note that x is already set to 1 the first For Next loop:
  1. The first time the code runs it sets y to 1.
  2. Board (1,1) (x was set to 1 earlier, and y is set to one in this pass of this loop) is given the value of  Rnd * 100.  This is a number from 0 to less than 100.  We want an integer from 1 to 100 inclusive so we set the value to (Rnd * 100) + 1.  The Int() function rounds down our result so we don't end up with a result of 101 percent.
  3. The Next command advances the index to 2 and we start at the beginning of the loop again.
  4. Board(1,2) is now given the percentage.
  5. The Next command advances the index to 3, and we loop again.
  6. The looping continues until y = 8  and we have hit the limit for the Next command.
If we look back to the code above that contains the For x = 1 to wide command we can see that we have two For Next loops, one within the other.  This is the typical way of using a for next loop for a two dimensional array.  The outside loop advances once for each complete loop sequence of the inside loop.  Once the outside loop has hit its limit it will then move onto the next command after the loop.  That explains the two Next statements, one belongs to the x and one belongs to the y.

Printing Our Results

Let's print the results out so we can use them. We'll enter the following code to print the contents of our squares all on one line:

For x = 1 To wide
For y = 1 To high
Print Str(board(x,y)); "     ";
Next
Next

If you have followed previous posts this bit of code should be easy to understand.

My output looks like this:



The skewed output is a result of the word wrapping on the screen.  If the screen had been extremely wide the whole lot would have appeared on one line.  This is great but it is not very useful because we cannot readily see what square a particular number refers to.  Let's try printing the square reference with each number.

To print the square coordinates in front of each percentage we need to modify the code above as follows:


For x = 1 To wide
For y = 1 To high
   Print Str(x); ", "; Str(y); ": ";Str(board(x,y)); "     ";
Next
Next

This produces the following more useful output:




It's pretty easy to see that the top left hand square, '1,1', contains 63 while the bottom right square, '8,8' , contains 88

I think we can do even better than that.  Let's try make our output more graphical by simulating the array.  First we need to print the top row coordinates.

For x = 1 To wide
Print  Tab(x*5); Str(x);
Next

Print

The Print line within the  For Next loop advances the position of the cursor to five times the value of x.  When x is 1 printing will start at column 5 (ie. 1*5).  When x is two printing will start at column 10 (ie. 2*5).  Str(x) converts the integer value of x so it can be printed. And the semicolon at the end makes sure we don't move down a line with each print statement.  We want to print out the column names all on one line.  Try it without the semicolon to see what happens.  

At the end of the For Next loop there is a single Print statement.  This prints a single empty line so we can space out our output.

Now we want to print the row number along with the results of that row.  To do that use the following code:

For y = 1 To High
Print y;
For x = 1 To wide
     Print Tab(x *5); Str(board(x,y));
Next
Print
print

Next

The first Print statement prints the row number, y.  The next Print line within the second For Next loop is similar to the code we used previously except we are now printing the contents of the square. We then use two print statements to give us to blank lines between each row.  Our output looks like this:


Mission Accomplished!

Conditionals

One last embellishment that I want to do to the output is to only show squares that have a particular interest to me.  Let's say the 8 x 8 square represents a piece of grassland.  Each square has a 20 percent chance of having a tree in it.  I know the rest contains grass so I want the output to only show squares that have a value of 20% or less.

To do this I want to create a constant that will hold our percentage chance of the tree being present.  A constant is very similar to a variable but its contents does not change; it is constant.

Below the variables section add the following line:

Const obstacle = 20

The code to just print out  the squares with contents less or equal to 20 ( the <= symbol means less than or equal) is given below:

For y = 1 To High
Print y;
For x = 1 To wide
If board(x,y) <= obstacle Then
Print Tab(x *5); Str(board(x,y));
End If
Next
Print
print
Next

The real magic occurs in the section starting with the If keyword.  The If is know as a conditional because the following code will only be executed if the condition has been met.  In this case the line

If board(x,y) <= obstacle Then

tells the computer that If the contents of the square board(x,y) is less or equal to the value of obstacle, <= obstacle then run the next bit of code.  The next bit, as we can see simply prints out the contents of the square.  The whole lot is rounded off by an End If statement to tell the computer that the conditional statement is over and it can resume normal duties.  Let's consider, for a moment, if the contents of the square were greater than or equal to 21, the value of obstacle, the condition would not be met and the program would not print the square's contents.

Our output now looks like this:




For your own amusement you can further improve the program by allowing the dimensions of the board to be changed for each run.  You could do this by asking the user for the x and y values before declaring the variables.  Similarly you could change the percentage of the obstacles by also asking for the percentage chance.  Give it a try.

The whole program

The whole program with some additional comments is given below.

/'
======================
Array Test in FreeBasic
Franktic
26 March 2017
======================
'/
Dim As Integer wide = 8
Dim As Integer high = 8

Dim As Integer x = 0
Dim As Integer y = 0

Const obstacle = 21

Dim As Integer board( 1 To wide, 1 To high)

Randomize, 1

'Fill the array with random numbers
For x = 1 To wide
For y = 1 To high
board(x,y) = Int(Rnd * 100) + 1
Next
Next

'Print out top row
For x = 1 To wide
Print  Tab(x*5); Str(x);
Next

Print

'Print out remaining rows
For y = 1 To High
Print y;
For x = 1 To wide
'print contents of square if it has an obstacle
If board(x,y) <= obstacle Then
Print Tab(x *5); Str(board(x,y));
End If
Next
Print
print
Next

Sleep
Please feel free to leave a comment and see you in the next post.

3 comments:

  1. Nice work here! Perhaps you will cover UDTs next?

    ReplyDelete
  2. Your indexing for the nested loops is inverted (which doesn't show up because wide and high are both 8). But if you dim 'wide' as 10, your code displays 8 columns and 10 rows! It should be:
    For y = 1 To high
    For x = 1 To wide
    board(y,x) = Int(Rnd * 100) + 1
    Next
    Next

    Otherwise a hugely useful series of posts. Thank you

    ReplyDelete
  3. Thanks Dave, good find. The importance of testing your code cannot be overstated. Better testing would have picked up this error early. Enjoy coding.

    ReplyDelete