Solution for Programming Exercise 3.8
This page contains a sample solution to one of the exercises from Introduction to Programming Using Java.
Exercise 3.8:
Write a GUI program that draws a checkerboard. Base your solution on the sample program SimpleAnimationStarter.java, even though you are creating only a static picture rather than an animation. You will draw the checkerboard in the drawFrame() subroutine. You should read the comments in the file to discover other changes that you might need to make.
Assume that the size of the drawing area is 400-by-400 pixels. A checkerboard contains 8 rows and 8 columns of squares. If the size of the drawing area is 400, that means that each square can be 50-by-50 pixels. The squares are red and black (or whatever other colors you choose). Here is a tricky way to determine whether a given square should be red or black: The rows and columns can be thought of as numbered from 0 to 7. If the row number of the square and the column number of the square are either both even or both odd, then the square is red. Otherwise, it is black. Note that a square is just a rectangle in which the height is equal to the width, so you can use the subroutine g.fillRect() to draw the squares. Here is a reduced-size image of the checkerboard that you want to draw:
The basic algorithm is obvious:
for each row of the checkerboard: Draw all the squares in that row
Since any given row contains eight squares, one in each column of the checkerboard, we can expand the body of the for loop into another for loop:
for each of the eight rows of the checkerboard: for each of the eight columns: Draw the square in that row and column
Each square is a rectangle with height 50 and width 50, so it can be drawn with the command g.fillRect(x,y,50,50), where x and y are the coordinates of the top-left corner of the square. Before drawing the square, we have to determine whether it should be red or black, and we have to set the correct color with g.setColor. So, the algorithm becomes
for each row on the checkerboard: for each of the eight columns: Compute x,y for the top-left corner of the square if it's a red square: g.setColor(Color.RED) else g.setColor(Color.BLACK) g.fillRect(x,y,20,20)
The top of the first row of squares is at y=0. Since each square is 50 pixels high, the top of the second row is at y=50, followed by 100 for the third row, then 150, 200, 250, 300, and 350. If we assume that the rows are numbered 0, 1, 2, ..., 7, then the tops are given by y = row*50, where row is the row number. (If you number the rows 1, 2, ..., 8, the formula would be (row-1)*20. The simpler formula in this and in many similar cases is one reason why computer scientists like to start counting with 0 instead of 1.) Similarly, the left edge of the squares in column col is given by x = col*50, where again the columns are numbered 0, 1, 2, ..., 7. I'll use "for (row=0; row<8; row++)" to count off the rows, rather than the equivalent "for (row=0; row<=7; row++)". The 8 reminds me that I am counting off the eight numbers 0, 1, 2, ..., 7. Again, this is typical computer science style.
The only problem remaining is how to determine whether the square is red. As noted in the exercise, a square is red if row and col are either both even or both odd. Since an integer N is even if N%2 is 0, the test could be expressed as
if ((row%2 == 0 && col%2 == 0) || (row%2 == 1 && col%2 == 1))
However, note that this is the same as asking whether row%2 and col%2 have the same value. So the test can be written more simply as "if (row%2 == col%2)". Putting this all together into syntactically correct Java code, the algorithm becomes
for ( row = 0; row < 8; row++ ) { for ( col = 0; col < 8; col++ ) { x = 50*col; y = 50*row; if ( (row % 2) == (col % 2) ) g.setColor(Color.RED); else g.setColor(Color.BLACK); g.fillRect(x,y,50,50); } }
Of course, the variables row, col, x, and y have to be declared to be of type int. Then, the code goes into the body of the drawFrame()subroutine.
The complete program is shown below. In the original program, the size of the drawing area was 600-by-450. As noted in a comment, to change the size to 400-by-400, I had to change one of the lines in the main routine, replacing 600 and 450 by 400 to get:
drawingArea.setPreferredSize(new Dimension(400,400));
You don't need to understand what this means or how it works to make the change. I also wanted to stop the user from changing the size of the window, to change the text in the title bar of the window, and to change the name of the class from SimpleAnimationStarter to Checkerboard. All these things were easy to do following instructions in the program. (That leaves the program drawing the same checkerboard 50 times every second. Although the comments in the original program don't tell you how to prevent that, I did it by commenting out the very last line of main(). Without that line, the animation is never started, and the checkerboard doesn't get redrawn over and over unnecessarily.)
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Checkerboard extends JPanel implements ActionListener { public void drawFrame(Graphics g, int frameNumber, int width, int height) { int row; // Row number, from 0 to 7 int col; // Column number, from 0 to 7 int x,y; // Top-left corner of square for ( row = 0; row < 8; row++ ) { for ( col = 0; col < 8; col++) { x = col * 50; y = row * 50; if ( (row % 2) == (col % 2) ) g.setColor(Color.RED); else g.setColor(Color.BLACK); g.fillRect(x, y, 50, 50); } } } //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------ public static void main(String[] args) { JFrame window = new JFrame("Checkerboard"); Checkerboard drawingArea = new Checkerboard(); drawingArea.setBackground(Color.WHITE); window.setContentPane(drawingArea); drawingArea.setPreferredSize(new Dimension(400,400)); window.pack(); window.setLocation(100,50); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setResizable(false); // The user can't change the size. Timer frameTimer = new Timer(20,drawingArea); window.setVisible(true); //frameTimer.start(); // commented out so we don't get an animation } // end main private int frameNum; public void actionPerformed(ActionEvent evt) { frameNum++; repaint(); } protected void paintComponent(Graphics g) { super.paintComponent(g); drawFrame(g, frameNum, getWidth(), getHeight()); } }