By Michael Hartley
On another page there's a java applet that howto make a magic square of any size. On this page the magic square java code is available. Why? Simply because the program is "open source". For more information about what that means, see this page.Ten years ago, a mathematician called Yasusi Kanada wrote the original java code. It uses some advanced math techniques to find magic squares as quickly as possible. He made the code available as open software under the "GNU Public Licence"
I downloaded the code, and changed it to make it a little more user friendly. Now, my version of the magic square java code is below. Enjoy!
Yours, Dr Mike...
import java.awt.*;
import java.applet.*;
import java.util.*;
public class MagicSquare extends Applet implements Runnable {
/************************************************************************/
/* */
/* Class MagicSquare: Magic Square Finder */
/* */
/* Copyright (C) 1996 by Yasusi Kanada (v1.05) */
/* Copyright (C) 2006 by Michael Hartley (v1.06) */
/* */
/* This program can be freely distributed under the GNU General */
/* Public License. */
/* */
/* Initial Version = "ver 1.00, 1996-8-13"; */
/* Next Version = "ver 1.05, 1996-11-23"; */
String Version = "ver 1.06, 2006-02-11";
/* */
/************************************************************************/
/*======================================================================*/
/* Class global objects and methods */
/*======================================================================*/
protected int n = 4; /* default: 4 x 4 square */
private Thread solver;
private int min(int x, int y) {
return x <= y ? x : y;
}
/*======================================================================*/
/* Widget handler */
/*======================================================================*/
private TextField message_box; /* public inside the class */
private TextField n_box; /* to specify n (#queens) */
private TextField initial_frustration_box;
private TextField frustration_factor_box;
private void init_widgets() {
setLayout(new FlowLayout());
// add(new Label("Magic square solver using CCM (Chemical Casting Model) -- " +
// Version + " --"));
add(new Button("Stop"));
add(new Button("Start"));
Panel major_option_panel = new Panel();
major_option_panel.add(new Label("Size ="));
n_box = new TextField("4 ");
major_option_panel.add(n_box);
init_speed_choice(major_option_panel);
init_rule_choice(major_option_panel);
add(major_option_panel);
Panel minor_option_panel = new Panel();
minor_option_panel.add
(new Label("Minor options: Initial frustration ="));
initial_frustration_box = new TextField("1e-15 ");
minor_option_panel.add(initial_frustration_box);
minor_option_panel.add(new Label("Frustration factor ="));
frustration_factor_box = new TextField("2.0 ");
minor_option_panel.add(frustration_factor_box);
// add(minor_option_panel);
message_box = new TextField(50);
add(message_box);
}
public boolean action(Event e, Object arg) {
message_box.setText("");
if (speed_action(arg) || rule_action(arg)) {
return true;
} else if ("Start".equals(arg)) {
stop();
start();
return true;
} else if ("Stop".equals(arg)) {
stop();
return true;
} else {
return false;
}
}
/* Choice extension: */
private Choice new_choice(String[] labels, int default_index,
Panel p) {
Choice c = new Choice();
for (int i = 0; i < labels.length; i++) {
c.addItem(labels[i]);
};
c.select(default_index);
// p.add(c);
return c;
}
private int find_index(Object arg, String[] labels) {
for (int i = 0; i < labels.length; i++) {
if (labels[i].equals(arg)) {
return i;
};
};
return -1; /* not found */
}
/* Speed control: */
final int[] Sleep_time = {333, 50, 0}; /* in mili seconds */
final int Medium_speed_index = 1;
final int Fast_speed_index = 2;
final int Full_speed_index = 3;
final int Speed_default_index = Full_speed_index;
final String[] Speed_choice_labels =
{"Slow speed (3 rps)", "Medium speed (20 rps)",
"Fast speed", "Full speed"};
private int speed_selected_index;
private Choice speed_choice;
private void init_speed_choice(Panel p) {
/* Speed choice is created and initialized. */
speed_choice =
new_choice(Speed_choice_labels, Speed_default_index, p);
speed_selected_index = Speed_default_index;
}
private boolean speed_action(Object arg) {
/* Event handler for speed control choice. */
/* Warning: This function is called not only when the event is */
/* caused by the speed control choice. */
int index = find_index(arg, Speed_choice_labels);
if (index >= 0) { /* found */
speed_selected_index = index;
display_switch =
index == Fast_speed_index || index == Full_speed_index
? false /* no display while executing */
: true;
return true;
} else { /* not found */
return false;
}
}
/* Rule choice: */
final int Swap_index = 0;
final int Rotate_index = 1;
final int[] Rules = {Swap_index, Rotate_index};
final int Rule_default_index = Swap_index;
final String[] Rule_choice_labels = {"Swapping rule", "Rotation rule"};
private int rule_selected_index;
private Choice rule_choice;
private void init_rule_choice(Panel p) {
/* Rule choice is created and initialized. */
rule_choice =
new_choice(Rule_choice_labels, Rule_default_index, p);
rule_selected_index = Rule_default_index;
}
private boolean rule_action(Object arg) {
int index = find_index(arg, Rule_choice_labels);
if (index >= 0) { /* found */
rule_selected_index = index;
return true;
} else {
return false;
}
}
/*======================================================================*/
/* Magic square viewer */
/*======================================================================*/
protected boolean display_switch = false;
/* To display the map or not */
final int Common_offset = 8;
protected int x_offset = 0;
protected int y_offset = 170;
protected int drawn_n;
protected int queen_x_offset, queen_y_offset;
int drawing_size, drawing_step, queen_size;
int font_size;
private void init_view() {
}
private void start_view() {
drawing_step =
(min(size().width - x_offset, size().height - y_offset) -
2 * Common_offset) / n;
if (drawing_step <= 0) {
message_box.setText("Applet size too small! ");
drawing_step = 0;
};
drawing_size = drawing_step * n;
font_size = drawing_step * 3 / 5;
}
public void paint(Graphics g) {
int x0 = x_offset + Common_offset;
int y0 = y_offset + Common_offset;
for (int i = 0; i <= n; i++) {
int grid_coord = i * drawing_step;
g.drawLine(x0, y0 + grid_coord,
x0 + drawing_size - 1, y0 + grid_coord);
g.drawLine(x0 + grid_coord, y0,
x0 + grid_coord, y0 + drawing_size - 1);
};
x0 = x0 + drawing_step / 10;
y0 = y0 + drawing_step / 10;
Font old_font = g.getFont();
g.setFont(new Font("TimesRoman", Font.PLAIN, font_size));
for (int i = 0; i < wm_size; i++) {
g.drawString
("" + value_wm[i],
x0 + x_coord_wm[i] * drawing_step,
y0 + y_coord_wm[i] * drawing_step + drawing_step * 2 / 3);
};
g.setFont(old_font);
}
/*======================================================================*/
/* Solver */
/*======================================================================*/
double initial_frustration = 1.0e-15;
double frustration_factor = 2.0;
protected int expected_summation;
/* Working memory: */
private int[] value_wm, x_coord_wm, y_coord_wm;
private double[] frustration_wm;
private int[][][] summation_wm;
private int[] n_summations;
private int wm_size;
private int n_tests;
private int n_reactions;
private int n_failed_tests;
/* Order degree: */
private int order(int index, int value_difference) {
int sum = 0;
for (int i = 0; i < n_summations[index]; i++) {
int diff = expected_summation -
(summation_wm[index][i][0] + value_difference);
sum -= diff * diff;
}
return sum;
}
private double mean_order() {
/* Returns the mean order degree of the current WM. */
int total_order = 0;
for (int i = 0; i < wm_size; i++) {
total_order += order(i, 0);
};
return total_order / (double)wm_size;
}
private void init_solver() {
}
private double get_option(TextField option, String format_error_message,
double minimum_value, String value_error_message,
double default_value) {
double value;
try {
value = (new Double(option.getText())).doubleValue();
/* value = Integer.parseInt(option.getText()); */
} catch (NumberFormatException e) {
message_box.setText(format_error_message);
value = default_value;
};
if (option==n_box) option.setText(""+(int)value); else option.setText("" + value);
if (value < minimum_value) {
message_box.setText(value_error_message);
value = default_value;
};
return value;
}
/* Random color/index generators */
private Random selector = new Random();
private int random(int max) {
return (selector.nextInt() & 0x7FFFFFFF) % max;
}
private void update_summations(int index, int difference) {
for (int i = 0; i < n_summations[index]; i++) {
summation_wm[index][i][0] += difference;
};
}
/* Rules */
private void swap_rule() {
/* A rule to swap two columns of the magic square */
int index1, index2;
n_tests++;
/* Select columns to apply the rule: */
index1 = random(wm_size);
do {
index2 = random(wm_size);
} while (index2 == index1);
/* Compute LODs: */
int old_order = order(index1, 0) + order(index2, 0);
int new_order = order(index1, value_wm[index2] - value_wm[index1]) +
order(index2, value_wm[index1] - value_wm[index2]);
if (old_order >= 0) { /* A local termination condition holds */
n_failed_tests++;
} else if (((double)old_order - frustration_wm[index1]
- frustration_wm[index2]) >
new_order) {
/* not to be reacted */
n_failed_tests++;
frustration_wm[index1] *= frustration_factor;
frustration_wm[index2] *= frustration_factor;
} else {
n_reactions++;
n_failed_tests = 0;
int v1 = value_wm[index1];
int v2 = value_wm[index2];
update_summations(index1, v2 - v1);
update_summations(index2, v1 - v2);
value_wm[index1] = v2;
value_wm[index2] = v1;
if (display_switch) {
repaint();
try {solver.sleep(Sleep_time[speed_selected_index]);}
catch (InterruptedException e) {};
};
frustration_wm[index1] = initial_frustration;
frustration_wm[index2] = initial_frustration;
};
}
private void rotate_rule() {
/* A rule to rotate three columns of the magic square */
int index1, index2, index3;
n_tests++;
/* Select columns to apply the rule: */
index1 = random(wm_size);
do {
index2 = random(wm_size);
} while (index2 == index1);
do {
index3 = random(wm_size);
} while (index3 == index1 || index3 == index2);
/* Compute LODs: */
int old_order =
order(index1, 0) + order(index2, 0) + order(index3, 0);
int new_order = order(index1, value_wm[index2] - value_wm[index1]) +
order(index2, value_wm[index3] - value_wm[index2]) +
order(index3, value_wm[index1] - value_wm[index3]);
if (old_order >= 0) { /* A local termination condition holds */
n_failed_tests++;
} else if (((double)old_order - frustration_wm[index1]
- frustration_wm[index2] - frustration_wm[index3]) >
new_order) {
/* not to be reacted */
n_failed_tests++;
frustration_wm[index1] *= frustration_factor;
frustration_wm[index2] *= frustration_factor;
frustration_wm[index3] *= frustration_factor;
} else {
n_reactions++;
n_failed_tests = 0;
int v1 = value_wm[index1];
int v2 = value_wm[index2];
int v3 = value_wm[index3];
update_summations(index1, v2 - v1);
update_summations(index2, v3 - v2);
update_summations(index3, v1 - v3);
value_wm[index1] = v2;
value_wm[index2] = v3;
value_wm[index3] = v1;
if (display_switch) {
repaint();
try {solver.sleep(Sleep_time[speed_selected_index]);}
catch (InterruptedException e) {};
};
frustration_wm[index1] = initial_frustration;
frustration_wm[index2] = initial_frustration;
frustration_wm[index3] = initial_frustration;
};
}
/* Solver main part: */
public void run() {
long initial_time;
int max_tests = 1000;
int max_tests_20 = 20 * max_tests;
n = (int)get_option(n_box, "Format error -- n ",
3, "The value of N must be 3 or larger! ", 3);
initial_frustration =
get_option(initial_frustration_box,
"Format error -- initial_frustration ",
0, "The value of initial frustration must be positive! ",
1e-15);
frustration_factor =
get_option(frustration_factor_box,
"Format error -- frustration_factor ",
0, "The value of initial frustration must be positive! ",
2.0);
init_wm(n);
start_view();
repaint();
try {solver.sleep(50);}
catch (InterruptedException e) {};
message_box.setText("Looking for Magic Squares...");
initial_time = System.currentTimeMillis();
n_tests = 0;
n_reactions = 0;
n_failed_tests = 0;
while (true) {
switch (rule_selected_index) {
case Swap_index:
swap_rule();
break;
case Rotate_index:
rotate_rule();
break;
}
if (n_failed_tests >= max_tests) { /* termination condition */
if (n_tests < max_tests_20) {
break;
};
max_tests_20 = 2 * max_tests_20;
max_tests = max_tests_20 / 20;
}
if (speed_selected_index != Full_speed_index &&
n_tests % 10000 == 0) {
message_box.setText
("Looking for Magic Squares... #tests = " + n_tests +
" #reactions = " + n_reactions + " MOD = " + mean_order() +
" Time = " +
(System.currentTimeMillis() - initial_time) / 1000.0);
repaint();
try {solver.sleep(50);}
catch (InterruptedException e) {};
}
if (speed_selected_index == Full_speed_index) {
int time = (int)((System.currentTimeMillis()-initial_time) % 3000);
switch (state) {
case 0:
if (time > 2500) {
state = 1;
message_box.setText("");
} break;
case 1:
if (time < 2500) {
state = 0;
String str = "Looking for Magic Squares... ";
switch ((msg = (msg+(int)(Math.random()*3)+9)%10)) {
case 0: case 1: case 2: str = "Looking for Magic Squares... Sorry to take your time..."; break;
case 4: str = "Looking for Magic Squares... Not long now..."; break;
case 3: str = "Still Looking for Magic Squares..."; break;
case 5: case 6: case 7: str = "Looking for Magic Squares... "; break;
default: str = "Looking for Magic Squares... Thanks for being patient...";
}
message_box.setText(str);
} break;
}
}
}
double order = mean_order();
message_box.setText((order < 0 ? " *** Failed! ***" : "I found one!") +
" It took me " + (System.currentTimeMillis() - initial_time) / 1000.0 + " seconds.");
repaint();
};
int state=0;
int msg=6;
/*======================================================================*/
/* Working memory (Initial queen layout) generation */
/*======================================================================*/
/* protected int summations[]; */
private void add2summation(int[] summation, int index) {
summation[0] += value_wm[index];
summation_wm[index][n_summations[index]++] = summation;
}
private void init_wm(int n) {
wm_size = n * n;
value_wm = new int[wm_size];
x_coord_wm = new int[wm_size];
y_coord_wm = new int[wm_size];
frustration_wm = new double[wm_size];
summation_wm = new int[wm_size][][];
n_summations = new int[wm_size];
expected_summation = (n * (n * n + 1)) / 2;
for (int i = 0; i < wm_size; i++) {
x_coord_wm[i] = i % n;
y_coord_wm[i] = i / n;
value_wm[i] = i + 1;
frustration_wm[i] = initial_frustration;
};
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
n_summations[n * i + j]++;
n_summations[n * j + i]++;
};
n_summations[n * i + i]++;
n_summations[n * i + n - i - 1]++;
};
for (int i = 0; i < wm_size; i++) {
summation_wm[i] = new int[n_summations[i]][];
n_summations[i] = 0;
};
int[] up_diagonal_summation = new int[1];
int[] down_diagonal_summation = new int[1];
for (int i = 0; i < n; i++) {
int[] row_summation = new int[1];
int[] column_summation = new int[1];
for (int j = 0; j < n; j++) {
add2summation(row_summation, n * i + j);
add2summation(column_summation, n * j + i);
};
add2summation(up_diagonal_summation, n * i + i);
add2summation(down_diagonal_summation, n * i + n - i - 1);
};
}
/*======================================================================*/
/* Dispatchers */
/*======================================================================*/
public void init() {
init_widgets();
init_solver();
init_view();
}
public void start() {
solver = new Thread(this);
solver.start();
}
public void stop() {
solver.stop();
}
}