import java.awt.*; import java.applet.*; import java.util.*; public class Queens_Sort extends Applet implements Runnable { /************************************************************************/ /* */ /* Class Queens_Sort: N Queens / Sort Problem Solver */ /* */ /* Copyright (C) 1996 by Yasusi Kanada */ /* */ /* This program can be freely distributed under the GNU General */ /* Public License. */ /* */ /* Initial Version = "ver 1.00, 1996-8-13"; */ String Version = "ver 1.05, 1996-11-23"; /* */ /************************************************************************/ /*======================================================================*/ /* Class global objects and methods */ /*======================================================================*/ protected int n = 8; /* default: eight queens */ 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 void init_widgets() { setLayout(new FlowLayout()); add(new Label ("N Queens / Sort using CCM (Chemical Casting Model) -- " + Version)); add(new Button("Stop")); add(new Button("Restart")); Panel option_panel_1 = new Panel(); option_panel_1.add(new Label("Options: N =")); n_box = new TextField("8 "); option_panel_1.add(n_box); init_speed_choice(option_panel_1); init_frustration_choice(option_panel_1); add(option_panel_1); Panel option_panel_2 = new Panel(); init_catalyst_choice(option_panel_2); init_problem_choice(option_panel_2); add(option_panel_2); message_box = new TextField(66); add(message_box); } public boolean action(Event e, Object arg) { message_box.setText(""); if (speed_action(arg) || catalyst_action(arg) || problem_action(arg) || frustration_action(arg)) { return true; } else if ("Restart".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 */ } final String Dangerous_string = "Combination of `no catalyst' and `fast speed' is inhibited, " + "because you may loose control! "; /* 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 = Medium_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 */ if (index == Full_speed_index && catalyst_selected_index == No_catalyst_index) { message_box.setText(Dangerous_string); speed_choice.select(Speed_choice_labels[speed_selected_index]); return false; } else if (index == Full_speed_index || index == Fast_speed_index) { display_switch = false; /* no display while executing */ speed_selected_index = index; return true; } else { display_switch = true; speed_selected_index = index; return true; }; } else { /* not found */ return false; }; } /* Catalyst control: */ final int Variable_catalyst_index = 0; final int No_catalyst_index = 1; final int Single_catalyst_index = 2; final int Double_catalyst_index = 3; final int[] Catalysts = {Variable_catalyst_index, No_catalyst_index, Single_catalyst_index, Double_catalyst_index}; final int Catalyst_default_index = Variable_catalyst_index; final String[] Catalyst_choice_labels = {"Variable catalyst MOVING rule", "No catalyst swapping rule (running forever...)", "Single catalyst swapping rule", "Double catalyst swapping rule"}; private int catalyst_selected_index; private Choice catalyst_choice; private void init_catalyst_choice(Panel p) { /* Catalyst control choice is created and initialized. */ catalyst_choice = new_choice(Catalyst_choice_labels, Catalyst_default_index, p); catalyst_selected_index = Catalyst_default_index; } private boolean catalyst_action(Object arg) { int index = find_index(arg, Catalyst_choice_labels); if (index >= 0) { /* found */ if (index == No_catalyst_index && speed_selected_index == Full_speed_index) { message_box.setText(Dangerous_string); catalyst_choice.select (Catalyst_choice_labels[catalyst_selected_index]); return false; } else { catalyst_selected_index = index; return true; }; } else { return false; }; } /* Order degree control (Problem control): */ final int Queens_index = 0; final int Sort_index = 1; final int[] Problems = {Queens_index, Sort_index}; final int Problem_default_index = Queens_index; final String[] Problem_choice_labels = {"N Qneens problem LOD", "Sort LOD"}; private int problem_selected_index; private Choice problem_choice; private void init_problem_choice(Panel p) { /* problem choice is created and initialized. */ problem_choice = new_choice(Problem_choice_labels, Problem_default_index, p); problem_selected_index = Problem_default_index; } private boolean problem_action(Object arg) { int index = find_index(arg, Problem_choice_labels); if (index >= 0) { /* found */ problem_selected_index = index; return true; } else { return false; }; } /* Frustration control: */ final int With_frustration_index = 0; final int Without_frustration_index = 1; final int[] Frustrations = {With_frustration_index, Without_frustration_index}; final double[] Frustration_values = {1e-5, 0.0}; final int Frustration_default_index = With_frustration_index; final String[] Frustration_choice_labels = {"Frustration ON", "Frustration OFF"}; private int frustration_selected_index; private Choice frustration_choice; private void init_frustration_choice(Panel p) { /* frustration choice is created and initialized. */ frustration_choice = new_choice(Frustration_choice_labels, Frustration_default_index, p); frustration_selected_index = Frustration_default_index; } private boolean frustration_action(Object arg) { int index = find_index(arg, Frustration_choice_labels); if (index >= 0) { /* found */ frustration_selected_index = index; initial_frustration = Frustration_values[index]; return true; } else { return false; }; } /*======================================================================*/ /* Queens viewer */ /*======================================================================*/ protected boolean display_switch = true; /* To display the map or not */ final int Common_offset = 8; protected int x_offset = 0; protected int y_offset = 180; protected int drawn_n; protected int queen_x_offset, queen_y_offset; int drawing_size, drawing_step, queen_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; queen_x_offset = Common_offset + x_offset; queen_y_offset = Common_offset + y_offset; queen_size = drawing_step * 4 / 5; } public void paint(Graphics g) { int x0 = x_offset + Common_offset; int y0 = y_offset + Common_offset; /* if (drawn_n != n) { g.clearRect(x0, y0, size().width, size().height); drawn_n = n; }; */ 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; g.setColor(Color.magenta); for (int i = 0; i < n; i++) { g.fillOval(x0 + column_wm[i] * drawing_step, y0 + row_wm[i] * drawing_step, queen_size, queen_size); }; } /*======================================================================*/ /* Solver */ /*======================================================================*/ double initial_frustration = 1.0e-5; double frustration_factor = 2.0; /* Working memory: */ private int[] column_wm, row_wm; private double[] frustration_wm; private int wm_size; private int n_tests; private int n_reactions; private int n_failed_tests; /* Order degree: */ private int order(int row1, int col1, int row2, int col2) { if (problem_selected_index == Queens_index) { return (row1 != row2 && col1 != col2 && row1 - row2 != col1 - col2 && row1 - row2 != col2 - col1 ? 1 : 0); } else { /* problem_selected_index == Sort_index */ int value1 = row1; int index1 = col1; int value2 = row2; int index2 = col2; /* return index1 >= index2 ? (value1 > value2 ? 0 : 1) : (value1 >= value2 ? 1 : 0); */ return (index1 - index2) * (value1 - value2) >= 0 ? 0 : 1; }; } 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++) { for (int j = 0; j < i; j++) { total_order += order(row_wm[i], column_wm[i], row_wm[j], column_wm[j]); }; }; return 2 * total_order / (double)(wm_size * (wm_size - 1)); } private void init_solver() { } /* Random color/index generators */ private Random selector = new Random(); private int random(int max) { return (selector.nextInt() & 0x7FFFFFFF) % max; } private void swap(int index1, int index2, int old_order, int new_order) { 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 temp = column_wm[index1]; column_wm[index1] = column_wm[index2]; column_wm[index2] = temp; 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; }; } /* Rules */ private void swap0() { /* No catalyst swapping rule */ int index1, index2; n_tests++; /* Select queens to apply the rule: */ index1 = random(wm_size); do { index2 = random(wm_size); } while (index2 == index1); /* Compute LODs: */ swap(index1, index2, -1, -1); } private void swap1() { /* Single catalyst swapping rule */ int index1, index2, index3; n_tests++; /* Select queens 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(row_wm[index1], column_wm[index1], row_wm[index3], column_wm[index3]) + order(row_wm[index2], column_wm[index2], row_wm[index3], column_wm[index3]) + order(row_wm[index1], column_wm[index1], /* for sort */ row_wm[index2], column_wm[index2]) - 3; int new_order = order(row_wm[index1], column_wm[index2], row_wm[index3], column_wm[index3]) + order(row_wm[index2], column_wm[index1], row_wm[index3], column_wm[index3]) + order(row_wm[index1], column_wm[index2], /* for sort */ row_wm[index2], column_wm[index1]) - 3; swap(index1, index2, old_order, new_order); } private void swap2() { /* Double catalyst swapping rule */ int index1, index2, index3, index4; n_tests++; /* Select queens 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); do { index4 = random(wm_size); } while (index4 == index1 || index4 == index2 || index4 == index3); /* Compute LODs: */ int old_order = order(row_wm[index1], column_wm[index1], row_wm[index3], column_wm[index3]) + order(row_wm[index2], column_wm[index2], row_wm[index3], column_wm[index3]) + order(row_wm[index1], column_wm[index1], row_wm[index4], column_wm[index4]) + order(row_wm[index2], column_wm[index2], row_wm[index4], column_wm[index4]) + order(row_wm[index1], column_wm[index1], /* for sort */ row_wm[index2], column_wm[index2]) - 5; int new_order = order(row_wm[index1], column_wm[index2], row_wm[index3], column_wm[index3]) + order(row_wm[index2], column_wm[index1], row_wm[index3], column_wm[index3]) + order(row_wm[index1], column_wm[index2], row_wm[index4], column_wm[index4]) + order(row_wm[index2], column_wm[index1], row_wm[index4], column_wm[index4]) + order(row_wm[index1], column_wm[index2], /* for sort */ row_wm[index2], column_wm[index1]) - 5; swap(index1, index2, old_order, new_order); } private void move() { /* Variable catalyst moving rule */ n_tests++; /* Select a queen to apply the rule: */ int index = random(wm_size); int new_column = random(wm_size); /* Compute LODs: */ int old_order = 0; int new_order = 0; for (int i = 0; i < n; i++) { if (i != index) { old_order += order(row_wm[index], column_wm[index], row_wm[i], column_wm[i]) - 1; new_order += order(row_wm[index], new_column, row_wm[i], column_wm[i]) - 1; }; }; if (old_order >= 0) { /* A local termination condition holds */ n_failed_tests++; } else if (((double)old_order - frustration_wm[index]) > new_order) { /* not to be reacted */ n_failed_tests++; frustration_wm[index] *= frustration_factor; } else { n_reactions++; n_failed_tests = 0; column_wm[index] = new_column; if (display_switch) { repaint(); try {solver.sleep(Sleep_time[speed_selected_index]);} catch (InterruptedException e) {}; }; frustration_wm[index] = initial_frustration; }; } /* Solver main part: */ public void run() { long initial_time; int max_tests = 1000 + n * n; int max_tests_20 = 20 * max_tests; try { n = Integer.parseInt(n_box.getText()); /* Get n from the widget */ } catch (NumberFormatException e) { message_box.setText("Format error -- n "); n = 8; }; if (n < 4) { message_box.setText("The value of N must be 4 or larger! "); n = 8; }; n_box.setText("" + n); init_wm(n); start_view(); repaint(); try {solver.sleep(200);} catch (InterruptedException e) {}; message_box.setText("Running..."); initial_time = System.currentTimeMillis(); n_tests = 0; n_reactions = 0; n_failed_tests = 0; while (true) { switch (catalyst_selected_index) { case No_catalyst_index: swap0(); break; case Single_catalyst_index: swap1(); break; case Double_catalyst_index: swap2(); break; case Variable_catalyst_index: move(); 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 ("Running... #tests = " + n_tests + " #reactions = " + n_reactions + " MOD = " + mean_order() + " Time = " + (System.currentTimeMillis() - initial_time) / 1000.0); repaint(); try {solver.sleep(50);} catch (InterruptedException e) {}; }; }; double order = mean_order(); message_box.setText ("Stopped. #tests = " + n_tests + " #reactions = " + n_reactions + " MOD = " + order + (order < 1 ? " *** Failed! ***" : "") + " Time = " + (System.currentTimeMillis() - initial_time) / 1000.0); repaint(); }; /*======================================================================*/ /* Working memory (Initial queen layout) generation */ /*======================================================================*/ private void init_wm(int n) { wm_size = n; column_wm = new int[wm_size]; row_wm = new int[wm_size]; frustration_wm = new double[wm_size]; for (int i = 0; i < wm_size; i++) { row_wm[i] = i; column_wm[i] = i; /* or random(n); */ frustration_wm[i] = initial_frustration; }; } /*======================================================================*/ /* Dispatchers */ /*======================================================================*/ public void init() { init_widgets(); init_solver(); init_view(); } public void start() { solver = new Thread(this); solver.start(); } public void stop() { solver.stop(); } }