//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //============================================================================= package org.uncommons.watchmaker.examples.biomorphs; import java.awt.BorderLayout; import java.awt.Container; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.SpringLayout; import javax.swing.SwingUtilities; import org.uncommons.maths.random.MersenneTwisterRNG; import org.uncommons.maths.random.Probability; import org.uncommons.swing.SpringUtilities; import org.uncommons.swing.SwingBackgroundTask; import org.uncommons.watchmaker.examples.AbstractExampleApplet; import org.uncommons.watchmaker.framework.EvolutionEngine; import org.uncommons.watchmaker.framework.EvolutionObserver; import org.uncommons.watchmaker.framework.EvolutionaryOperator; import org.uncommons.watchmaker.framework.GenerationalEvolutionEngine; import org.uncommons.watchmaker.framework.PopulationData; import org.uncommons.watchmaker.framework.interactive.InteractiveSelection; import org.uncommons.watchmaker.framework.interactive.Renderer; import org.uncommons.watchmaker.framework.termination.GenerationCount; import org.uncommons.watchmaker.swing.SwingConsole; /** * Watchmaker Framework implementation of Dawkin's biomorph program. * @author Daniel Dyer */ public class BiomorphApplet extends AbstractExampleApplet { private Renderer<Biomorph, JComponent> renderer; private SwingConsole console; private JDialog selectionDialog; private JPanel biomorphHolder; /** * Initialise and layout the GUI. * @param container The Swing component that will contain the GUI controls. */ @Override protected void prepareGUI(Container container) { renderer = new SwingBiomorphRenderer(); console = new SwingConsole(5); selectionDialog = new JDialog((JFrame) null, "Biomorph Selection", true); biomorphHolder = new JPanel(new GridLayout(1, 1)); container.add(new ControlPanel(), BorderLayout.WEST); container.add(biomorphHolder, BorderLayout.CENTER); biomorphHolder.setBorder(BorderFactory.createTitledBorder("Last Evolved Biomorph")); biomorphHolder.add(new JLabel("Nothing generated yet.", JLabel.CENTER)); selectionDialog.add(console, BorderLayout.CENTER); selectionDialog.setSize(800, 600); selectionDialog.validate(); } /** * Helper method to create a background task for running the interactive evolutionary * algorithm. * @param populationSize How big the population used by the created evolution engine * should be. * @param generationCount How many generations to use when the evolution engine is * invoked. * @param random If true use random mutation, otherwise use Dawkins mutation. * @return A Swing task that will execute on a background thread and update * the GUI when it is done. */ private SwingBackgroundTask<Biomorph> createTask(final int populationSize, final int generationCount, final boolean random) { return new SwingBackgroundTask<Biomorph>() { @Override protected Biomorph performTask() { EvolutionaryOperator<Biomorph> mutation = random ? new RandomBiomorphMutation(new Probability(0.12d)) : new DawkinsBiomorphMutation(); InteractiveSelection<Biomorph> selection = new InteractiveSelection<Biomorph>(console, renderer, populationSize, 1); EvolutionEngine<Biomorph> engine = new GenerationalEvolutionEngine<Biomorph>(new BiomorphFactory(), mutation, selection, new MersenneTwisterRNG()); engine.addEvolutionObserver(new GenerationTracker()); return engine.evolve(populationSize, 0, new GenerationCount(generationCount)); } @Override protected void postProcessing(Biomorph result) { selectionDialog.setVisible(false); biomorphHolder.removeAll(); biomorphHolder.add(renderer.render(result)); biomorphHolder.revalidate(); } }; } /** * Entry point for running this example as an application rather than an applet. * @param args Program arguments (ignored). */ public static void main(String[] args) { new BiomorphApplet().displayInFrame("Watchmaker Framework - Biomporphs Example"); } /** * Simple observer to update the dialog title every time the evolution advances * to a new generation. */ private final class GenerationTracker implements EvolutionObserver<Biomorph> { public void populationUpdate(final PopulationData<? extends Biomorph> populationData) { SwingUtilities.invokeLater(new Runnable() { public void run() { selectionDialog.setTitle("Biomorph Selection - Generation " + (populationData.getGenerationNumber() + 1)); } }); } } /** * Panel for controlling the evolutionary algorithm parameters. */ private final class ControlPanel extends JPanel { private JSpinner populationSpinner; private JSpinner generationsSpinner; private JComboBox mutationCombo; ControlPanel() { super(new BorderLayout()); add(createInputPanel(), BorderLayout.NORTH); add(createButtonPanel(), BorderLayout.SOUTH); setBorder(BorderFactory.createTitledBorder("Evolution Controls")); } private JComponent createInputPanel() { JPanel inputPanel = new JPanel(new SpringLayout()); JLabel populationLabel = new JLabel("Population Size: "); populationSpinner = new JSpinner(new SpinnerNumberModel(18, 2, 25, 1)); populationSpinner.setEnabled(false); populationLabel.setLabelFor(populationSpinner); inputPanel.add(populationLabel); inputPanel.add(populationSpinner); JLabel generationsLabel = new JLabel("Number of Generations: "); generationsSpinner = new JSpinner(new SpinnerNumberModel(20, 1, 100, 1)); generationsLabel.setLabelFor(generationsSpinner); inputPanel.add(generationsLabel); inputPanel.add(generationsSpinner); JLabel mutationLabel = new JLabel("Mutation Type: "); mutationCombo = new JComboBox(new String[]{"Dawkins (Non-random)", "Random"}); mutationCombo.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent itemEvent) { if (mutationCombo.getSelectedIndex() == 0) { populationSpinner.setValue(18); populationSpinner.setEnabled(false); } else { populationSpinner.setEnabled(true); } } }); inputPanel.add(mutationLabel); inputPanel.add(mutationCombo); SpringUtilities.makeCompactGrid(inputPanel, 3, 2, 30, 6, 6, 6); return inputPanel; } private JComponent createButtonPanel() { JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton startButton = new JButton("Start"); startButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { createTask((Integer) populationSpinner.getValue(), (Integer) generationsSpinner.getValue(), mutationCombo.getSelectedIndex() == 1).execute(); selectionDialog.setVisible(true); } }); buttonPanel.add(startButton); return buttonPanel; } } }