Java 1.4 Game Programming (Wordware Game and Graphics Library)

An array is a list of variables (or elements, as they are generally known). Every element in an array is of the same specified type (this statement is not entirely true, as objects can be cast to different types, but do not worry about this for now; we will discuss this in detail in the next chapter). You can have an array of int variables, just as you can have an array of boolean variables or you can have an array of objects—like an array of String objects, as we shall see later on in this chapter. Arrays are generally used for creating lists of a finite length (finite meaning the array has a specific number of elements or length, hence it is not infinite). So for example, we may want to create ten variables of type int to store ten numeric values. What we would not want to do is declare ten variable names, such as:

int firstValue, secondValue; // and so on, tedious, doh

Of course, what we would do is create an array of length 10 and of type int. We can declare an array as follows:

int[] myArray;

We can also declare the same array with the following code:

int myArray[];

The square brackets can be placed either after the data type or after the variable identifier name; it is up to you, as they both do the same thing, which is declare an array. However, you may have a number of arrays that you want to declare of the same type in the same line of code, so the brackets next to the data type may be preferred.

int[] listA, listB;

Or you may prefer to declare an array and also a single data type in the same line of code that are both of the same data type.

int list[], number;

This will declare an array reference list and a single variable called number that are both of type int. Referring back to the original task of creating 10 variables of type int, the following code will do just that:

int[] myArray = new int[10];

We now have ten individual int variables that each have their individual memory places to store individual values. We use the keyword new to specify that we are creating a new array object. You will use the keyword new to explicitly create all objects in Java, so get used to it. If you want to create the array object at a later point and not when it is declared, you can do so by using single variables, as follows.

int[] myArray; // just declare for now // later in the code myArray = new int[10];

When you declare an array variable but do not assign it to an array, such as myArray, its value is null. We shall see about this in a moment when discussing arrays as objects.

Note 

Just in case this concerns you at this time, if you declare the array with the square brackets after the identifier, like int myArray[];, then assigning myArray later on is done the same way, as myArray = new int[10];, without the square brackets.

Accessing Array Elements

So how do we access these variables? Well, we access one of the variables in the array using a positive integer regarding its position in the array, known as the index. We can print the value of the first element in myArray, as follows.

int myArray = new int[10]; // print the value of the first element System.out.println("Element 0 = " + myArray[0]);

When an array is first initialized, the values of its elements are set to the default value for the type of the array. So for myArray, which is an array of type int, its elements are all equal to 0. We can assign desired values to the individual element, as follows.

myArray[0] = 369;

Notice that the index number used to access the first element in the array is zero. In computing in general, you might have heard that the first number to a computer is zero, not one ("one" is the obvious equivalent to the word "first"). This is something that you should be aware of from the beginning and at the end for that matter, with the end of an array being where the most common array error occurs. At first you might try to access the last element in the array of length 10 with the following code:

myArray[10] = 911; // hmmm, alert the police // this variable does not exist // there is no variable at index 10

The array may be of length 10, but the index of the last element is not; it is 9, as the first element was 0. Just imagine that you have an array of size 2; the first element is accessed using the index value 0, so the second element, which is also the last, must therefore be accessed using the index value 1. So the index value of the last variable in an array is the length of the array minus 1. You should stamp this fact in your mind if you are new to arrays, as this is a common misconception.

So far, we have initialized the array using the keyword new specifying the size of the array numerically. You may want to specify values to be stored in an array at the point of declaration. The following code shows how this can be done:

int[] myArray = {71, 76, 69, 78, 78, 73, 83, 71, 79, 68}; // ASCII codes?

Instead of explicitly specifying the length of the array and using the keyword new, defining this list of values assigns these values to the individual array elements and also defines the size of the array, which is the number of values in the list and in this case is 10.

Note 

This assignment technique can only be implemented when the array is declared.

So you could have a list that states whether three lights are switched on or off. If you knew this information when the array was declared, then you originally would have entered the following code.

boolean[] lightOn = new boolean[3]; lightOn[0] = true; lightOn[1] = false; lightOn[2] = true;

A less tedious way to program this when the variable is declared is as follows:

boolean[] lightOn = {true, false, true};

Arrays Are Objects

We should backtrack a little at this time and return to when we first declared an array—remember when we declared the array myArray of type int without initializing it?

int[] myArray;

At the moment, we actually have a reference to an int array object called myArray which is currently equal to null because it has not been assigned to reference an array object, which first needs to be created.

if(myArray == null) System.out.println("myArray refers to no object"); else System.out.println("myArray refers to an object");

At the very beginning of Chapter 2, we looked at the Person class and objects derived from it with references such as glennMurphy and andrewMulholland. Similarly, myArray is used to reference an array object of type int. We can set myArray to reference one array object at one time and then another at a later place in the code.

int[] myArray = new int[10]; // later on in the code myArray = new int[5];

The array reference value is declared and initialized to reference an array object of length 10, and somewhere later on, myArray is set to reference a new array object of length 5. The originally created array object of length 10 is no longer referenced and the data is lost (though the Java Virtual Machine will handle deallocating this memory with the garbage collector (see Chapter 12) for you).

Setting Values from Array to Array

Imagine we have two arrays, listA and listB, both of type int and length 5.

int[] listA = {1, 1, 2, 3, 5}; int[] listB = {0, 1, 4, 9, 16};

The following diagram represents this code with listA and listB referring to separate array objects.

Figure 3-1:

If we want to set all of the values from one array object to the other, the obvious choice may be the following code:

listA = listB; // warning, warning!!!

We have already realized that arrays are objects in Java and that listA and listB are references. This means that this method will actually make listA reference the same object that listB references, meaning from now on using either reference will affect the same object. The following diagram is an updated illustration of Figure 3-1 after the above code is added.

Figure 3-2:

If you want listA and listB to continue to reference individual objects and just make the value of one set of elements equal to another, you can cycle through each element of both arrays assigning the values one at a time. The following code will perform this task:

for(int counter=0; counter<listA.length; counter++) listA[counter] = listB[counter];

Note 

This code assumes that both listA and listB are of equal length.

We will now take a closer look at using for loops to access array elements and find out what the length member of an array object is.

Arrays with for Loops

The best way of "cycling" through all of the elements of an array is using a for loop using the loop's counter variable as the index value to access each of the array elements. For example, imagine that we wanted to set the value of all of the elements in the aforementioned array lightOn to false. We would use a for loop to perform this operation as follows.

boolean[] lightOn = {true, false, true}; // turn out the lights, turn out the lights for(int counter=0; counter<lightOn.length; counter++) lightOn[counter] = false;

This code introduces us to a new area, the length member of the array. This variable is a member of all array objects and contains the value that is the length of the array, which is 3 for the array object that lightOn references. Using the length member in the termination condition for the for loop means that later alterations to the size of the array object that lightOn references will not affect the code that sets all of the elements to false. The only thing that may be different is the number of elements in the array that are to be set to false, which the for loop will adapt to accordingly with the new array length.

The following example called FreelanceProgrammer.java imagines that you are a freelance programmer who wants to work out the average amount of money earned per month in a single year. In it we create an array of type double of length 12. Each array element (of 12) represents the amount of money earned in that respective month. This example is used not only to highlight the use of arrays but also to show some new implementation techniques when using classes, as we shall see. Let's first take a look at the code.

public class FreelanceProgrammer { public static void printEarnings() { for(int i=0; i<earnings.length; i++) System.out.println(earnings[i]); } public static void printAverageMonthlyEarnings() { double totalEarnings = 0.0; for(int i=0; i<earnings.length; i++) totalEarnings += earnings[i]; double average = totalEarnings / earnings.length; System.out.println("Average Monthly Earnings = " + average); } public static void main(String argv[]) { printEarnings(); printAverageMonthlyEarnings(); } static final double earnings[] = { 20, 80, 640, 1200, 300, 900, 800, 680, 1200, 480, 2000, 1300 }; }

When you run the program, you should get an output similar to the screen shot on the following page.

The immediate thing to note from this example is that the array variable earnings has been declared as a class member and is not local to any methods. This means that the three defined methods (main, printEarnings, and printAverageMonthlyEarnings) have access to this variable. The remainder of the code is quite self-explanatory.

Figure 3-3:

The method main is entered where the method printEarnings is invoked, which simply prints all of the values in the array to the console screen. Then the method printAverageMonthlyEarnings is invoked. This method first cycles through the array, adding up all of the earnings per month to get the total earned for the year. Then the average monthly earnings are printed to the console screen, which is the total earnings (9600) divided by the number of months (12), giving the answer (800).

Note 

Accessing an element that does not exist, such as array element number 12 in the previous example, would cause an ArrayIndexOutOfBoundsException exception to be thrown. Exceptions are objects that are "thrown" by the Java Virtual Machine when an "undesired" event occurs in your program. Exceptions are discussed in detail in Chapter 5, so try not to throw any as best you can for now, and we'll try not to as well.

Passing Arrays as Parameters

Passing arrays as parameters can sometimes be a stumbling block and needs to be pointed out. As we have seen when we assign one array reference to another using the = operator, we do not copy the element values themselves but just reference the new object instead. Passing arrays as parameters works similarly. For example, set up a method to take an array parameter, as follows.

public void gimmeSomeNumbers(int[] list) { // affects the original array for(int i=0; i<list.length; i++) list[i] = 0; // does not affect the original array list = null; }

Then we can declare an array, passing it to this method as follows.

int[] myArray = {3, 1, 4, 1, 5, 9, 2, 6}; gimmeSomeNumbers(myArray);

Now, the reason for the strange code in the gimmeSomeNumbers method was to illustrate an important part of passing the object to the array. In the first bit of code, we set all of the array elements to 0. As you have merely passed a reference to the method gimmeSomeNumbers, the variable parameter list will now access the same array object that myArray refers to. This basically means that changes made to list will affect the same object that myArray refers to. The second thing to note is that by setting list to null at the end of the method gimmeSomeNumbers, we do not affect the original variable myArray, which still references the array object. We are merely stating that list itself no longer refers to the array object, so myArray is safe.

These highlighted facts are true for any objects passed as parameters. It is always important to be aware of whether you are altering your original values when using parameters. When passing primitive data values to a method, a new value is created in the method; this was discussed at the end of the previous chapter.

Another thing you may stumble across when using arrays is when you have a group of numbers that you want to pass as an array object to a method, but the numbers are not currently in an array. A neat way to implement this is by using the previous method, gimmeSomeNumbers, as the example method to call.

int numA = 1; int numB = 14; int numC = 147; gimmeSomeNumbers(new int[] {numA, numB, numC});

That's just one of those freakish-looking anomalies that works, but it is probably most useful when you have just a single variable value and you want to pass the value as an array argument with just the one element.

Multi-dimensional Arrays

It is often the case that you will require arrays of more than one dimension—not just a list of elements but a table of elements with two dimensions, or as many dimensions as you require. The game board for the game tic-tac-toe is ideal for illustrating a two-dimensional array, storing the data (which is 3x3 squares, nine in total, for the players to place their "O" or "X") and using a two-dimensional array where both dimensions are three elements in length. This can be implemented as follows.

char board[][] = new char[3][3];

You now have nine elements of type char (3 x 3 = 9), which can be accessed by defining the position of the array element with two index values. The following table illustrates graphically the two-dimensional array for the tic-tac-toe game board and the indices of the array elements ("indices" is simply the plural for index). This should give you a good insight into how arrays are structured.

Board Indices

0

1

2

0

Element [0][0]

Element [1][0]

Element [2][0]

1

Element [0][1]

Element [1][1]

Element [2][1]

2

Element [0][2]

Element [1][2]

Element [2][2]

You could then place an X character value in the middle square on the game board, setting the value of the element at indices (1, 1) for the respective dimensions, as follows.

board[1][1] = 'X';

So let's draw a game board for tic-tac-toe using this two-dimensional array and make some pretend moves for the game, showing Os winning the game with a diagonal line of three. Here is the source code for PretendTicTacToe.java.

public class PretendTicTacToe { public static void drawBoard() { System.out.println(); // new line System.out.print(" "); for(int i=0; i<BOARD_SIZE; i++) System.out.print(" " + i); System.out.println(); // new line for(int j=0; j<BOARD_SIZE; j++) { System.out.print(j + " |"); for(int i=0; i<BOARD_SIZE; i++) System.out.print(board[i][j] + "|"); System.out.println(); // new line } System.out.println(); // new line } public static void main(String args[]) { board[0][0] = 'O'; // move 1 board[1][0] = 'X'; // move 2 board[1][1] = 'O'; // move 3 board[2][2] = 'X'; // move 4 board[0][2] = 'O'; // move 5 board[0][1] = 'X'; // move 6 board[2][0] = 'O'; // move 7 drawBoard(); System.out.println("O's have won the game"); } static int BOARD_SIZE = 3; static char[][] board = new char[BOARD_SIZE][BOARD_SIZE]; }

Here is a screen shot of the output that you should expect when you compile this source code:

Figure 3-4:

At the beginning of main, seven char values are assigned to elements of the two-dimensional array board, representing moves made in a game of tic-tac-toe at specified board positions. Then the method drawBoard is invoked. This method draws the data stored in the array board to the console screen by using two for loops to cycle through the array, with one for loop nested inside the other.

for(int i=0; i<3; i++) for(int j=0; j<3; j++) System.out.println(board[i][j]);

This code is a basic example of how two for loops work together, cycling through all of the elements in the two-dimensional array board.

In Chapter 6, "Stream I/O," we will learn about reading input from the keyboard, where we will make a complete working game of tic-tac-toe.

Multi-dimensional Multi-length Arrays

There may come a time when you do not want to create a completely rectangular two-dimensional array, like the game board for the tic-tac-toe game. For instance, you may want the second array to contain only two elements and the third array to contain only one. Using a 3x3 array for this would therefore allocate memory for three more elements than you require.

It is possible in Java to declare a two-dimensional array by specifying only the length of the first dimension and omitting the length of the second.

char[][] board = new char[3][];

We now have a two-dimensional array with three elements defined in the first dimension. Each of these three elements is a reference to a single-dimensional array object, and each of these references does not currently reference a single-dimensional array object. So we can therefore create new single-dimensional array objects of varying lengths and assign these references to them, as follows:

board[0] = new char[3]; board[1] = new char[2]; board[2] = new char[1];

The table we used earlier to illustrate the two-dimensional array for the tic-tac-toe game board would now look like this for the new game board:

Board Indices

0

1

2

0

Element [0][0]

Element [1][0]

Element [2][0]

1

Element [0][1]

Element [1][1]

No element

2

Element [0][2]

No element

No element

So how do we get the length of these individual arrays by accessing the length attribute? The length of the first dimension of the array is accessed as usual using board.length. The length of the arrays of the second dimension can be accessed similarly.

for(int counter=0; counter<board.length; counter++) System.out.println(board[counter].length);

The point to note is that board[0], board[1], and board[2] all reference array objects, just like board does. The difference is that board is a reference to a two-dimensional array object whereas board[0], board[1], and board[2] are references to one-dimensional array objects (you can see that board represents the table and board[0], board[1], and board[2] represent the columns of the table).

A good example of where you may require arrays of varying lengths is if you wanted to create a variable for every day of the year. Maybe each element, say of type int, could store the number of hours you have spent on your computer for a day in that year (what's 365 multiplied by 24 again?). You could just create an array as follows.

int[] hoursOnComputer = new int[365];

The problem with this would be if you wanted to access the days of the year based on what month it was. You would need to accumulate all of the days in the months up until the target month and then add to this figure the day of the target month, eventually giving you the index value for the element representing the day that you require. Preferably, you would implement each day as a two-dimensional array.

int[][] hoursOnComputer = new int[12][31];

Here we specify the maximum possible days in any of the months, which is 31. However, there are some months with less than 31 days in them, which means we allocate memory that is not required for the months with 30 days and February (which has 28 or 29 days, depending on whether it is a leap year).

The example ComputerHours.java creates a two-dimensional array, allocating the correct number of elements for each month in the year specified. Here we can use the switch statement that we created in Chapter 2, which returns the number of days in a given month and year.

public class ComputerHours { public static int getTotalDays(int month, int year) { switch(month) { case 0: case 2: case 4: case 6: case 7: case 9: case 11: // 31 days return 31; case 3: case 5: case 8: case 10: // 30 days return 30; case 1: // 28 or 29 days if(year % 4 != 0) return 28; else if(year % 400 == 0) return 29; else if(year % 100 == 0) return 28; else return 29; default: System.out.println("Error, Invalid month index = " + month); return -1; } } public static void main(String args[]) { int year = 2002; int totalElements = 0; int[][] hours = new int[12][]; System.out.println("The year is " + year); for(int i=0; i<hours.length; i++) hours[i] = new int[getTotalDays(i, year)]; for(int i=0; i<hours.length; i++) { System.out.println("Month "+i+" contains "+hours[i].length+" days"); for(int j=0; j<hours[i].length; j++) { hours[i][j] = 24; totalElements++; } } System.out.println("Total elements allocated = " + totalElements); } }

When you run this program, the output should be similar to the screen shot on the following page.

First, we declare and initialize a two-dimensional array, only specifying that it contains 12 elements that are empty references to undefined array objects of one dimension.

int[][] hours = new int[12][];

Figure 3-5:

Then we can initialize these 12 array elements to reference one-dimensional array objects of a specified length on the fly. These lengths are retrieved from the method getTotalDays(int month, int year). This method returns the number of days in the month and year, defined as arguments to the method.

for(int i=0; i<hours.length; i++) hours[i] = new int[getTotalDays(i, year)];

Finally, all of the array data elements are assigned the value of 24, and each time the variable totalElements is incremented by 1.

for(int i=0; i<hours.length; i++) { System.out.println("Month "+i+" contains "+hours[i].length+" days"); for(int j=0; j<hours[i].length; j++) { hours[i][j] = 24; totalElements++; } }

The total number of elements assigned a value is printed to the console screen. In this case it is 365 (as you will see when compiling and running the program), which is the correct number of days for the year 2002. Try setting the year to a leap year, like the year 2004. The total elements allocated should change to 366.

As we have seen, arrays are very useful for listing data of a finite length; that is, you know what the length is going to be when the array object is created. Arrays are not always ideal for listing though. In Chapter 5 we will look at more dynamic means of data storage with classes such as LinkedList and ArrayList.

Категории