Commit 1ed8e933 authored by bbguimaraes's avatar bbguimaraes
Browse files

sudoku

parent fb17c000
......@@ -4,4 +4,5 @@ for details/demos/screenshots.
- asm: simple assembly program that slides a three-digit number across the
terminal and an external seven-segment display.
- nqueens: n-queens solver and graphical display.
- sudoku: Sudoku solver library/GUI using genetic algorithms.
- tablut: ANSI C / ncurses implementation of the game Tablut.
JC = javac
CLASSES = \
gui/ConfigurationPanel.class \
gui/ConfigurationWindow.class \
gui/FamilyWindow.class \
gui/MainWindow.class \
gui/ProgressPanel.class \
gui/SimpleWindow.class \
gui/SolutionDetailsDisplay.class \
gui/SolutionDisplay.class \
gui/SolutionsPanel.class \
gui/TextDisplay.class \
model/Configuration.class \
model/Family.class \
model/Generation.class \
model/Population.class \
model/Solution.class \
operators/CyclicOperator.class \
operators/GeneticOperations.class \
operators/GeneticOperator.class \
operators/UniformOperator.class \
test/Main.class \
test/SolutionReader.class
all: $(CLASSES)
%.class: %.java
$(JC) $<
classes: $(CLASSES:.java=.class)
.PHONY: clean
clean:
find -type f -name '*.class' -delete
Sudoku solver using genetic algorithms. Provides two execution methods:
- a simple interface that creates new generations until the solution is found
- a detailed interface, where each generation is graphically displayed
package gui;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import model.Configuration;
import operators.GeneticOperations;
import operators.GeneticOperator;
public class ConfigurationPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final int N_FIELDS = 3;
private final String[] OPERATORS = {"Uniform"};
private JLabel labels[];
private JTextField tfield_n_elems;
private JTextField tfield_mutation;
private JComboBox<String> combo_operator;
public ConfigurationPanel() {
init();
}
private void init() {
setBackground(Color.WHITE);
tfield_n_elems = new JTextField("8");
tfield_mutation = new JTextField("0.05");
combo_operator = new JComboBox<String>(OPERATORS);
JPanel panel = new JPanel(new GridLayout(0, 4, 15, 0));
panel.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
panel.add(new JLabel("Number of elements"));
panel.add(tfield_n_elems);
panel.add(new JLabel("Mutation rate"));
panel.add(tfield_mutation);
panel.add(new JLabel("Operator"));
panel.add(combo_operator);
add(panel);
}
public Configuration getValue() throws Exception {
int n_tries = 1;
int n_elems = this.getNElems(this.tfield_n_elems.getText());
float mutation_rate = this.getMutationRate(tfield_mutation.getText());
GeneticOperator operator =
GeneticOperations.operator_by_name(
(String) combo_operator.getSelectedItem());
return new Configuration(n_tries, n_elems, mutation_rate, operator);
}
private static int getNElems(String v) throws Exception {
int n_elems = 0;
try {
n_elems = Integer.parseInt(v);
} catch(NumberFormatException e) {
throw new Exception("Invalid argument: Number of elements.");
}
if(n_elems % 2 != 0)
throw new Exception(
"Invalid argument: " +
"Number of elements (only even numbers alowed).");
return n_elems;
}
private static float getMutationRate(String v) throws Exception {
try {
return Float.parseFloat(v);
} catch(NumberFormatException e) {
throw new Exception("Invalid argument: Mutation rate.");
}
}
}
package gui;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
import model.Configuration;
import model.Population;
public class ConfigurationWindow extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private ConfigurationPanel panel;
private JCheckBox checkbox_detailed;
private JButton button_ok;
private JButton button_cancel;
public ConfigurationWindow() {
init();
}
private void init() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
JPanel center_panel = new JPanel();
center_panel.setLayout(new GridLayout(0, 1));
center_panel.setBorder(
BorderFactory.createBevelBorder(BevelBorder.LOWERED));
this.panel = new ConfigurationPanel();
center_panel.add(this.panel);
JPanel south_panel = new JPanel();
this.button_ok = new JButton("OK");
this.button_cancel = new JButton("Cancel");
this.checkbox_detailed = new JCheckBox("Detailed window");
south_panel.add(this.button_ok);
south_panel.add(this.button_cancel);
south_panel.add(this.checkbox_detailed);
getRootPane().setDefaultButton(this.button_ok);
this.button_ok.addActionListener(this);
this.button_cancel.addActionListener(this);
add(center_panel, BorderLayout.CENTER);
add(south_panel, BorderLayout.PAGE_END);
pack();
setResizable(false);
setLocationRelativeTo(null);
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == this.button_cancel)
System.exit(0);
else if(e.getSource() == this.button_ok)
if(this.create_main_window())
dispose();
}
private boolean create_main_window() {
Configuration c;
try {
c = this.panel.getValue();
} catch(Exception ex) {
JOptionPane.showMessageDialog(
this, ex.getMessage(), "", JOptionPane.ERROR_MESSAGE);
return false;
}
JFrame window;
if(this.checkbox_detailed.isSelected())
window = new MainWindow(new Population(c));
else
window = new SimpleWindow(c);
window.setVisible(true);
return true;
}
}
package gui;
import java.awt.Color;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import model.Family;
public class FamilyWindow extends JFrame {
private static final long serialVersionUID = 1L;
public FamilyWindow(Family family) {
init(family);
}
private void init(Family family) {
setLayout(new FlowLayout());
SolutionDisplay panel = new SolutionDisplay(family.parent0);
panel.setBackground(new Color(192, 255, 192));
add(panel);
panel = new SolutionDisplay(family.parent1);
panel.setBackground(new Color(192, 192, 255));
add(panel);
panel = new SolutionDisplay(family.child0);
panel.setDrawnFamily(family);
add(panel);
panel = new SolutionDisplay(family.child1);
panel.setDrawnFamily(family);
add(panel);
setSize(390, 412);
}
}
package gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import model.Family;
import model.Generation;
import model.Population;
import model.Solution;
public class MainWindow extends JFrame {
private static final long serialVersionUID = 1L;
private Population population;
private boolean solved;
private SolutionsPanel west_panel;
private SolutionDetailsDisplay detail_display;
private JComboBox<String> generations_combo;
private JList<Family> list;
private JButton button_next_gen;
private JButton button_next_10_gen;
private JButton button_next_n_gen;
private JSpinner spinner_n_gen;
private JMenuItem menu_fitness;
private JMenuItem menu_details;
public MainWindow(Population population) {
this.population = population;
this.solved = false;
init();
}
private void init() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
MainWindowListener listener = new MainWindowListener();
JMenuBar menu_bar = new JMenuBar();
setJMenuBar(menu_bar);
JMenu menu_solutions = new JMenu("Solutions");
JMenu menu_generations = new JMenu("Generation");
this.menu_fitness = new JMenuItem("Fitness");
this.menu_details = new JMenuItem("Details");
menu_bar.add(menu_solutions);
menu_bar.add(menu_generations);
menu_solutions.add(this.menu_fitness);
menu_generations.add(this.menu_details);
ActionListener menu_listener = new MenuList();
this.menu_fitness.addActionListener(menu_listener);
this.menu_details.addActionListener(menu_listener);
JPanel upper_panel = new JPanel();
this.button_next_gen = new JButton("Next");
this.button_next_10_gen = new JButton("Next 10");
this.button_next_n_gen = new JButton("Next n");
this.spinner_n_gen = new JSpinner(
new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1));
this.generations_combo = new JComboBox<String>();
upper_panel.add(this.button_next_gen);
upper_panel.add(this.button_next_10_gen);
upper_panel.add(Box.createRigidArea(new Dimension(32, 0)));
upper_panel.add(this.button_next_n_gen);
upper_panel.add(this.spinner_n_gen);
upper_panel.add(Box.createRigidArea(new Dimension(32, 0)));
upper_panel.add(new JLabel("Generations:"));
upper_panel.add(this.generations_combo);
this.button_next_gen.addActionListener(listener);
this.button_next_10_gen.addActionListener(listener);
this.button_next_n_gen.addActionListener(listener);
this.generations_combo.addActionListener(listener);
this.west_panel = new SolutionsPanel();
this.west_panel.setLayout(new GridLayout(0, 1));
this.west_panel.setBorder(BorderFactory.createBevelBorder(1));
this.west_panel.setBackground(Color.WHITE);
this.west_panel.addMouseListener(listener);
JScrollPane west_scroll = new JScrollPane(this.west_panel);
west_scroll.setVerticalScrollBarPolicy(
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
JPanel center_panel = new JPanel();
center_panel.setBorder(BorderFactory.createBevelBorder(1));
center_panel.setLayout(new BorderLayout());
DefaultListModel<Family> model = new DefaultListModel<Family>();
list = new JList<Family>(model);
list.setPreferredSize(new Dimension(300, 0));
list.setFont(new Font("Lucida Console", Font.PLAIN, 18));
list.addMouseListener(listener);
center_panel.add(list, BorderLayout.CENTER);
JPanel bottom_panel = new JPanel();
this.detail_display = new SolutionDetailsDisplay(null);
bottom_panel.add(this.detail_display);
add(west_scroll, BorderLayout.LINE_START);
add(upper_panel, BorderLayout.PAGE_START);
add(center_panel, BorderLayout.CENTER);
add(bottom_panel, BorderLayout.PAGE_END);
next_generation();
changeGeneration(0);
pack();
setLocationRelativeTo(null);
}
public void next_generation() {
population.create_next_generation();
if(!solved && population.solved) {
solved = true;
String s = "";
s += "Solution found!\n";
s += "Generation number:" + (population.current_index());
JOptionPane.showMessageDialog(this, s);
}
this.generations_combo.addItem(
Integer.toString(population.current_index()));
}
public void nextGenerations(int n) {
for(int i = 0; i < n - 1; i++)
next_generation();
}
public void createAndShowProgress(int n) {
ProgressPanel progress_window = new ProgressPanel(n);
JFrame frame = new JFrame();
frame.add(progress_window);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
int inc;
if(n > 10)
inc = n / 10;
else
inc = 10;
for(int i = 0; i < n; i += inc) {
nextGenerations(inc);
progress_window.update(i);
}
this.west_panel.paint(this.west_panel.getGraphics());
frame.dispose();
}
public void changeGeneration(int generationIndex) {
this.west_panel.setSolutions(
population.generation(generationIndex).solutions,
true);
if(generationIndex > 0)
populateFamilyList(generationIndex);
this.generations_combo.setSelectedIndex(generationIndex);
}
private void populateFamilyList(int generationIndex) {
DefaultListModel<Family> model =
(DefaultListModel<Family>) list.getModel();
model.clear();
Generation generation = population.generation(generationIndex);
float best_fitness = -1;
Solution best_solution = null;
Solution[] solutions = generation.solutions;
for(Solution s : solutions)
if(s.fitness > best_fitness) {
best_fitness = s.fitness;
best_solution = s;
}
for(Family f : generation.families)
model.addElement(f);
}
private class MainWindowListener
extends MouseAdapter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == generations_combo) {
changeGeneration(generations_combo.getSelectedIndex());
return;
}
if(e.getSource() == button_next_gen)
next_generation();
else if(e.getSource() == button_next_10_gen)
nextGenerations(10);
else if(e.getSource() == button_next_n_gen)
createAndShowProgress(
((SpinnerNumberModel) spinner_n_gen.getModel())
.getNumber().intValue());
changeGeneration(population.current_index());
}
@Override
public void mouseEntered(MouseEvent e) {
Solution s = null;
if(e.getSource() instanceof SolutionDisplay) {
SolutionDisplay display = (SolutionDisplay) e.getSource();
s = display.getSolution();
}
detail_display.setSolution(s);
}
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() != 2 || list.getSelectedIndex() < 0)
return;
new FamilyWindow(list.getSelectedValue()).setVisible(true);
}
}
private class MenuList implements ActionListener {
public void actionPerformed(ActionEvent e) {
if(e.getSource() == menu_fitness) {
if(population.generation_count() == 0)
return;
String s = new String();
Solution[] gen = population.current_generation().solutions;
for(Solution sol : gen)
s += "ID: " + sol.id + " Fitness: " + sol.fitness + "\n";
JOptionPane.showMessageDialog(MainWindow.this, s);
} else if(e.getSource() == menu_details) {
Generation generation = population.current_generation();
Family[] families = generation.families;
if(families.length == 0)
return;
String s = new String();
for(Family f : families) {
s += "Family: " + f.id + "\n\n";
s += "Parents:\n";
s += f.parent0.id + " (chance: ";
s += f.parent0_chance + ")\n";
s += f.parent1.id + " (chance: ";
s += f.parent1_chance + ")\n\n";
s += "Children:\n";
s += f.child0.id + "\n";
s += f.child1.id;
s += "\n\n";
s += "---------------------------------------------------";
s += "----------------------";
s += "\n\n";
}
TextDisplay textDisplay = new TextDisplay(s);
textDisplay.setVisible(true);
}
}
}
}
package gui;
import java.awt.GridLayout;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
public class ProgressPanel extends JPanel {
private static final long serialVersionUID = 1L;
private JProgressBar bar;
public ProgressPanel(int max) {
init();
this.bar.setMaximum(max);
}
private void init() {
setLayout(new GridLayout());
this.bar = new JProgressBar(new DefaultBoundedRangeModel(0, 0, 0, 0));
add(this.bar);
}
public void update(int value) {
this.bar.setValue(value);
paint(getGraphics());
}
public void setIndeterminate(boolean indeterminate) {
this.bar.setIndeterminate(indeterminate);
}
}
package gui;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import model.Configuration;
import model.Population;
import model.Solution;
public class SimpleWindow extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
private Configuration configuration;
private String log;
private JLabel progress_label;
private JButton start_button;
private JButton log_button;
private ProgressPanel progress_panel;
public SimpleWindow(Configuration configuration) {
this.configuration = configuration;
this.log = "";
init();
}
private void init() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
this.progress_label =
new JLabel("Generation - Best solution: , fitness");
this.start_button = new JButton("Start");
this.log_button = new JButton("Log");
this.log_button.setEnabled(false);
this.start_button.addActionListener(this);
this.log_button.addActionListener(this);
JPanel ne_panel = new JPanel();
ne_panel.add(this.start_button);
ne_panel.add(this.log_button);
JPanel nc_panel = new JPanel();
nc_panel.add(this.progress_label);
JPanel n_panel = new JPanel();
n_panel.setLayout(new BorderLayout());
n_panel.add(nc_panel, BorderLayout.CENTER);
n_panel.add(ne_panel, BorderLayout.LINE_END);
this.progress_panel = new ProgressPanel(1);
add(n_panel, BorderLayout.PAGE_START);
add(this.progress_panel, BorderLayout.PAGE_END);
setSize(600, 100);
setLocationRelativeTo(null);
}
private boolean next_generation(Population population) {
population.create_next_generation();
Solution[] solutions = population.current_generation().solutions;
Solution best = solutions[population.generation_size - 1];
String s = ""
+ "Generation: " + population.current_generation().id + " - "
+ "Best solution: " + best.id
+ ", fitness " + best.fitness;
this.progress_label.setText(s);
return best.n_errors == 0;
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == this.start_button) {
Thread thread = new SimpleWindowThread();
thread.start();