Eighth Assignment: Magic Squares
CIT 591, David Matuszek, Fall 2001

Purposes of this assignment:

What a magic square is:

A magic square is an array of numbers from 1 to N squared, such that the sum of the numbers in each row, each column, and each of the two diagonals is the same. For example, here is a 3x3 magic square:

8 1 6
3 5 7
4 9 2

Rows:
8 + 1 + 6 = 15
3 + 5 + 7 = 15
4 + 9 + 2 = 15

Columns:
8 + 3 + 4 = 15
1 + 5 + 9 = 15
6 + 7 + 2 = 15
Diagonals:
8 + 5 + 2 = 15
4 + 5 + 6 = 15

How to create a magic square:

There is a simple algorithm for creating an odd-order magic square (a magic square with an odd number of rows and columns).

Start by putting the number 1 in the center of the top row:
    1    
         
         
         
         
    1    
         
         
        3
      2  
Count upward and to the right. If you go off the top, drop down to the bottom. (That is, if the row number becomes negative, reset it to the array length minus one.)
Continue counting upward and to the right. If you go off the right, come back in on the left. (Hint: use the modulo operator, %.)
    1    
  5      
4        
        3
      2  
    1    
  5      
4 6      
        3
      2  
Continue counting upward and to the right. If you reach a square that is already occupied, go directly under the previous number.
Continue in this manner as long as possible.
    1 8 15
  5 7 14  
4 6 13    
10 12     3
11     2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

When you go off the top right corner, the same rules apply; the next square is already occupied (by 11), so drop directly below the 15. Then continue.

This technique works for any odd square.

If you are having trouble understanding how the "wrapping around" works in the above algorithm, it may help to think of the array as being surrounded by identical ghost copies of itself. As you move up and to the right, if you go off the edge of the array, go ahead and put the next number into the surrounding array, and that will show you where the number belongs in the original, "real" array.

For example, going diagonally upward from the initial 1, look where the 2 ends up in an adjacent array; this is where it should be in the original array (because it is identical to the "ghost" array).

This is only an aid for understanding; you should not use nine arrays in your program.

17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9

Your assignment:

  Write a program that creates and displays magic squares. Here are the specific requirements:

  • Your program must be an application, not an applet.
  • Use a BorderLayout, and put:
    • Your name in the NORTH.
    • The magic square in a panel in the CENTER.
    • A panel containing two buttons in the SOUTH .
  • For the magic square,
    • Use a GridLayout with N rows and N columns.
    • Create and install a Label for each number in the magic square.
  • Put these two buttons in the panel in the SOUTH:
    • Smaller: When clicked, creates and displays a new magic square with two fewer rows and two fewer columns. Disabled if an only if the displayed magic square is 1x1.
    • Larger: When clicked, creates and displays a new magic square with two more rows and two more columns. Always enabled.
  • Attach a WindowListener that will close the window and quit the program when the close box is clicked.

The result should look very much like this:

Suggestions:

I did several versions of this program before I found an organization that worked well. If you would like to use the same organization, here it is. I used the following three classes:

public class MagicSquareMaker
Contains the public static void main(String args[]) method, and nothing else. The only thing the main method does is create an instance of the Gui. (I did it this way because it's a nuisance trying to do anything inside a static method.)
public class Gui extends Frame
Sets up the Graphical User Interface, calls a constructor to make the initial new MagicSquare, displays the magic square, and adds a listener to handle window closing events.
public class MagicSquare
Has a constructor that takes an odd positive integer argument, and creates a magic square of that order. In addition, it contains a method that takes a Panel as an argument, removes the previous contents (if any) of the Panel, attaches a GridLayout manager to the Panel, and creates and add N*N numeric labels to the Panel, where N is the order of this magic square.

That is,

public class MagicSquareMaker {

    public static void main(String args[]) {
        Gui gui = new Gui(5);
    }
}
public class Gui extends Frame {
    public Gui(int sizeOfMagicSquare) {...} // constructor
    class WindowCloser extends WindowAdapter {...}
}
public class MagicSquare {
    public MagicSquare(int size) {...} // constructor
    public void fillPanel(Panel panel) {...}
}

In this organization, the "Controller" and "View" are merged into the single class Gui, while the MagicSquare represents the "Model." This separation is not perfect, however, because some class has to take the responsibility of creating a Panel full of Labels from the information in the MagicSquare class. I decided to do this in the MagicSquare class, because it seemed better to use a Panel (which is a GUI thing) in the MagicSquare class than to give the Gui class access to all the numbers inside the magic square.

Another way to do this would have been to have a separate View class, and to write a method public int[][] getArray() in the MagicSquare class; the View class would use this to get the numbers it needs.

Necessary information:

A Frame, like an Applet, is a Container. Applets are usually embedded in web pages, while frames are usually used in applications (stand-alone programs). The most important differences between a Frame and an Applet are:

For an application, in order to respond to a WindowClosing event, you need a WindowListener to the Frame:

    addWindowListener(new MyWindowCloser());

The actual listener (best implemented as a member class) looks like this:

    class MyWindowCloser extends WindowAdapter {    
        public void windowClosing(WindowEvent e) {
            dispose();
            System.exit(0);
        }
    }

Usually, when you create a GUI, you don't keep changing the layout managers or the components. If you do, you have to do a little extra work.

removeAll()
This message, sent to a Container, will remove all the Components in that Container. Use this to remove the old Labels from your magic square, before changing the layout manager and putting in the new Labels.
validate()
This message, sent to a Container, will tell Java that you have changed the contents of that Container, and Java needs to redraw it for you.

Due date:

This program is due by midnight on Wednesday, November 14. Please turn in a complete BlueJ package via Blackboard.

Late penalties of 5 points per day will apply. No programs will be accepted after Wednesday, November 21.