Let's build a GUI for the Tower of Hanoi:
import java.util.*; public class TowerOfHanoi4 { // stacks[0], stacks[1] and stacks[2] are the three stacks. static Stack<Integer>[] towers; static HanoiGUI hanoiGUI; public static void main (String[] argv) { // A 4-disk puzzle: System.out.println ("4-Disk solution: "); solveHanoi (3, 0, 1); } static void solveHanoi (int n, int i, int j) { // Create the three stacks and initialize ... this code is the same ... // GUI. Note: n+1 = # disks. We will pass in the stacks. hanoiGUI = new HanoiGUI (towers, n+1); // Now solve recursively as before. solveHanoiRecursive (n, i, j); } static void solveHanoiRecursive (int n, int i, int j) { // ... } static void move (int n, int i, int j) { // Pull out the top disk on stack i and move to j ... same as before ... // This method will handle re-drawing the towers/disks. hanoiGUI.updateGUI (); // Pause execution for animation effect. try { Thread.sleep (1000); } catch (InterruptedException e) { } } static int other (int i, int j) { // ... } }
Exercise 1: Download TowerOfHanoi4.java and the file HanoiGUI.class. Then compile and execute TowerOfHanoi4. You should see an animation of the solution. Next, download HanoiGUITemplate.java. Save the latter as HanoiGUI.java. Then compile and execute. You will notice that the GUI code in the template only draws the three towers. You are to fill in the code to draw the disks. All you need to do is call g.fillRect(), the parameters of which are: the topleft corner (first two parameters) and the width and height.
Let us examine the class ChessBoard: (source file)
public class ChessBoard { // Constructor. public ChessBoard (int size) { // Build the board ... } public int size () { // Return the size ... } public void addQueen (int row, int col) { // Add a queen to the square [row,col] ... } public void removeQueen (int row, int col) { // Remove a queen from the square [row,col] ... } public boolean isForbidden (int row, int col) { // Check whether [row,col] is attacked ... } }
public class ChessBoard { // Instance variables. int size; char[][] board; // board[i][j] == 'X' if there's a queen on it. // Constructor. public ChessBoard (int size) { } // ... }
public class ChessBoard { // Instance variables. int size; char[][] board; // board[i][j] == 'X' if there's a queen on it. // Constructor. public ChessBoard (int size) { // Build the board. Initially empty: board[i][j] == 'O'. this.size = size; board = new char [size][size]; for (int i=0; i < size; i++) { for (int j=0; j < size; j++) { board[i][j] = 'O'; } } } public int size () { return size; } // ... }Thus, we use the character 'X' to represent the presence of a queen and 'O' to represent an empty square.
public class ChessBoard { // ... public void addQueen (int row, int col) { board[row][col] = 'X'; } public void removeQueen (int row, int col) { board[row][col] = 'O'; } // ... }
public boolean isForbidden (int row, int col) { // First, try the row. for (int c=0; c < size; c++) { if (board[row][c] == 'X') { return true; } } // Now try the column for (int r=0; r < size; r++) { if (board[r][col] == 'X') { return true; } } // Now the diagonals. for (int r=0; r < size; r++) { for (int c=0; c < size; c++) { if ( (r != row) && (c != col) && (board[r][c] == 'X') ) { // See if (r,c) can be attacked by (row,col). if (Math.abs(r-row) == Math.abs(c-col)) { return true; } } } } // If we reach here, there's no queen attacking [row,col] return false; }For the diagonal:
The class ChessBoard has the code for displaying the board in a GUI. Let's examine some of this code:
public void display () { // Make the frame and set some of its parameters: JFrame f = new JFrame (); f.setSize (300,300); f.setTitle ("N Queens problem"); // Create our extension of the JPanel: ChessPanel drawPanel = new ChessPanel (); // We'll need to pass on the data: drawPanel.size = size; drawPanel.board = board; // Add this to the frame. Notice the strange syntax. f.getContentPane().add (drawPanel); // Bring up the frame. f.setVisible (true); }
class ChessPanel extends JPanel { int size; char[][] board; public void paintComponent (Graphics g) { // ... This is where all the drawing is done ... } }
Exercise 2: Download ChessBoard.java and NQueens.java. You will also need ImageTool.java and queen.jpg. Then, replace the char array in ChessBoard with an int equivalent. What changes do you need to make in the code? Would it be possible to use a boolean array?
An alternative data structure:
public class ChessBoard2 { int size; int[] columns; // column[r] = which column in row r has a queen, if any // column[r] = -1, if this row has no queen // ... }
public void addQueen (int row, int col) { columns[row] = col; } public void removeQueen (int row, int col) { columns[row] = -1; }
public boolean isForbidden (int row, int col) { // First, try the row. if (columns[row] >= 0) { return true; } // Now try the columns. for (int c=0; c < size; c++) { if (columns[row] == col) { return true; } } // Now the diagonals. for (int r=0; r < size; r++) { if (columns[r] >= 0) { if (Math.abs(columns[r]-col) == Math.abs(row-r)) { return true; } } } return false; }
Exercise 3:
Download ChessBoard2.java and
NQueens2.java and read through
the implementation of ChessBoard2.
Apart from the space savings, what is the advantage of
using the 1D array?