9. Control Structures

9.5. Graphics Example: Drawing a Checkerboard

In this section we will combine some of the graphics methods we have learned with the nested for-loop structure to draw a checkerboard with checkers on it (Fig. 6.4). For this example, we will just concentrate on drawing the checkerboard. We will not worry about providing a full checkerboard representation, of the sort we would need if we were writing a program to play the game of checkers. So, our design will not involve in- stance variables for whose turn it is, how many checkers each player has, where the checkers are located on the board, and other elements of the game’s state. Still, our visible representation of the board should be de-

signed so that it will eventually be useful when we do develop a checkers

game in Chapter 8.

 

Problem Description and Specification

The specification for this problem is to develop a program that will draw a checkerboard and place upon it the checkers in their appropriate starting positions. As with many of our programs, our design will involve two classes, one which defines the user interface and the other which repre- sents the computational object. For this problem, the computational object will be defined in the CheckerBoard class. The details of its design are described in the next section.

Because the purpose of this example is to focus on how we use loops and drawing methods, we will employ a very simple JFrame interface, whose implementation is given in Figure 6.5. As shown there, the program simply creates a CheckerBoard instance

,,


Figure 6.4: A checkerboard with checkers.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

J

Figure 6.5: The CheckerBoardFrame class.

 

in its CheckerBoardFrame() constructor, and then invokes the

CheckerBoard’s draw method in its paint() method. The reason we

 

 

 

 

 

 

 

 

 

 

 

Figure6.6:Designofthe

CheckerBoard class.


invoke the draw() method in paint() is because we need to have ac- cess to the JFrame’s Graphics context, which is passed as an argument to the draw() method. Recall that the main() method is invoked when CheckerBoardFrame is run,and then the paint() methods are invoked automatically by calling the setVisible(true) method in main(). Thus, the action taken by this program is simply to draw a visual rep- resentation of the checkerboard.

Class Design: CheckerBoard

Because the program will invoke its draw() method, this method must be part of the CheckerBoard’s interface. Hence, it must be declared public. The task of drawing a checkerboard involves two distinct sub- tasks: (1) drawing the board itself, which will involve drawing a square with smaller squares of alternating colors; and, (2) drawing the check- ers on the checkerboard. A good design for the draw() method would be simply to invoke helper methods that encapsulate these two subtasks. This is good method design because it results in relatively small methods, each of which performs a very well-defined task. Let’s call these methods drawBoard() and drawCheckers(), respectively. Their signatures are shown in Figure 6.6, which summarizes the design of the CheckerBoard class.

Before gettinginto the details of the drawBoard and drawCheckers() methods, we must first discuss CheckerBoard’s several instance vari- ables. The first two variables LEFT X and UPPER Y, give the absolute po- sition of the upper left corner of the checkerboard on the JFrame’s drawing panel. The SQ SIDE variable gives the size of the checkerboard’s individ- ual squares. N ROWS and N COLS give the number of rows and columns in the checkerboard (typically, 8 by 8). All of these variables are integers. The final four variables, SQ COLOR1, SQ COLOR2, CHECKER COLOR1, and CHECKER COLOR2, specify the colors of the checkerboard and the check- ers.

Note that the names of all the instance variables are written in upper- case letters. This is to identify them as symbolic constants—that is, as fi- nal variables whose values do not chage once they are initialized. Because their actual values are not important, we do not show them in the UML diagram and we won’t discuss them here. Recall that the advantage of defining class constants, rather than sprinkling literal values throughout the program, is that they make it easy to modify the program if we decide to change the size, location, or color of the checkerboard.

Method Design

Returning now to the design of CheckerBoard’s instance methods, the complete definition of the CheckerBoard class is given in Figure 6.7. Note how simple its draw() method is. As we noted earlier, in order to using Java’s drawing commands, it is necessary to have a reference to a Graphics object. This is passed to the draw() method when the draw() method is invoked in the program. Because the draw() method delegates the details of the drawing algorithm to its helper methods, drawBoard() and drawCheckers(), it has to pass them a reference to the Graphics object.

The drawBoard() method uses a nested for loop to draw an 88

array of rectangles of alternating colors. The loop variables, row and col,

 

,,

import j ava . awt . ;

public c l a s s CheckerBoard {

// D e f a u l t v a l u e s f o r a s t a n d a r d c h e c k e r b o a r d private f i n a l in t LEFT X = 1 0 ;// P o s i t i o n o f l e f t private f i n a l in t UPPER Y = 1 0 ;// u p p e r c o r n e r private f i n a l in t SQ SIDE = 4 0 ;// S i z e o f e a c h s q u a r e private f i n a l in t N ROWS = 8 ;// C h e c k e r b o a r d r o w s private f i n a l in t N COLS = 8 ;// C h e c k e r b o a r d c o l u m n s private f i n a l Color SQ COLOR1 = Color . l ight Gray ; // C o l o r s private f i n a l Color SQ COLOR2 = Color . gray ;// o f s q u a r e s private f i n a l Color CHECKER COLOR1 = Color . white ;// a n d

private f i n a l Color CHECKER COLOR2 = Color . black ; // c h e c k e r s

 

private void drawBoard ( Graphics g )

for ( in t row = 0 ; row < N ROWS; row++)

// F o r e a c h r o w

for ( in t c o l = 0 ; c o l < N COLS ; c o l ++)// F o r e a c h s q u a r e

i f ( ( row + c o l ) % 2 == 0 )

// A l t e r n a t e c o l o r s

g . set Color ( SQ COLOR1 ) ;

// L i g h t

e ls e

g . set Color ( SQ COLOR2 ) ;

// o r d a r k

g . f i l l R e c t ( LEFT X+c o l SQ SIDE ,

UPPER Y+rowSQ SIDE , SQ SIDE , SQ SIDE ) ;

} // f o r

// d r a w B o a r d ( )

private void drawCheckers ( Graphics g )// P l a c e c h e c k e r s

for ( in t row = 0 ; row < N ROWS; row++)// F o r e a c h r o w

for ( in t c o l = 0 ; c o l < N COLS ; c o l ++) // F o r e a c h s q u a r e

i f ( ( row + c o l )%2 == 1 )// One p l a y e r h a s t o p 3 r o w s

i f ( row < 3 )

g . set Color (CHECKER COLOR1 ) ;

g . f i l l O v a l ( LEFT X+c o l SQ SIDE ,

UPPER Y+rowSQ SIDE , SQ SIDE2 ,SQ SIDE 2 ) ;

// i f

i f ( row >= N ROWS3 )// O t h e r h a s b o t t o m 3 r o w s

g . set Color (CHECKER COLOR2 ) ;

g . f i l l O v a l ( LEFT X+c o l SQ SIDE ,

UPPER Y+rowSQ SIDE , SQ SIDE2 ,SQ SIDE 2 ) ;

}// i f

}// i f

// d r a w C h e c k e r s ( )

public void draw ( Graphics g )// Draw b o a r d a n d c h e c k e r s

drawBoard ( g ) ; drawCheckers ( g ) ;

}// d r a w ( )

// C h e c k e r B o a r d

\J

Figure 6.7: The CheckerBoard class.

 

 

 

 

 

 

 

 

| 0 1 2 3

-----------------

0 |

0

1

2

3

1 |

1

2

3

4

2 |

2

3

4

5

3 |

3

4

5

6


both range from 0 to 7. The expression used to determine alternating colors tests whether the sum of the row and column subscripts is even: ((row + col)%2 == 0). If their sum is even, we use one color; if odd, we use the other color.

As the table in the margin shows for a 4 4 board, the sum of a board’s row and column subscripts alternates between even and odd values. Thus, in row 2 column 3, the sum of the subscripts is 5.

To switch from one color to the other, we use the Graphics setColor() method to alternate between the two colors designated for the checkerboard, SQ COLOR1 and SQ COLOR2. We then use the following method call to draw the colored squares:

,,

 

 

J

Note how we use the loop variables, row col, together with the constants specifying the top left corner of the board (UPPER Y and LEFT X) and the size of the squares (SQ SIDE) to calculate the location and size of each square. The calculation here is illustrated in Figure 6.8. The first two pa- rameters in fillRect(left,top,width,height) specify the coordi- nates for the rectangle’s top-left corner. These are calculated as a func- tion of the rectangle’s row and column position within the checkerboard and the rectangle’s width and height, which are equal for the squares of a checkerboard.

 

 

 

 

 

 

Figure 6.8: Calculating the loca- tions of the checkerboard squares.


 

 

 

 

 

 

The drawCheckers() method also uses a nested for loop to trace through the checkerboard’s rows and columns. In this case, however, we draw checkers on just the dark-colored squares—that is, those that sat- isfy the expression (row + col)%2 == 1)—on the first three rows of each

 

player’s side of the board. So, each player’s checkers initially are located in the first three rows and last three rows of the checker board:

,,

 

 

 

 

 

 

 

 

J

Because the checkers are circles, we use the fillOval() method to draw them. Note that the parameters for fillOval(left, top, width, height) are identical to those for fillRect(). The parameters spec- ify an enclosing rectangle in which the oval is inscribed. In this case, of course, the enclosing rectangle is a square, which causes fillOval() to draw a circle.

Our design of the CheckerBoard class illustrates an important princi- ple of method design. First, rather than placing all of the commands for drawing the checkerboard and the checkers into one method, we broke up this larger task into distinct subtasks. This resulted in small methods, each of which has a well defined purpose.