diff options
author | Eduardo Pedroni <ep625@york.ac.uk> | 2014-04-06 21:58:53 +0100 |
---|---|---|
committer | Eduardo Pedroni <ep625@york.ac.uk> | 2014-04-06 21:58:53 +0100 |
commit | e6dd7711c7dad5e000445208eb5845801f4ccffc (patch) | |
tree | 1454bd20a8dd7069b1283184c42f4def6d5f7e6f /src | |
parent | c7969623b44f375e30fa3f15dcd7581609276a0f (diff) |
About to make big changes to the way fitness works, committing just in case
Diffstat (limited to 'src')
48 files changed, 1329 insertions, 1274 deletions
diff --git a/src/jcgp/JCGP.java b/src/jcgp/JCGP.java index 962ddec..2c7023e 100644 --- a/src/jcgp/JCGP.java +++ b/src/jcgp/JCGP.java @@ -1,375 +1,36 @@ package jcgp; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Random; - -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import jcgp.backend.function.Arithmetic; -import jcgp.backend.function.BitwiseLogic; -import jcgp.backend.function.BooleanLogic; -import jcgp.backend.function.Function; -import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.ea.EvolutionaryAlgorithm; import jcgp.backend.modules.ea.MuPlusLambda; import jcgp.backend.modules.ea.TournamentSelection; import jcgp.backend.modules.fitness.FitnessFunction; -import jcgp.backend.modules.fitness.TestCase; -import jcgp.backend.modules.fitness.TestCaseEvaluator; +import jcgp.backend.modules.fitness.testcase.TestCaseEvaluator.TestCase; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.modules.mutator.PointMutator; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; -import jcgp.backend.parameters.ParameterStatus; import jcgp.backend.population.Population; -import jcgp.gui.console.Console; +import jcgp.backend.resources.Console; +import jcgp.backend.resources.ModifiableResources; +import jcgp.backend.resources.Resources; /** + * + * Top-level CGP class. This class is the entry point for a CGP experiment. + * <p> + * An instance of JCGP encapsulates the entire experiment. It contains a Resources + * object which can be retrieved via a getter. Modules can be selected using their + * respective setters and function sets can be selected through the resources. + * + * The flow of the experiment is controlled using start() and nextGeneration(). The + * experiment can be reset with reset(), + * + * * @author Eduardo Pedroni - * + * @see Resources, Module, FunctionSet */ public class JCGP { - - /** - * - * The resources class encapsulates all of the resources based on which the program operates. - * Each instance of JCGP contains a single instance of Resources, which gets passed to the selected - * modules as the program executes. - * - * @author Eduardo Pedroni - * - */ - public static class Resources { - private HashMap<String, Parameter> parameters = new HashMap<String, Parameter>(); - - private Random numberGenerator; - - private TestCase[] testCases; - - // function sets - private FunctionSet[] functionSets = new FunctionSet[] { - new Arithmetic(), - new BitwiseLogic(), - new BooleanLogic() }; - private FunctionSet functionSet = functionSets[0]; - - // GUI console - private Console console; - - public Resources() { - createBaseParameters(); - - numberGenerator = new Random(getInt("seed")); - set("arity", functionSet.getMaxArity()); - } - - public int getInt(String key) { - if (parameters.get(key) instanceof IntegerParameter) { - return ((IntegerParameter) parameters.get(key)).get(); - } else if (parameters.get(key) instanceof DoubleParameter) { - return (int) ((DoubleParameter) parameters.get(key)).get(); - } else { - throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to int."); - } - } - - public double getDouble(String key) { - if (parameters.get(key) instanceof IntegerParameter) { - return (double) ((IntegerParameter) parameters.get(key)).get(); - } else if (parameters.get(key) instanceof DoubleParameter) { - return ((DoubleParameter) parameters.get(key)).get(); - } else { - throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to double."); - } - } - - public boolean getBoolean(String key) { - if (parameters.get(key) instanceof BooleanParameter) { - return ((BooleanParameter) parameters.get(key)).get(); - } else { - throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to int."); - } - } - - public void set(String key, Object value) { - if (parameters.get(key) instanceof IntegerParameter) { - ((IntegerParameter) parameters.get(key)).set(((Integer) value).intValue()); - } else if (parameters.get(key) instanceof DoubleParameter) { - ((DoubleParameter) parameters.get(key)).set(((Double) value).doubleValue()); - } else if (parameters.get(key) instanceof BooleanParameter) { - ((BooleanParameter) parameters.get(key)).set(((Boolean) value).booleanValue()); - } - } - - public Parameter getParameter(String key) { - return parameters.get(key); - } - - public boolean contains(String key) { - return parameters.containsKey(key); - } - - private void createBaseParameters() { - parameters.put("rows", new IntegerParameter(8, "Rows", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 row."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("columns", new IntegerParameter(9, "Columns", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 column."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("inputs", new IntegerParameter(3, "Inputs", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 input."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("outputs", new IntegerParameter(3, "Outputs", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Chromosome must have at least 1 output."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("popSize", new IntegerParameter(5, "Population", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Population size must be at least 1."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("levelsBack", new IntegerParameter(2, "Levels back", false, true) { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Levels back must be at least 1."); - } else if (newValue > getInt("columns")) { - status = ParameterStatus.INVALID; - status.setDetails("Levels back must be less than or equal to the number of columns."); - } else { - status = ParameterStatus.VALID; - } - } - }); - - IntegerParameter nodes = new IntegerParameter(1, "Nodes", true, false) { - @Override - public void validate(int newValue) { - // blank - } - }; - nodes.valueProperty().bind(((SimpleIntegerProperty) ((IntegerParameter) parameters.get("rows")).valueProperty()).multiply((SimpleIntegerProperty) ((IntegerParameter) parameters.get("columns")).valueProperty())); - parameters.put("nodes", nodes); - - parameters.put("generations", new IntegerParameter(1000000, "Generations") { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Number of generations must be greater than 0."); - } else if (newValue < getInt("currentGen")) { - status = ParameterStatus.WARNING_RESET; - status.setDetails("Setting generations to less than the current generation will cause the experiment to restart."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("currentGen", new IntegerParameter(1, "Generation", true, false) { - @Override - public void validate(int newValue) { - // blank - } - }); - - parameters.put("runs", new IntegerParameter(5, "Runs") { - @Override - public void validate(int newValue) { - if (newValue <= 0) { - status = ParameterStatus.INVALID; - status.setDetails("Number of runs must be greater than 0."); - } else if (newValue < getInt("currentRun")) { - status = ParameterStatus.WARNING_RESET; - status.setDetails("Setting runs to less than the current run will cause the experiment to restart."); - } else { - status = ParameterStatus.VALID; - } - } - }); - parameters.put("currentRun", new IntegerParameter(1, "Run", true, false) { - @Override - public void validate(int newValue) { - // blank - } - }); - - parameters.put("arity", new IntegerParameter(0, "Max arity", true, false) { - @Override - public void validate(int newValue) { - // blank - } - }); - parameters.put("maxFitness", new IntegerParameter(3, "Max fitness", true, true) { - @Override - public void validate(int newValue) { - // blank - } - }); - - IntegerParameter seed = new IntegerParameter(123, "Seed", false, true) { - @Override - public void validate(int newValue) { - status = ParameterStatus.VALID; - } - }; - seed.valueProperty().addListener(new ChangeListener<Number>() { - @Override - public void changed( - ObservableValue<? extends Number> observable, - Number oldValue, Number newValue) { - numberGenerator.setSeed(newValue.longValue()); - } - }); - parameters.put("seed", seed); - - parameters.put("report", new IntegerParameter(1, "Report", false, false) { - @Override - public void validate(int newValue) { - if (newValue > getInt("generations")) { - status = ParameterStatus.WARNING; - status.setDetails("No reports will be printed."); - } else { - status = ParameterStatus.VALID; - } - } - }); - } - - /** - * - * - * @return the iterator for the set of base parameters - */ - public Iterator<Entry<String, Parameter>> iterator() { - return parameters.entrySet().iterator(); - } - - /* - * Utility functions - */ - public int getRandomInt(int limit) { - return numberGenerator.nextInt(limit); - } - - public double getRandomDouble(int limit) { - return numberGenerator.nextDouble() * limit; - } - - public double getRandomDouble() { - return numberGenerator.nextDouble(); - } - - /* - * FunctionSet functions - */ - public Function getRandomFunction() { - Function f = functionSet.getAllowedFunction(numberGenerator.nextInt(functionSet.getAllowedFunctionCount())); - return f; - } - - public Function getFunction(int index) { - return functionSet.getAllowedFunction(index); - } - - public void setFunctionSet(int index) { - functionSet = functionSets[index]; - } - - /** - * @return the functionSets - */ - public FunctionSet[] getFunctionSets() { - return functionSets; - } - - /** - * @return the functionSet - */ - public FunctionSet getFunctionSet() { - return functionSet; - } - - /* - * Test cases - */ - public void setTestCases(TestCase ... testCases) { - this.testCases = testCases; - } - - public TestCase getTestCase(int index) { - return testCases[index]; - } - - public int getTestCaseCount() { - return testCases.length; - } - /* - * Console functionality - */ - public void setConsole(Console console) { - this.console = console; - } - - public void println(String s) { - System.out.println(s); - if (console != null) { - console.println(s); - } - } - - public void print(String s) { - System.out.print(s); - if (console != null) { - console.print(s); - } - } - } - - private final Resources resources = new Resources(); + // make resources + private final ModifiableResources resources = new ModifiableResources(); /* * The following arrays contain all available modules. These collections are read by the GUI @@ -380,7 +41,7 @@ public class JCGP { */ // mutators private Mutator[] mutators = new Mutator[] { - new PointMutator() }; + new PointMutator(resources) }; private Mutator mutator; // evolutionary algorithms @@ -391,7 +52,7 @@ public class JCGP { // fitness evaluators private FitnessFunction[] fitnessFunctions = new FitnessFunction[] { - new TestCaseEvaluator() }; + /*new TestCaseEvaluator()*/ }; private FitnessFunction fitnessFunction; /* @@ -401,7 +62,6 @@ public class JCGP { private boolean finished = false; - public JCGP() { population = new Population(resources); @@ -411,20 +71,17 @@ public class JCGP { mutator = mutators[0]; fitnessFunction = fitnessFunctions[0]; - - resources.setTestCases(new TestCase(new Integer[]{1, 2, 3}, new Integer[]{-4, 5, 6})); } - public Resources getResources() { + public ModifiableResources getResources() { return resources; } - + public Population getPopulation() { return population; } - /** * @return the mutators */ @@ -498,7 +155,7 @@ public class JCGP { public void nextGeneration() { if (!finished) { - fitnessFunction.evaluate(population, resources); + fitnessFunction.evaluate(population, (Resources) resources); report(); @@ -506,7 +163,7 @@ public class JCGP { // we still have generations left to go if (population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).getFitness() >= resources.getInt("maxFitness")) { // solution has been found, start next run - resources.println("Solution found in generation " + resources.getInt("currentGen") + ", chromosome: " + evolutionaryAlgorithm.getFittestChromosome()); + resources.println("Solution found on generation " + resources.getInt("currentGen") + ", chromosome: " + evolutionaryAlgorithm.getFittestChromosome()); if (resources.getInt("currentRun") < resources.getInt("runs")) { // there are still runs left @@ -514,7 +171,7 @@ public class JCGP { resources.set("currentGen", 0); // start a new population - population = new Population(resources); + population = new Population((Resources) resources); } else { // no more generations and no more runs, we're done finished = true; @@ -543,7 +200,7 @@ public class JCGP { } } - evolutionaryAlgorithm.evolve(population, mutator, resources); + evolutionaryAlgorithm.evolve(population, mutator, (Resources) resources); } @@ -553,7 +210,6 @@ public class JCGP { resources.println("Generation: " + resources.getInt("currentGen") + ", fitness: " + population.getChromosome(evolutionaryAlgorithm.getFittestChromosome()).getFitness()); } } - } public void start() { @@ -581,4 +237,8 @@ public class JCGP { return finished; } + public void setConsole(Console console) { + resources.setConsole(console); + } + } diff --git a/src/jcgp/backend/exceptions/ManagedModuleException.java b/src/jcgp/backend/exceptions/ManagedModuleException.java deleted file mode 100644 index 3452d35..0000000 --- a/src/jcgp/backend/exceptions/ManagedModuleException.java +++ /dev/null @@ -1,10 +0,0 @@ -package jcgp.backend.exceptions; - -public class ManagedModuleException extends RuntimeException { - - /** - * - */ - private static final long serialVersionUID = -3450063156524494967L; - -} diff --git a/src/jcgp/backend/function/BitwiseLogic.java b/src/jcgp/backend/function/BitwiseLogic.java index bfe361f..5d47ff7 100644 --- a/src/jcgp/backend/function/BitwiseLogic.java +++ b/src/jcgp/backend/function/BitwiseLogic.java @@ -1,8 +1,5 @@ package jcgp.backend.function; -import java.util.ArrayList; -import java.util.Arrays; - import jcgp.backend.exceptions.InvalidArgumentsException; import jcgp.backend.population.Connection; @@ -19,15 +16,13 @@ public class BitwiseLogic extends FunctionSet { new Nor(), new Xnor()}; - allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList)); + enableAll(); } - public static class And extends Function { - private int arity = 2; - + public static class And extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -40,7 +35,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -49,12 +44,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Or extends Function { - private int arity = 2; - + public static class Or extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -67,7 +60,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -76,12 +69,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Not extends Function { - private int arity = 1; - + public static class Not extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 1) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -93,7 +84,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 1; } @Override @@ -102,12 +93,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Xor extends Function { - private int arity = 2; - + public static class Xor extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -120,7 +109,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -129,12 +118,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Nand extends Function { - private int arity = 2; - + public static class Nand extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -147,7 +134,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -156,12 +143,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Nor extends Function { - private int arity = 2; - + public static class Nor extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -174,7 +159,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -183,12 +168,10 @@ public class BitwiseLogic extends FunctionSet { } } - public static class Xnor extends Function { - private int arity = 2; - + public static class Xnor extends Function { @Override public Object run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { int arg1 = ((int) connections[0].getValue()); @@ -201,7 +184,7 @@ public class BitwiseLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override diff --git a/src/jcgp/backend/function/BooleanLogic.java b/src/jcgp/backend/function/BooleanLogic.java index e0b5c9c..9e7d1ff 100644 --- a/src/jcgp/backend/function/BooleanLogic.java +++ b/src/jcgp/backend/function/BooleanLogic.java @@ -1,7 +1,5 @@ package jcgp.backend.function; -import java.util.ArrayList; -import java.util.Arrays; import jcgp.backend.exceptions.InvalidArgumentsException; import jcgp.backend.population.Connection; @@ -19,15 +17,13 @@ public class BooleanLogic extends FunctionSet { new Nor(), new Xnor()}; - allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList)); + enableAll(); } - public static class And extends Function { - private int arity = 2; - + public static class And extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -40,7 +36,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -49,12 +45,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Or extends Function { - private int arity = 2; - + public static class Or extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -67,7 +61,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -76,12 +70,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Not extends Function { - private int arity = 1; - + public static class Not extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 1) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -93,7 +85,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 1; } @Override @@ -102,12 +94,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Xor extends Function { - private int arity = 2; - + public static class Xor extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -120,7 +110,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -129,12 +119,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Nand extends Function { - private int arity = 2; - + public static class Nand extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -147,7 +135,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -156,12 +144,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Nor extends Function { - private int arity = 2; - + public static class Nor extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -174,7 +160,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -183,12 +169,10 @@ public class BooleanLogic extends FunctionSet { } } - public static class Xnor extends Function { - private int arity = 2; - + public static class Xnor extends Function { @Override public Boolean run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Boolean arg1 = ((Boolean) connections[0].getValue()); @@ -201,7 +185,7 @@ public class BooleanLogic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override diff --git a/src/jcgp/backend/function/FunctionSet.java b/src/jcgp/backend/function/FunctionSet.java index 4470ac8..78801fc 100644 --- a/src/jcgp/backend/function/FunctionSet.java +++ b/src/jcgp/backend/function/FunctionSet.java @@ -1,6 +1,7 @@ package jcgp.backend.function; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; @@ -11,7 +12,7 @@ import java.util.Iterator; */ public abstract class FunctionSet { protected Function[] functionList; - protected ArrayList<Function> allowedFunctions; + protected ArrayList<Integer> allowedFunctions; protected String name; public int getAllowedFunctionCount() { @@ -23,7 +24,7 @@ public abstract class FunctionSet { } public Function getAllowedFunction(int index) { - return allowedFunctions.get(index); + return functionList[allowedFunctions.get(index)]; } public Function getFunction(int index) { @@ -32,7 +33,7 @@ public abstract class FunctionSet { public int getMaxArity(){ int arity = 0; - for (Function function : allowedFunctions) { + for (Function function : functionList) { if (function.getArity() > arity) { arity = function.getArity(); } @@ -46,9 +47,9 @@ public abstract class FunctionSet { public void disableFunction(int index) { if (index < functionList.length) { - for (Iterator<Function> iterator = allowedFunctions.iterator(); iterator.hasNext();) { - Function function = (Function) iterator.next(); - if (function == functionList[index]) { + for (Iterator<Integer> iterator = allowedFunctions.iterator(); iterator.hasNext();) { + int function = iterator.next(); + if (function == index) { iterator.remove(); } } @@ -58,8 +59,9 @@ public abstract class FunctionSet { } public void enableFunction(int index) { - if (!allowedFunctions.contains(functionList[index])) { - allowedFunctions.add(functionList[index]); + if (!allowedFunctions.contains(index)) { + allowedFunctions.add(index); + Collections.sort(allowedFunctions); } } @@ -69,6 +71,18 @@ public abstract class FunctionSet { } public boolean isEnabled(Function f) { - return allowedFunctions.contains(f); + for (int i = 0; i < allowedFunctions.size(); i++) { + if (functionList[allowedFunctions.get(i)] == f) { + return true; + } + } + return false; + } + + protected void enableAll() { + allowedFunctions = new ArrayList<Integer>(); + for (int i = 0; i < functionList.length; i++) { + allowedFunctions.add(i); + } } }
\ No newline at end of file diff --git a/src/jcgp/backend/function/Arithmetic.java b/src/jcgp/backend/function/IntegerArithmetic.java index 6971663..c08a72a 100644 --- a/src/jcgp/backend/function/Arithmetic.java +++ b/src/jcgp/backend/function/IntegerArithmetic.java @@ -1,31 +1,25 @@ package jcgp.backend.function; -import java.util.ArrayList; -import java.util.Arrays; - import jcgp.backend.exceptions.InvalidArgumentsException; import jcgp.backend.population.Connection; -public class Arithmetic extends FunctionSet { +public class IntegerArithmetic extends FunctionSet { - public Arithmetic() { - name = "Arithmetic"; + public IntegerArithmetic() { + name = "Integer Arithmetic"; functionList = new Function[]{ new Addition(), new Subtraction(), new Multiplication(), new Division()}; - allowedFunctions = new ArrayList<Function>(Arrays.asList(functionList)); + enableAll(); } public static class Addition extends Function { - - private int arity = 2; - @Override public Integer run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Integer arg1 = ((Integer) connections[0].getValue()); @@ -38,7 +32,7 @@ public class Arithmetic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -48,12 +42,9 @@ public class Arithmetic extends FunctionSet { } public static class Subtraction extends Function { - - private int arity = 2; - @Override public Integer run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Integer arg1 = ((Integer) connections[0].getValue()); @@ -66,7 +57,7 @@ public class Arithmetic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -76,12 +67,9 @@ public class Arithmetic extends FunctionSet { } public static class Multiplication extends Function { - - private int arity = 2; - @Override public Integer run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Integer arg1 = ((Integer) connections[0].getValue()); @@ -94,7 +82,7 @@ public class Arithmetic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override @@ -104,12 +92,9 @@ public class Arithmetic extends FunctionSet { } public static class Division extends Function { - - private int arity = 2; - @Override public Integer run(Connection... connections) { - if (connections.length < arity) { + if (connections.length < 2) { throw new InvalidArgumentsException("Not enough connections were given."); } else { Integer arg1 = ((Integer) connections[0].getValue()); @@ -127,7 +112,7 @@ public class Arithmetic extends FunctionSet { @Override public int getArity() { - return arity; + return 2; } @Override diff --git a/src/jcgp/backend/modules/Module.java b/src/jcgp/backend/modules/Module.java index 82c3d80..f2b91b0 100644 --- a/src/jcgp/backend/modules/Module.java +++ b/src/jcgp/backend/modules/Module.java @@ -1,11 +1,9 @@ package jcgp.backend.modules; -import jcgp.JCGP.Resources; -import jcgp.backend.parameters.Parameter; +import jcgp.backend.resources.parameters.Parameter; public interface Module { public Parameter[] getLocalParameters(); - public ModuleStatus getStatus(Resources resources); } diff --git a/src/jcgp/backend/modules/ModuleStatus.java b/src/jcgp/backend/modules/ModuleStatus.java deleted file mode 100644 index fec8490..0000000 --- a/src/jcgp/backend/modules/ModuleStatus.java +++ /dev/null @@ -1,16 +0,0 @@ -package jcgp.backend.modules; - -public enum ModuleStatus { - ERROR, WARNING, READY; - - private String details; - - public void setDetails(String details) { - this.details = details; - } - - public String getDetails() { - return details; - } - -} diff --git a/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java index 7719111..3aca104 100644 --- a/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java +++ b/src/jcgp/backend/modules/ea/EvolutionaryAlgorithm.java @@ -1,9 +1,9 @@ package jcgp.backend.modules.ea; -import jcgp.JCGP.Resources; import jcgp.backend.modules.Module; import jcgp.backend.modules.mutator.Mutator; import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; public interface EvolutionaryAlgorithm extends Module { diff --git a/src/jcgp/backend/modules/ea/MuPlusLambda.java b/src/jcgp/backend/modules/ea/MuPlusLambda.java index ad8c5d8..0d16111 100644 --- a/src/jcgp/backend/modules/ea/MuPlusLambda.java +++ b/src/jcgp/backend/modules/ea/MuPlusLambda.java @@ -1,13 +1,12 @@ package jcgp.backend.modules.ea; -import jcgp.JCGP.Resources; -import jcgp.backend.modules.ModuleStatus; import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; -import jcgp.backend.parameters.ParameterStatus; import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; /** * (μ + λ) EA. @@ -62,8 +61,7 @@ public class MuPlusLambda implements EvolutionaryAlgorithm { @Override public void evolve(Population population, Mutator mutator, Resources resources) { - // TODO actually use parents and offspring - // select fittest chromosome + // select fittest chromosomes fittestChromosome = 0; for (int i = 1; i < resources.getInt("popSize"); i++) { @@ -79,6 +77,7 @@ public class MuPlusLambda implements EvolutionaryAlgorithm { mutator.mutate(population.getChromosome(i), resources); } } + } @Override @@ -95,10 +94,5 @@ public class MuPlusLambda implements EvolutionaryAlgorithm { public String toString() { return "(μ + λ)"; } - - @Override - public ModuleStatus getStatus(Resources resources) { - return ModuleStatus.READY; - } } diff --git a/src/jcgp/backend/modules/ea/TournamentSelection.java b/src/jcgp/backend/modules/ea/TournamentSelection.java index 32ac54d..baf6704 100644 --- a/src/jcgp/backend/modules/ea/TournamentSelection.java +++ b/src/jcgp/backend/modules/ea/TournamentSelection.java @@ -2,12 +2,11 @@ package jcgp.backend.modules.ea; import java.util.HashMap; -import jcgp.JCGP.Resources; -import jcgp.backend.modules.ModuleStatus; import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; public class TournamentSelection implements EvolutionaryAlgorithm { @@ -51,9 +50,4 @@ public class TournamentSelection implements EvolutionaryAlgorithm { public String toString() { return "Tournament"; } - - @Override - public ModuleStatus getStatus(Resources resources) { - return null; - } } diff --git a/src/jcgp/backend/modules/fitness/FitnessFunction.java b/src/jcgp/backend/modules/fitness/FitnessFunction.java index ffce9b7..d3b76cb 100644 --- a/src/jcgp/backend/modules/fitness/FitnessFunction.java +++ b/src/jcgp/backend/modules/fitness/FitnessFunction.java @@ -1,8 +1,8 @@ package jcgp.backend.modules.fitness; -import jcgp.JCGP.Resources; import jcgp.backend.modules.Module; import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; public interface FitnessFunction extends Module { diff --git a/src/jcgp/backend/modules/fitness/TestCase.java b/src/jcgp/backend/modules/fitness/TestCase.java deleted file mode 100644 index 23f7ecf..0000000 --- a/src/jcgp/backend/modules/fitness/TestCase.java +++ /dev/null @@ -1,29 +0,0 @@ -package jcgp.backend.modules.fitness; - -public class TestCase { - - private Object[] inputs; - private Object[] outputs; - - public TestCase(Object[] inputs, Object[] outputs) { - this.inputs = inputs; - this.outputs = outputs; - } - - public Object getInput(int index) { - return inputs[index]; - } - - public Object getOutput(int index) { - return outputs[index]; - } - - public Object[] getInputs() { - return inputs; - } - - public Object[] getOutputs() { - return outputs; - } - -} diff --git a/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java b/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java deleted file mode 100644 index 5ff6973..0000000 --- a/src/jcgp/backend/modules/fitness/TestCaseEvaluator.java +++ /dev/null @@ -1,43 +0,0 @@ -package jcgp.backend.modules.fitness; - -import jcgp.JCGP.Resources; -import jcgp.backend.modules.ModuleStatus; -import jcgp.backend.parameters.Parameter; -import jcgp.backend.population.Population; - -public class TestCaseEvaluator implements FitnessFunction { - - @Override - public void evaluate(Population population, Resources resources) { - // for every chromosome in the population - for (int i = 0; i < resources.getInt("popSize"); i++) { - int fitness = 0; - // for every test case - for (int t = 0; t < resources.getTestCaseCount(); t++) { - population.getChromosome(i).setInputs(resources.getTestCase(t).getInputs()); - // check every output - for (int o = 0; o < resources.getInt("outputs"); o++) { - if (population.getChromosome(i).getOutput(o).calculate() == resources.getTestCase(t).getOutput(o)) { - fitness++; - } - } - } - population.getChromosome(i).setFitness(fitness); - } - } - - @Override - public Parameter[] getLocalParameters() { - return null; - } - - @Override - public String toString() { - return "Test case"; - } - - @Override - public ModuleStatus getStatus(Resources resources) { - return null; - } -} diff --git a/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java b/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java new file mode 100644 index 0000000..51676e3 --- /dev/null +++ b/src/jcgp/backend/modules/fitness/testcase/TestCaseEvaluator.java @@ -0,0 +1,75 @@ +package jcgp.backend.modules.fitness.testcase; + +import java.util.ArrayList; + +import jcgp.backend.modules.fitness.FitnessFunction; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; + +/** + * + * This fitness function module implements a simple test case evaluator. + * + * A TestCase object is a + * + * + * @author Eduardo Pedroni + * + */ +public abstract class TestCaseEvaluator implements FitnessFunction { + + public static class TestCase<T> { + + private T[] inputs; + private T[] outputs; + + public TestCase(T[] inputs, T[] outputs) { + this.inputs = inputs; + this.outputs = outputs; + } + + public T getInput(int index) { + return inputs[index]; + } + + public T getOutput(int index) { + return outputs[index]; + } + + public T[] getInputs() { + return inputs; + } + + public T[] getOutputs() { + return outputs; + } + } + + protected ArrayList<TestCase> testCases; + + @Override + public void evaluate(Population population, Resources resources) { + // for every chromosome in the population + for (int i = 0; i < resources.getInt("popSize"); i++) { + // assume an initial fitness of 0 + int fitness = 0; + // for each test case + for (int t = 0; t < testCases.size(); t++) { + population.getChromosome(i).setInputs(testCases.get(t).getInputs()); + // check each output + for (int o = 0; o < resources.getInt("outputs"); o++) { + if (population.getChromosome(i).getOutput(o).calculate() == testCases.get(t).getOutput(o)) { + fitness++; + } + } + } + // assign the resulting fitness to the respective individual + population.getChromosome(i).setFitness(fitness); + } + } + + public int getMaxFitness() { + return 0; + } +} + diff --git a/src/jcgp/backend/modules/mutator/Mutator.java b/src/jcgp/backend/modules/mutator/Mutator.java index fd7cd27..ffdd35c 100644 --- a/src/jcgp/backend/modules/mutator/Mutator.java +++ b/src/jcgp/backend/modules/mutator/Mutator.java @@ -1,8 +1,8 @@ package jcgp.backend.modules.mutator; -import jcgp.JCGP.Resources; import jcgp.backend.modules.Module; import jcgp.backend.population.Chromosome; +import jcgp.backend.resources.Resources; public interface Mutator extends Module { diff --git a/src/jcgp/backend/modules/mutator/PointMutator.java b/src/jcgp/backend/modules/mutator/PointMutator.java index cdac8bb..54d5f3d 100644 --- a/src/jcgp/backend/modules/mutator/PointMutator.java +++ b/src/jcgp/backend/modules/mutator/PointMutator.java @@ -1,33 +1,39 @@ package jcgp.backend.modules.mutator; import jcgp.backend.function.Function; -import jcgp.backend.modules.ModuleStatus; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.Parameter; import jcgp.backend.population.Chromosome; import jcgp.backend.population.MutableElement; import jcgp.backend.population.Node; import jcgp.backend.population.Output; -import jcgp.JCGP.Resources; +import jcgp.backend.resources.Resources; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; public class PointMutator implements Mutator { private DoubleParameter mutationRate; - private ModuleStatus status = ModuleStatus.READY; - - public PointMutator() { - mutationRate = new DoubleParameter(0.5, "Percent mutation", false, false) { + public PointMutator(final Resources resources) { + mutationRate = new DoubleParameter(50, "Percent mutation", false, false) { @Override public void validate(double newValue) { - // TODO this + if (newValue <= 0 || newValue > 100) { + status = ParameterStatus.INVALID; + status.setDetails("Mutation rate must be > 0 and <= 100"); + } else if ((int) ((newValue / 100) * resources.getDouble("nodes")) <= 0) { + status = ParameterStatus.WARNING; + status.setDetails("With mutation rate " + mutationRate.get() + ", 0 genes will be mutated."); + } else { + status = ParameterStatus.VALID; + } } }; } @Override public void mutate(Chromosome chromosome, Resources resources) { - int mutations = (int) Math.ceil(((mutationRate.get()) * ((((resources.getDouble("nodes")) + (resources.getDouble("outputs")))) / (double) 100))); + int mutations = (int) ((mutationRate.get()) * ((((resources.getDouble("nodes")) + (resources.getDouble("outputs")))) / 100)); for (int i = 0; i < mutations; i++) { MutableElement m = chromosome.getRandomMutableElement(); @@ -55,18 +61,4 @@ public class PointMutator implements Mutator { return "Point mutation"; } - @Override - public ModuleStatus getStatus(Resources resources) { - if (mutationRate.get() <= 0 || mutationRate.get() > 100) { - status = ModuleStatus.ERROR; - status.setDetails("Mutation rate must be > 0 and <= 100"); - } else if ((int) ((mutationRate.get() / 100) * resources.getDouble("nodes")) > 0) { - status = ModuleStatus.WARNING; - status.setDetails("With mutation rate " + mutationRate.get() + ", no mutations will occur."); - } else { - status = ModuleStatus.READY; - status.setDetails(""); - } - return status; - } } diff --git a/src/jcgp/backend/population/Chromosome.java b/src/jcgp/backend/population/Chromosome.java index c7493b9..d438375 100644 --- a/src/jcgp/backend/population/Chromosome.java +++ b/src/jcgp/backend/population/Chromosome.java @@ -2,8 +2,8 @@ package jcgp.backend.population; import java.util.ArrayList; -import jcgp.JCGP.Resources; import jcgp.backend.exceptions.ParameterMismatchException; +import jcgp.backend.resources.Resources; public class Chromosome { diff --git a/src/jcgp/backend/population/Node.java b/src/jcgp/backend/population/Node.java index f1d37a1..6960ded 100644 --- a/src/jcgp/backend/population/Node.java +++ b/src/jcgp/backend/population/Node.java @@ -37,7 +37,7 @@ public class Node extends Gene implements MutableElement, Connection { public void initialise(Function newFunction, Connection ... newConnections) throws InsufficientConnectionsException { function = newFunction; - if (newConnections.length == function.getArity()) { + if (newConnections.length == chromosome.getResources().getInt("arity")) { connections = newConnections; } else { throw new InsufficientConnectionsException(); diff --git a/src/jcgp/backend/population/Population.java b/src/jcgp/backend/population/Population.java index 7b62d27..d2e6058 100644 --- a/src/jcgp/backend/population/Population.java +++ b/src/jcgp/backend/population/Population.java @@ -1,7 +1,6 @@ package jcgp.backend.population; -import jcgp.JCGP.Resources; - +import jcgp.backend.resources.Resources; public class Population { @@ -56,7 +55,6 @@ public class Population { return chromosomes[resources.getRandomInt(chromosomes.length)]; } - /** * Copy a chromosome into a different position. * After this returns, the target chromosome has @@ -75,4 +73,6 @@ public class Population { } + + } diff --git a/src/jcgp/gui/console/Console.java b/src/jcgp/backend/resources/Console.java index 63c7f5b..6bbd5ba 100644 --- a/src/jcgp/gui/console/Console.java +++ b/src/jcgp/backend/resources/Console.java @@ -1,4 +1,4 @@ -package jcgp.gui.console; +package jcgp.backend.resources; public interface Console { diff --git a/src/jcgp/backend/resources/ModifiableResources.java b/src/jcgp/backend/resources/ModifiableResources.java new file mode 100644 index 0000000..3e6b55e --- /dev/null +++ b/src/jcgp/backend/resources/ModifiableResources.java @@ -0,0 +1,42 @@ +package jcgp.backend.resources; + +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.IntegerParameter; + +/** + * + * This subclass of Resources allows modifications to be made. + * A read-only cast of this class is passed to modules for safety, + * and only classes with access to a JCGP instance may modify + * the resources. + * + * @author eddy + * + */ +public class ModifiableResources extends Resources { + + public ModifiableResources() { + super(); + } + + public void set(String key, Object value) { + if (parameters.get(key) instanceof IntegerParameter) { + ((IntegerParameter) parameters.get(key)).set(((Integer) value).intValue()); + } else if (parameters.get(key) instanceof DoubleParameter) { + ((DoubleParameter) parameters.get(key)).set(((Double) value).doubleValue()); + } else if (parameters.get(key) instanceof BooleanParameter) { + ((BooleanParameter) parameters.get(key)).set(((Boolean) value).booleanValue()); + } + } + + public void setFunctionSet(int index) { + functionSet = functionSets[index]; + set("arity", functionSet.getMaxArity()); + } + + public void setConsole(Console console) { + this.console = console; + } + +} diff --git a/src/jcgp/backend/resources/Resources.java b/src/jcgp/backend/resources/Resources.java new file mode 100644 index 0000000..c1c3e4c --- /dev/null +++ b/src/jcgp/backend/resources/Resources.java @@ -0,0 +1,310 @@ +package jcgp.backend.resources; + +import java.util.HashMap; +import java.util.Random; + +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import jcgp.backend.function.BitwiseLogic; +import jcgp.backend.function.BooleanLogic; +import jcgp.backend.function.Function; +import jcgp.backend.function.FunctionSet; +import jcgp.backend.function.IntegerArithmetic; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; + +/** + * + * The resources class encapsulates all of the resources based on which the program operates. + * Each instance of JCGP contains a single instance of Resources, which gets passed to the selected + * modules as the program executes. + * + * @author Eduardo Pedroni + * + */ +public class Resources { + protected HashMap<String, Parameter> parameters = new HashMap<String, Parameter>(); + + protected Random numberGenerator = new Random(); + + // function sets + protected FunctionSet[] functionSets = new FunctionSet[] { + new IntegerArithmetic(), + new BitwiseLogic(), + new BooleanLogic() }; + protected FunctionSet functionSet = functionSets[0]; + + // GUI console + protected Console console; + + public Resources() { + createBaseParameters(); + } + + public int getInt(String key) { + if (parameters.get(key) instanceof IntegerParameter) { + return ((IntegerParameter) parameters.get(key)).get(); + } else if (parameters.get(key) instanceof DoubleParameter) { + return (int) ((DoubleParameter) parameters.get(key)).get(); + } else { + throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to int."); + } + } + + public double getDouble(String key) { + if (parameters.get(key) instanceof IntegerParameter) { + return ((IntegerParameter) parameters.get(key)).get(); + } else if (parameters.get(key) instanceof DoubleParameter) { + return ((DoubleParameter) parameters.get(key)).get(); + } else { + throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to double."); + } + } + + public boolean getBoolean(String key) { + if (parameters.get(key) instanceof BooleanParameter) { + return ((BooleanParameter) parameters.get(key)).get(); + } else { + throw new ClassCastException("Could not cast " + parameters.get(key).getClass() + " to int."); + } + } + + public Parameter getParameter(String key) { + return parameters.get(key); + } + + public boolean contains(String key) { + return parameters.containsKey(key); + } + + private void createBaseParameters() { + parameters.put("rows", new IntegerParameter(8, "Rows", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 row."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("columns", new IntegerParameter(9, "Columns", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 column."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("inputs", new IntegerParameter(3, "Inputs", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 input."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("outputs", new IntegerParameter(3, "Outputs", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Chromosome must have at least 1 output."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("popSize", new IntegerParameter(5, "Population", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Population size must be at least 1."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("levelsBack", new IntegerParameter(2, "Levels back", false, true) { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be at least 1."); + } else if (newValue > getInt("columns")) { + status = ParameterStatus.INVALID; + status.setDetails("Levels back must be less than or equal to the number of columns."); + } else { + status = ParameterStatus.VALID; + } + } + }); + + IntegerParameter nodes = new IntegerParameter(1, "Nodes", true, false) { + @Override + public void validate(int newValue) { + // blank + } + }; + nodes.valueProperty().bind(((IntegerParameter) parameters.get("rows")).valueProperty().multiply(((IntegerParameter) parameters.get("columns")).valueProperty())); + parameters.put("nodes", nodes); + + parameters.put("generations", new IntegerParameter(1000000, "Generations") { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Number of generations must be greater than 0."); + } else if (newValue < getInt("currentGen")) { + status = ParameterStatus.WARNING_RESET; + status.setDetails("Setting generations to less than the current generation will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("currentGen", new IntegerParameter(1, "Generation", true, false) { + @Override + public void validate(int newValue) { + // blank + } + }); + + parameters.put("runs", new IntegerParameter(5, "Runs") { + @Override + public void validate(int newValue) { + if (newValue <= 0) { + status = ParameterStatus.INVALID; + status.setDetails("Number of runs must be greater than 0."); + } else if (newValue < getInt("currentRun")) { + status = ParameterStatus.WARNING_RESET; + status.setDetails("Setting runs to less than the current run will cause the experiment to restart."); + } else { + status = ParameterStatus.VALID; + } + } + }); + parameters.put("currentRun", new IntegerParameter(1, "Run", true, false) { + @Override + public void validate(int newValue) { + // blank + } + }); + + parameters.put("arity", new IntegerParameter(functionSet.getMaxArity(), "Max arity", true, false) { + @Override + public void validate(int newValue) { + // blank + } + }); + + IntegerParameter seed = new IntegerParameter(1234, "Seed", false, true) { + @Override + public void validate(int newValue) { + status = ParameterStatus.VALID; + } + }; + seed.valueProperty().addListener(new ChangeListener<Number>() { + @Override + public void changed( + ObservableValue<? extends Number> observable, + Number oldValue, Number newValue) { + numberGenerator.setSeed(newValue.longValue()); + } + }); + numberGenerator.setSeed(seed.get()); + parameters.put("seed", seed); + + parameters.put("report", new IntegerParameter(1, "Report", false, false) { + @Override + public void validate(int newValue) { + if (newValue > getInt("generations")) { + status = ParameterStatus.WARNING; + status.setDetails("No reports will be printed."); + } else { + status = ParameterStatus.VALID; + } + } + }); + } + + /* + * Utility functions + */ + public int getRandomInt(int limit) { + return numberGenerator.nextInt(limit); + } + + public double getRandomDouble(int limit) { + return numberGenerator.nextDouble() * limit; + } + + public double getRandomDouble() { + return numberGenerator.nextDouble(); + } + + /* + * FunctionSet functions + */ + public Function getRandomFunction() { + Function f = functionSet.getAllowedFunction(numberGenerator.nextInt(functionSet.getAllowedFunctionCount())); + return f; + } + + public Function getFunction(int index) { + return functionSet.getAllowedFunction(index); + } + + /** + * @return the functionSets + */ + public FunctionSet[] getFunctionSets() { + return functionSets; + } + + /** + * @return the functionSet + */ + public FunctionSet getFunctionSet() { + return functionSet; + } + +// /* +// * Test cases +// */ +// public TestCase getTestCase(int index) { +// return testCases[index]; +// } +// +// public int getTestCaseCount() { +// return testCases.length; +// } + + /* + * Console functionality + */ + public void println(String s) { + System.out.println(s); + if (console != null) { + console.println(s); + } + } + + public void print(String s) { + System.out.print(s); + if (console != null) { + console.print(s); + } + } +}
\ No newline at end of file diff --git a/src/jcgp/backend/parameters/BooleanParameter.java b/src/jcgp/backend/resources/parameters/BooleanParameter.java index 43825be..cd17649 100644 --- a/src/jcgp/backend/parameters/BooleanParameter.java +++ b/src/jcgp/backend/resources/parameters/BooleanParameter.java @@ -1,4 +1,4 @@ -package jcgp.backend.parameters; +package jcgp.backend.resources.parameters; import javafx.beans.property.SimpleBooleanProperty; @@ -35,6 +35,7 @@ public abstract class BooleanParameter extends Parameter { public abstract void validate(boolean newValue); + @Override public SimpleBooleanProperty valueProperty() { return value; } diff --git a/src/jcgp/backend/parameters/DoubleParameter.java b/src/jcgp/backend/resources/parameters/DoubleParameter.java index 53551f5..8464c83 100644 --- a/src/jcgp/backend/parameters/DoubleParameter.java +++ b/src/jcgp/backend/resources/parameters/DoubleParameter.java @@ -1,4 +1,4 @@ -package jcgp.backend.parameters; +package jcgp.backend.resources.parameters; import javafx.beans.property.SimpleDoubleProperty; @@ -26,6 +26,7 @@ public abstract class DoubleParameter extends Parameter { } } + @Override public SimpleDoubleProperty valueProperty() { return value; } diff --git a/src/jcgp/backend/parameters/IntegerParameter.java b/src/jcgp/backend/resources/parameters/IntegerParameter.java index 68095d9..2a7b2a7 100644 --- a/src/jcgp/backend/parameters/IntegerParameter.java +++ b/src/jcgp/backend/resources/parameters/IntegerParameter.java @@ -1,4 +1,4 @@ -package jcgp.backend.parameters; +package jcgp.backend.resources.parameters; import javafx.beans.property.SimpleIntegerProperty; @@ -27,6 +27,7 @@ public abstract class IntegerParameter extends Parameter { } } + @Override public SimpleIntegerProperty valueProperty() { return value; } diff --git a/src/jcgp/backend/parameters/Parameter.java b/src/jcgp/backend/resources/parameters/Parameter.java index ddd5d5b..7e12ff8 100644 --- a/src/jcgp/backend/parameters/Parameter.java +++ b/src/jcgp/backend/resources/parameters/Parameter.java @@ -1,4 +1,4 @@ -package jcgp.backend.parameters; +package jcgp.backend.resources.parameters; import javafx.beans.property.Property; diff --git a/src/jcgp/backend/parameters/ParameterStatus.java b/src/jcgp/backend/resources/parameters/ParameterStatus.java index 86abe33..11da4c2 100644 --- a/src/jcgp/backend/parameters/ParameterStatus.java +++ b/src/jcgp/backend/resources/parameters/ParameterStatus.java @@ -1,4 +1,4 @@ -package jcgp.backend.parameters; +package jcgp.backend.resources.parameters; public enum ParameterStatus { INVALID, WARNING, WARNING_RESET, VALID; diff --git a/src/jcgp/backend/tests/ChromosomeTests.java b/src/jcgp/backend/tests/ChromosomeTests.java index 1cd067e..a16ba75 100644 --- a/src/jcgp/backend/tests/ChromosomeTests.java +++ b/src/jcgp/backend/tests/ChromosomeTests.java @@ -2,13 +2,13 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import jcgp.JCGP.Resources; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.MutableElement; import jcgp.backend.population.Node; import jcgp.backend.population.Output; +import jcgp.backend.resources.ModifiableResources; import org.junit.Before; import org.junit.BeforeClass; @@ -40,11 +40,11 @@ import org.junit.Test; public class ChromosomeTests { private Chromosome chromosome; - private static Resources resources; + private static ModifiableResources resources; @BeforeClass public static void setUpBeforeClass() { - resources = new Resources(); + resources = new ModifiableResources(); } @Before diff --git a/src/jcgp/backend/tests/NodeTests.java b/src/jcgp/backend/tests/NodeTests.java index 7121e81..2294816 100644 --- a/src/jcgp/backend/tests/NodeTests.java +++ b/src/jcgp/backend/tests/NodeTests.java @@ -1,12 +1,13 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; -import jcgp.JCGP.Resources; -import jcgp.backend.function.Arithmetic; +import jcgp.backend.exceptions.InvalidArgumentsException; import jcgp.backend.function.Function; +import jcgp.backend.function.IntegerArithmetic; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Node; +import jcgp.backend.resources.Resources; import org.junit.Before; import org.junit.BeforeClass; @@ -48,7 +49,7 @@ public class NodeTests { public void setUp() throws Exception { node = new Node(chromosome, 0, 0, resources.getInt("arity")); // make node with anonymous addition function and hard-coded value connections - node.initialise(new Arithmetic.Addition(), + node.initialise(new IntegerArithmetic.Addition(), new Connection[]{new Connection() { @Override @@ -126,7 +127,7 @@ public class NodeTests { ((int) node.getValue()) == arg1 + arg2); // put in a different function, check the output has changed appropriately - node.setFunction(new Arithmetic.Subtraction()); + node.setFunction(new IntegerArithmetic.Subtraction()); assertTrue("Node did not return expected value (difference of arguments).", ((Integer) node.getValue()) == arg1 - arg2); @@ -166,7 +167,28 @@ public class NodeTests { } }; - node.initialise(null, conn0, conn1); + + Function function = new Function() { + @Override + public Object run(Connection... connections) + throws InvalidArgumentsException { + // blank + return null; + } + + @Override + public int getArity() { + return 2; + } + + @Override + public String getName() { + // blank + return null; + } + }; + + node.initialise(function, conn0, conn1); assertTrue("Connection 0 is incorrect.", node.getConnection(0) == conn0); assertTrue("Connection 1 is incorrect.", node.getConnection(1) == conn1); diff --git a/src/jcgp/backend/tests/OutputTests.java b/src/jcgp/backend/tests/OutputTests.java index 7ff8a4a..b2bc7ec 100644 --- a/src/jcgp/backend/tests/OutputTests.java +++ b/src/jcgp/backend/tests/OutputTests.java @@ -1,10 +1,10 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; -import jcgp.JCGP.Resources; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Output; +import jcgp.backend.resources.Resources; import org.junit.Before; import org.junit.BeforeClass; diff --git a/src/jcgp/backend/tests/PopulationTests.java b/src/jcgp/backend/tests/PopulationTests.java index 51b5168..31df8b9 100644 --- a/src/jcgp/backend/tests/PopulationTests.java +++ b/src/jcgp/backend/tests/PopulationTests.java @@ -1,9 +1,9 @@ package jcgp.backend.tests; import static org.junit.Assert.assertTrue; -import jcgp.JCGP.Resources; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; import org.junit.Before; import org.junit.BeforeClass; diff --git a/src/jcgp/gui/GUI.java b/src/jcgp/gui/GUI.java index f7aa25d..8f28884 100644 --- a/src/jcgp/gui/GUI.java +++ b/src/jcgp/gui/GUI.java @@ -5,14 +5,16 @@ import javafx.application.Platform; import javafx.concurrent.Service; import javafx.concurrent.Task; import javafx.scene.Scene; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Pane; import javafx.stage.Stage; import jcgp.JCGP; -import jcgp.JCGP.Resources; +import jcgp.backend.resources.Resources; import jcgp.gui.console.GUIConsole; +import jcgp.gui.dragresize.HorizontalDragResize; +import jcgp.gui.dragresize.VerticalDragResize; +import jcgp.gui.population.FunctionSelector; +import jcgp.gui.population.PopulationPane; import jcgp.gui.settings.SettingsPane; public class GUI extends Application { @@ -28,10 +30,6 @@ public class GUI extends Application { public static final String GOOD_SELECTION_COLOUR = "#38C25B"; public static final String NEUTRAL_SELECTION_COLOUR = "#FFEF73"; public static final String BAD_SELECTION_COLOUR = "#FF5C5C"; - - public static final String INVALID_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + BAD_SELECTION_COLOUR; - public static final String WARNING_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + NEUTRAL_SELECTION_COLOUR; - public static final String VALID_PARAMETER_STYLE = "-fx-border-color: C9C9C9; -fx-border-radius: 2; -fx-padding: 0; -fx-background-color: " + NEUTRAL_COLOUR; /* Sizes and distances */ public static final double RESIZE_MARGIN = 5.0; @@ -41,17 +39,16 @@ public class GUI extends Application { public static final double WRAP_WIDTH = 90; - private static JCGP cgp; - public static Resources resources; + public static final JCGP jcgp = new JCGP(); + public static final Resources resources = jcgp.getResources(); - private BorderPane leftPane; - private BorderPane window; + public static final FunctionSelector functionSelector = new FunctionSelector(resources.getFunctionSet()); - private ChromosomePane[] chromosomes; - private TabPane chromosomeTabs; + private PopulationPane populationPane; private GUIConsole console = new GUIConsole(); - private SettingsPane settings; + + private SettingsPane settingsPane; private boolean evolving = false; @@ -63,14 +60,14 @@ public class GUI extends Application { Task<Void> t = new Task<Void>() { @Override protected Void call() throws Exception { - while (!isCancelled() && !cgp.isFinished()) { + while (!isCancelled() && !jcgp.isFinished()) { synchronized (printLock) { Platform.runLater(consoleFlush); - cgp.nextGeneration(); + jcgp.nextGeneration(); printLock.wait(); } } - if (cgp.isFinished()) { + if (jcgp.isFinished()) { Platform.runLater(new Runnable() { @Override public void run() { @@ -96,84 +93,55 @@ public class GUI extends Application { }; public static void main(String[] args) { - cgp = new JCGP(); - resources = cgp.getResources(); +// jcgp = new JCGP(); +// resources = jcgp.getResources(); +// +// functionSelector = launch(); } @Override public void start(Stage primaryStage) throws Exception { - resources.setConsole(console); + jcgp.setConsole(console); /* * Instantiate the various GUI elements here. * * */ + BorderPane leftFrame = new BorderPane(); - leftPane = new BorderPane(); + populationPane = new PopulationPane(jcgp); - chromosomeTabs = new TabPane(); - chromosomeTabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - makeChromosomeTabPane(); + settingsPane = new SettingsPane(jcgp, this); - settings = new SettingsPane(cgp, this); + HorizontalDragResize.makeDragResizable(settingsPane); + VerticalDragResize.makeDragResizable(console); - leftPane.setCenter(chromosomeTabs); - leftPane.setBottom(console); + leftFrame.setCenter(populationPane); + leftFrame.setBottom(console); - window = new BorderPane(); + BorderPane experimentLayer = new BorderPane(); - window.setCenter(leftPane); - window.setRight(settings); + experimentLayer.setCenter(leftFrame); + experimentLayer.setRight(settingsPane); primaryStage.setTitle("JCGP"); - primaryStage.setScene(new Scene(window)); + Pane sceneParent = new Pane(); + experimentLayer.prefHeightProperty().bind(sceneParent.heightProperty()); + experimentLayer.prefWidthProperty().bind(sceneParent.widthProperty()); + sceneParent.getChildren().addAll(experimentLayer, functionSelector); + + primaryStage.setScene(new Scene(sceneParent)); + primaryStage.setMinWidth(800); + primaryStage.setMinHeight(600); primaryStage.show(); } - /** - * - */ - private void makeChromosomeTabPane() { - chromosomeTabs.getTabs().clear(); - - chromosomes = new ChromosomePane[cgp.getResources().getInt("popSize")]; - Tab tab; - for (int i = 0; i < chromosomes.length; i++) { - chromosomes[i] = new ChromosomePane(cgp.getPopulation().getChromosome(i), resources); - tab = new Tab("Chr " + i); - tab.setContent(chromosomes[i]); - chromosomeTabs.getTabs().add(tab); - } - } - - private void updateNodeGrids() { - for (int i = 0; i < chromosomes.length; i++) { - chromosomes[i].update(); - } - } - - private void unlockOutputs() { - for (int i = 0; i < chromosomes.length; i++) { - chromosomes[i].unlockOutputs(); - } - } - - private void relockOutputs() { - for (int i = 0; i < chromosomes.length; i++) { - chromosomes[i].relockOutputs(); - } - } - - public void disableChromosomePanes(boolean value) { - chromosomeTabs.setDisable(value); - } - public void runPause() { - if (!cgp.isFinished() && settings.areParametersValid()) { + if (!jcgp.isFinished() && settingsPane.areParametersValid()) { if (!evolving) { runningMode(true); cgpService.restart(); @@ -185,55 +153,49 @@ public class GUI extends Application { } public void step() { - if (!evolving && !cgp.isFinished() && settings.areParametersValid()) { - if (settings.isResetRequired()) { + if (!evolving && !jcgp.isFinished() && settingsPane.areParametersValid()) { + if (settingsPane.isResetRequired()) { reset(); } - unlockOutputs(); - Task<Void> task = new Task<Void>() { - @Override - protected Void call() throws Exception { - cgp.nextGeneration(); - Platform.runLater(consoleFlush); - return null; - } - }; - Thread t = new Thread(task); - t.start(); - try { - t.join(); - } catch (InterruptedException e) { - // nothing - } finally { - updateNodeGrids(); - relockOutputs(); - } + populationPane.unlockOutputs(); + + jcgp.nextGeneration(); + console.flush(); + + populationPane.updateGenes(); + populationPane.relockOutputs(); + } } public void reset() { - if (!evolving && settings.areParametersValid()) { - settings.applyParameters(); - cgp.reset(); - makeChromosomeTabPane(); - settings.revalidateParameters(); + if (!evolving && settingsPane.areParametersValid()) { + settingsPane.applyParameters(); + jcgp.reset(); + populationPane.remakeTabs(jcgp.getPopulation(), jcgp.getResources()); + settingsPane.revalidateParameters(); + console.flush(); } } private void runningMode(boolean value) { - chromosomeTabs.setDisable(value); - settings.disableSettings(value); + populationPane.setDisable(value); + settingsPane.runningMode(value); + if (value) { - unlockOutputs(); - settings.getRunButton().setText("Pause"); - if (settings.isResetRequired()) { + populationPane.unlockOutputs(); + if (settingsPane.isResetRequired()) { reset(); } } else { - updateNodeGrids(); - relockOutputs(); - settings.getRunButton().setText("Run"); + populationPane.updateGenes(); + populationPane.relockOutputs(); } evolving = value; } + + public static void updateFunctionSelector() { + functionSelector.remakeFunctions(resources.getFunctionSet()); + } + } diff --git a/src/jcgp/gui/console/GUIConsole.java b/src/jcgp/gui/console/GUIConsole.java index d8625f5..694f1a5 100644 --- a/src/jcgp/gui/console/GUIConsole.java +++ b/src/jcgp/gui/console/GUIConsole.java @@ -1,15 +1,12 @@ package jcgp.gui.console; -import javafx.event.EventHandler; -import javafx.scene.Cursor; import javafx.scene.control.TextArea; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; +import jcgp.backend.resources.Console; import jcgp.gui.GUI; public class GUIConsole extends AnchorPane implements Console { - private boolean dragging; private TextArea textArea = new TextArea("Welcome to JCGP!"); private StringBuffer printBuffer = new StringBuffer(); @@ -18,8 +15,6 @@ public class GUIConsole extends AnchorPane implements Console { super(); textArea.setEditable(false); - setResizeListeners(); - AnchorPane.setTopAnchor(textArea, GUI.RESIZE_MARGIN); AnchorPane.setBottomAnchor(textArea, 0.0); AnchorPane.setRightAnchor(textArea, 0.0); @@ -32,62 +27,6 @@ public class GUIConsole extends AnchorPane implements Console { } - /** - * - */ - private void setResizeListeners() { - setOnMousePressed(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - // ignore clicks outside of the draggable margin - if(isInDraggableZone(event)) { - dragging = true; - } - } - }); - setOnMouseDragged(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - if(dragging) { - double newHeight = getHeight() - event.getY(); - if (newHeight >= getMinHeight()) { - setPrefHeight(newHeight); - } else { - setPrefHeight(getMinHeight()); - } - } - } - }); - setOnMouseMoved(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - if(isInDraggableZone(event) || dragging) { - setCursor(Cursor.V_RESIZE); - } - else { - setCursor(Cursor.DEFAULT); - } - } - }); - setOnMouseReleased(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dragging = false; - setCursor(Cursor.DEFAULT); - } - }); - textArea.setOnMouseEntered(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - setCursor(Cursor.DEFAULT); - } - }); - } - - private boolean isInDraggableZone(MouseEvent event) { - return event.getY() < (GUI.RESIZE_MARGIN); - } - @Override public void println(String s) { printBuffer.append("\n" + s); diff --git a/src/jcgp/gui/dragresize/HorizontalDragResize.java b/src/jcgp/gui/dragresize/HorizontalDragResize.java new file mode 100644 index 0000000..d580878 --- /dev/null +++ b/src/jcgp/gui/dragresize/HorizontalDragResize.java @@ -0,0 +1,90 @@ +package jcgp.gui.dragresize; + +import javafx.event.EventHandler; +import javafx.scene.Cursor; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import jcgp.gui.GUI; + +/** + * + * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html + * + * @author eddy + * + */ +public class HorizontalDragResize { + + private boolean dragging = false; + private final Region region; + + private HorizontalDragResize(Region region) { + this.region = region; + } + + public static void makeDragResizable(final Region region) { + final HorizontalDragResize dr = new HorizontalDragResize(region); + + region.setOnMousePressed(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mousePressed(event); + } + }); + region.setOnMouseDragged(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseDragged(event); + } + }); + region.setOnMouseMoved(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseMoved(event); + } + }); + region.setOnMouseReleased(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseReleased(); + } + }); + + } + + private void mousePressed(MouseEvent event) { + if(isInDraggableZone(event)) { + dragging = true; + } + } + + private void mouseDragged(MouseEvent event) { + if(dragging) { + double newWidth = region.getWidth() - event.getX(); + if (newWidth >= region.getMinWidth()) { + region.setPrefWidth(newWidth); + } else { + region.setPrefWidth(region.getMinWidth()); + } + } + } + + private void mouseMoved(MouseEvent event) { + if(isInDraggableZone(event) || dragging) { + region.setCursor(Cursor.H_RESIZE); + } + else { + region.setCursor(Cursor.DEFAULT); + } + } + + private void mouseReleased() { + dragging = false; + region.setCursor(Cursor.DEFAULT); + } + + private boolean isInDraggableZone(MouseEvent event) { + return event.getX() < (GUI.RESIZE_MARGIN); + } + +} diff --git a/src/jcgp/gui/dragresize/VerticalDragResize.java b/src/jcgp/gui/dragresize/VerticalDragResize.java new file mode 100644 index 0000000..32a7526 --- /dev/null +++ b/src/jcgp/gui/dragresize/VerticalDragResize.java @@ -0,0 +1,90 @@ +package jcgp.gui.dragresize; + +import javafx.event.EventHandler; +import javafx.scene.Cursor; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; +import jcgp.gui.GUI; + +/** + * + * http://andrewtill.blogspot.co.uk/2012/12/dragging-to-resize-javafx-region.html + * + * @author eddy + * + */ +public class VerticalDragResize { + + private boolean dragging = false; + private final Region region; + + private VerticalDragResize(Region region) { + this.region = region; + } + + public static void makeDragResizable(final Region region) { + final VerticalDragResize dr = new VerticalDragResize(region); + + region.setOnMousePressed(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mousePressed(event); + } + }); + region.setOnMouseDragged(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseDragged(event); + } + }); + region.setOnMouseMoved(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseMoved(event); + } + }); + region.setOnMouseReleased(new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dr.mouseReleased(); + } + }); + + } + + private void mousePressed(MouseEvent event) { + if(isInDraggableZone(event)) { + dragging = true; + } + } + + private void mouseDragged(MouseEvent event) { + if(dragging) { + double newHeight = region.getHeight() - event.getY(); + if (newHeight >= region.getMinHeight()) { + region.setPrefHeight(newHeight); + } else { + region.setPrefHeight(region.getMinHeight()); + } + } + } + + private void mouseMoved(MouseEvent event) { + if(isInDraggableZone(event) || dragging) { + region.setCursor(Cursor.V_RESIZE); + } + else { + region.setCursor(Cursor.DEFAULT); + } + } + + private void mouseReleased() { + dragging = false; + region.setCursor(Cursor.DEFAULT); + } + + private boolean isInDraggableZone(MouseEvent event) { + return event.getY() < (GUI.RESIZE_MARGIN); + } + +} diff --git a/src/jcgp/gui/ChromosomePane.java b/src/jcgp/gui/population/ChromosomePane.java index b1f4bf0..cba58d2 100644 --- a/src/jcgp/gui/ChromosomePane.java +++ b/src/jcgp/gui/population/ChromosomePane.java @@ -1,19 +1,15 @@ -package jcgp.gui; +package jcgp.gui.population; import java.util.ArrayList; import javafx.scene.control.ScrollPane; import javafx.scene.layout.Pane; import javafx.scene.shape.Line; -import jcgp.JCGP.Resources; import jcgp.backend.population.Chromosome; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; -import jcgp.gui.population.GUIGene; -import jcgp.gui.population.GUIInput; -import jcgp.gui.population.GUINode; -import jcgp.gui.population.GUIOutput; +import jcgp.backend.resources.Resources; public class ChromosomePane extends ScrollPane { @@ -27,13 +23,13 @@ public class ChromosomePane extends ScrollPane { private ArrayList<Line> connectionLines; private ArrayList<GUIOutput> relock = new ArrayList<GUIOutput>(); - + private boolean target = false; public ChromosomePane(Chromosome chromosome, Resources resources) { super(); connectionLines = new ArrayList<Line>(); - + content = new Pane(); content.setId("content pane for genes"); @@ -96,8 +92,7 @@ public class ChromosomePane extends ScrollPane { } else { // something bad happened! throw new ClassCastException(); - } - + } } public boolean isTarget() { @@ -108,7 +103,7 @@ public class ChromosomePane extends ScrollPane { target = newValue; } - public void update() { + public void updateGenes() { for (int r = 0; r < guiNodes.length; r++) { for (int c = 0; c < guiNodes[r].length; c++) { guiNodes[r][c].updateLines(); @@ -119,7 +114,7 @@ public class ChromosomePane extends ScrollPane { guiOutputs[i].updateLines(); } } - + public void unlockOutputs() { relock.clear(); for (int i = 0; i < guiOutputs.length; i++) { diff --git a/src/jcgp/gui/population/FunctionSelector.java b/src/jcgp/gui/population/FunctionSelector.java new file mode 100644 index 0000000..0a9606f --- /dev/null +++ b/src/jcgp/gui/population/FunctionSelector.java @@ -0,0 +1,73 @@ +package jcgp.gui.population; + +import javafx.event.EventHandler; +import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.VBox; +import jcgp.backend.function.FunctionSet; +import jcgp.gui.GUI; + +public class FunctionSelector extends VBox { + + private GUINode target; + + public FunctionSelector(FunctionSet functionSet) { + setFillWidth(true); + setVisible(false); + setStyle("-fx-border-color: #A0A0A0; -fx-border-width: 1 1 0 1"); + + remakeFunctions(functionSet); + + addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + dismiss(); + } + }); + } + + public void remakeFunctions(final FunctionSet fs) { + getChildren().clear(); + + for (int i = 0; i < fs.getAllowedFunctionCount(); i++) { + final int index = i; + Label l = new Label(fs.getAllowedFunction(i).getName()); + l.setMaxWidth(Double.MAX_VALUE); + l.setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); + + l.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + ((Label) event.getSource()).setStyle("-fx-background-color: " + GUI.SOFT_HIGHLIGHT_COLOUR + "; -fx-border-color: #B0B0B0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); + } + }); + l.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + ((Label) event.getSource()).setStyle("-fx-background-color: #FFFFFF; -fx-border-color: #A0A0A0; -fx-border-width: 0 0 1 0; -fx-padding: 2"); + } + }); + l.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + target.getGene().setFunction(fs.getAllowedFunction(index)); + target.updateFunction(); + dismiss(); + } + }); + + getChildren().add(l); + } + } + + public void relocateAndShow(MouseEvent event, GUINode node) { + relocate(event.getSceneX() - 5, event.getSceneY() - 5); + target = node; + setVisible(true); + } + + private void dismiss() { + setVisible(false); + } + +} diff --git a/src/jcgp/gui/population/GUIGene.java b/src/jcgp/gui/population/GUIGene.java index 26be2a5..6e9d098 100644 --- a/src/jcgp/gui/population/GUIGene.java +++ b/src/jcgp/gui/population/GUIGene.java @@ -1,12 +1,14 @@ package jcgp.gui.population; -import javafx.beans.property.SimpleObjectProperty; +import javafx.geometry.VPos; import javafx.scene.Group; +import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; +import javafx.scene.text.Font; import javafx.scene.text.Text; +import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; import jcgp.backend.population.Gene; -import jcgp.gui.ChromosomePane; enum GUIGeneState { NEUTRAL, @@ -30,27 +32,34 @@ public abstract class GUIGene extends Group { public static final double NODE_TEXT = NODE_RADIUS / 2.5; - protected Text text; - protected Circle mainCircle; + protected Text text = new Text(); + protected Circle mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); - protected SimpleObjectProperty<GUIGeneState> stateProperty = new SimpleObjectProperty<GUIGeneState>(GUIGeneState.NEUTRAL); + private GUIGeneState state = GUIGeneState.NEUTRAL; protected ChromosomePane parent; protected int locked = 0; - - public SimpleObjectProperty<GUIGeneState> stateProperty() { - return stateProperty; + + public GUIGene() { + text.setFont(Font.font("Arial", 12)); + text.setTextOrigin(VPos.CENTER); + text.setTextAlignment(TextAlignment.CENTER); + text.setWrappingWidth(NODE_RADIUS * 2); + text.setX(-NODE_RADIUS); + text.setVisible(true); + + mainCircle.setStroke(Paint.valueOf("black")); } public void setState(GUIGeneState newState) { - stateProperty.set(newState); - } - - public void showText(boolean value) { - text.setVisible(value); + state = newState; } + public GUIGeneState getState() { + return state; + } + public boolean isLocked() { return locked > 0; } @@ -63,6 +72,11 @@ public abstract class GUIGene extends Group { public abstract void addLocks(int value); + /** + * test + * + * @param value + */ public abstract void removeLocks(int value); public abstract void updateLines(); diff --git a/src/jcgp/gui/population/GUIInput.java b/src/jcgp/gui/population/GUIInput.java index f47186c..b4eccaa 100644 --- a/src/jcgp/gui/population/GUIInput.java +++ b/src/jcgp/gui/population/GUIInput.java @@ -1,20 +1,13 @@ package jcgp.gui.population; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; -import javafx.geometry.VPos; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Output; -import jcgp.gui.ChromosomePane; import jcgp.gui.GUI; public class GUIInput extends GUIGene { @@ -22,23 +15,16 @@ public class GUIInput extends GUIGene { private Input input; public GUIInput(ChromosomePane parentRef, final Input input) { - + super(); + this.parent = parentRef; this.input = input; relocate(NODE_RADIUS, (input.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS); - mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); - mainCircle.setStroke(Paint.valueOf("black")); - - text = new Text("I: " + input.getIndex()); - text.setFont(Font.font("Arial", 12)); - text.setTextOrigin(VPos.CENTER); - text.setTextAlignment(TextAlignment.CENTER); - text.setWrappingWidth(NODE_RADIUS * 2); - text.setX(-NODE_RADIUS); - text.setVisible(true); + text.setText("I: " + input.getIndex()); + Circle outputSocket = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); outputSocket.setId(String.valueOf(0)); @@ -59,7 +45,7 @@ public class GUIInput extends GUIGene { if (event.getGestureSource() instanceof GUINode) { Connection source = ((GUINode) event.getGestureSource()).getChangingConnection(); if (input == source) { - stateProperty.set(GUIGeneState.NO_CHANGE_TARGET); + setState(GUIGeneState.NO_CHANGE_TARGET); return; } } else if (event.getGestureSource() instanceof GUIOutput) { @@ -68,7 +54,7 @@ public class GUIInput extends GUIGene { ((GUIGene) event.getSource()).setState(GUIGeneState.NO_CHANGE_TARGET); } } - stateProperty.set(GUIGeneState.TARGET); + setState(GUIGeneState.TARGET); } }); @@ -79,10 +65,10 @@ public class GUIInput extends GUIGene { // this happens even if we are the source of the drag parent.setTarget(false); if (event.isPrimaryButtonDown()) { - if (stateProperty.get() == GUIGeneState.NO_CHANGE_TARGET) { - stateProperty.set(GUIGeneState.INDIRECT_HOVER); + if (getState() == GUIGeneState.NO_CHANGE_TARGET) { + setState(GUIGeneState.INDIRECT_HOVER); } else { - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); ((GUIGene) event.getGestureSource()).setConnections(GUIGeneState.INDIRECT_HOVER); } } @@ -115,7 +101,7 @@ public class GUIInput extends GUIGene { } source.updateLines(); - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); } }); @@ -123,8 +109,8 @@ public class GUIInput extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has entered this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.NEUTRAL) { - stateProperty.set(GUIGeneState.HOVER); + if (getState() == GUIGeneState.NEUTRAL) { + setState(GUIGeneState.HOVER); } } }); @@ -133,62 +119,61 @@ public class GUIInput extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has left this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.HOVER) { - stateProperty.set(GUIGeneState.NEUTRAL); + if (getState() == GUIGeneState.HOVER) { + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } } }); + } + + @Override + public void setState(GUIGeneState newState) { + super.setState(newState); - stateProperty.addListener(new ChangeListener<GUIGeneState>() { - @Override - public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) { - - switch (newValue) { - case ACTIVE_HOVER: - if (locked > 0) { - stateProperty().set(GUIGeneState.LOCKED_HOVER); - } else { - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - } - break; - case FORBIDDEN_TARGET: - mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); - break; - case LOCKED_HOVER: - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - break; - case HOVER: - mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); - break; - case INDIRECT_HOVER: - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - break; - case NEUTRAL: - if (locked > 0) { - stateProperty.set(GUIGeneState.HOVER); - } else { - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); - } - break; - case NO_CHANGE_TARGET: - parent.setTarget(true); - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); - break; - case SOURCE: - mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); - break; - case TARGET: - parent.setTarget(true); - mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); - break; - default: - break; - - } + switch (newState) { + case ACTIVE_HOVER: + if (locked > 0) { + setState(GUIGeneState.LOCKED_HOVER); + } else { + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); } - }); - + break; + case FORBIDDEN_TARGET: + mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); + break; + case LOCKED_HOVER: + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + break; + case HOVER: + mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); + break; + case INDIRECT_HOVER: + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + break; + case NEUTRAL: + if (locked > 0) { + setState(GUIGeneState.HOVER); + } else { + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); + } + break; + case NO_CHANGE_TARGET: + parent.setTarget(true); + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); + break; + case SOURCE: + mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); + break; + case TARGET: + parent.setTarget(true); + mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); + break; + default: + break; + + } + } @Override @@ -208,13 +193,13 @@ public class GUIInput extends GUIGene { @Override public void resetState() { - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); } @Override void setLocked(boolean value) { locked += value ? 1 : -1; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); } @Override @@ -230,7 +215,7 @@ public class GUIInput extends GUIGene { @Override public void addLocks(int value) { locked += value; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); } @Override @@ -241,7 +226,7 @@ public class GUIInput extends GUIGene { @Override public void removeLocks(int value) { locked -= value; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); } @Override diff --git a/src/jcgp/gui/population/GUINode.java b/src/jcgp/gui/population/GUINode.java index 450647f..98ba738 100644 --- a/src/jcgp/gui/population/GUINode.java +++ b/src/jcgp/gui/population/GUINode.java @@ -1,22 +1,15 @@ package jcgp.gui.population; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; -import javafx.geometry.VPos; import javafx.scene.control.Label; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; -import jcgp.gui.ChromosomePane; import jcgp.gui.GUI; public class GUINode extends GUIGene { @@ -26,7 +19,8 @@ public class GUINode extends GUIGene { private int connectionIndex = 0; public GUINode(ChromosomePane parentRef, final Node node, Line[] connectionLines) { - + super(); + // store references this.parent = parentRef; this.node = node; @@ -46,21 +40,12 @@ public class GUINode extends GUIGene { Circle output = new Circle(NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); output.setStroke(Paint.valueOf("black")); - mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); - mainCircle.setStroke(Paint.valueOf("black")); - - text = new Text(node.getFunction().getName()); - text.setFont(Font.font("Arial", NODE_TEXT)); - text.setTextOrigin(VPos.CENTER); - text.setTextAlignment(TextAlignment.CENTER); - text.setWrappingWidth(NODE_RADIUS * 2); - text.setX(-NODE_RADIUS); - text.setVisible(true); + text.setText(node.getFunction().getName()); Circle[] sockets = new Circle[GUI.resources.getInt("arity")]; double angle, xPos, yPos; for (int l = 0; l < sockets.length; l++) { - angle = ((((double) (l + 1)) / ((GUI.resources.getDouble("arity") + 1))) * THETA) - (THETA / 2); + angle = (((l + 1) / ((GUI.resources.getDouble("arity") + 1))) * THETA) - (THETA / 2); xPos = -Math.cos(angle) * NODE_RADIUS; yPos = Math.sin(angle) * NODE_RADIUS; @@ -75,7 +60,7 @@ public class GUINode extends GUIGene { * Mouse event handlers on sockets * */ - s.addEventFilter(MouseDragEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() { + s.addEventFilter(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { // the mouse has been dragged out of the socket, this means a full drag is in progress @@ -101,16 +86,16 @@ public class GUINode extends GUIGene { } }); - s.addEventFilter(MouseDragEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() { + s.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { // mouse was pressed on the socket - stateProperty.set(GUIGeneState.SOURCE); + setState(GUIGeneState.SOURCE); connectionIndex = index; } }); - s.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { + s.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (!parent.isTarget()) { @@ -120,14 +105,14 @@ public class GUINode extends GUIGene { } }); - s.addEventFilter(MouseDragEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { + s.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (event.isStillSincePress()) { // mouse was released before dragging out of the socket updateLine(index); - stateProperty.set(GUIGeneState.HOVER); - } else if (stateProperty.get() == GUIGeneState.SOURCE) { + setState(GUIGeneState.HOVER); + } else if (getState() == GUIGeneState.SOURCE) { // no connection has been made, fallback resetState(); updateLines(); @@ -139,6 +124,14 @@ public class GUINode extends GUIGene { /* * Mouse event handlers on whole gene */ + + addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + GUI.functionSelector.relocateAndShow(event, (GUINode) event.getSource()); + } + }); + addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() { @Override public void handle(MouseDragEvent event) { @@ -149,12 +142,12 @@ public class GUINode extends GUIGene { Connection source = ((GUIGene) event.getGestureSource()).getChangingConnection(); if (node == source) { - stateProperty.set(GUIGeneState.NO_CHANGE_TARGET); + setState(GUIGeneState.NO_CHANGE_TARGET); } else { - stateProperty.set(GUIGeneState.TARGET); + setState(GUIGeneState.TARGET); } } else { - stateProperty.set(GUIGeneState.FORBIDDEN_TARGET); + setState(GUIGeneState.FORBIDDEN_TARGET); } } }); @@ -167,12 +160,12 @@ public class GUINode extends GUIGene { parent.setTarget(false); if (event.isPrimaryButtonDown()) { if (event.getGestureSource() == event.getSource()) { - stateProperty.set(GUIGeneState.SOURCE); + setState(GUIGeneState.SOURCE); } else { - if (stateProperty.get() == GUIGeneState.NO_CHANGE_TARGET) { - stateProperty.set(GUIGeneState.INDIRECT_HOVER); + if (getState() == GUIGeneState.NO_CHANGE_TARGET) { + setState(GUIGeneState.INDIRECT_HOVER); } else { - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); ((GUIGene) event.getGestureSource()).setConnections(GUIGeneState.INDIRECT_HOVER); } } @@ -210,7 +203,7 @@ public class GUINode extends GUIGene { } source.updateLines(); - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); } }); @@ -219,8 +212,8 @@ public class GUINode extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has entered this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.NEUTRAL) { - stateProperty.set(GUIGeneState.HOVER); + if (getState() == GUIGeneState.NEUTRAL) { + setState(GUIGeneState.HOVER); } else if (locked > 0) { setConnections(GUIGeneState.LOCKED_HOVER); } @@ -231,11 +224,11 @@ public class GUINode extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has left this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.HOVER && locked <= 0) { - stateProperty.set(GUIGeneState.NEUTRAL); + if (getState() == GUIGeneState.HOVER && locked <= 0) { + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } else if (locked > 0) { - if (stateProperty.get() == GUIGeneState.SOURCE || stateProperty.get() == GUIGeneState.FORBIDDEN_TARGET) { + if (getState() == GUIGeneState.SOURCE || getState() == GUIGeneState.FORBIDDEN_TARGET) { setConnections(GUIGeneState.INDIRECT_HOVER); } else { setConnections(GUIGeneState.HOVER); @@ -249,85 +242,72 @@ public class GUINode extends GUIGene { getChildren().addAll(sockets); getChildren().addAll(output, connectionNumber); - stateProperty.addListener(new ChangeListener<GUIGeneState>() { - @Override - public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) { + } - switch (newValue) { - case ACTIVE_HOVER: - if (locked > 0) { - stateProperty().set(GUIGeneState.LOCKED_HOVER); - } else { - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - showLines(true); - } - setConnections(GUIGeneState.ACTIVE_HOVER); - break; - case LOCKED_HOVER: - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - break; - case FORBIDDEN_TARGET: - mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); - break; - case HOVER: - mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); - showLines(true); - if (locked <= 0) { - setConnections(GUIGeneState.INDIRECT_HOVER); - } else { - setConnections(GUIGeneState.HOVER); - } - break; - case INDIRECT_HOVER: - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - break; - case NEUTRAL: - if (locked > 0) { - stateProperty.set(GUIGeneState.HOVER); - } else { - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); - showLines(false); - if (oldValue == GUIGeneState.ACTIVE_HOVER) { - setConnections(GUIGeneState.NEUTRAL); - } - } - break; - case NO_CHANGE_TARGET: - parent.setTarget(true); - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); - break; - case SOURCE: - mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR)); - break; - case TARGET: - parent.setTarget(true); - mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); - break; - default: - break; + @Override + public void setState(GUIGeneState newState) { + switch (newState) { + case ACTIVE_HOVER: + if (locked > 0) { + setState(GUIGeneState.LOCKED_HOVER); + } else { + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + showLines(true); + } + setConnections(GUIGeneState.ACTIVE_HOVER); + break; + case LOCKED_HOVER: + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + break; + case FORBIDDEN_TARGET: + mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); + break; + case HOVER: + mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); + showLines(true); + if (locked <= 0) { + setConnections(GUIGeneState.INDIRECT_HOVER); + } else { + setConnections(GUIGeneState.HOVER); + } + break; + case INDIRECT_HOVER: + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + break; + case NEUTRAL: + if (locked > 0) { + setState(GUIGeneState.HOVER); + } else { + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); + showLines(false); + if (getState() == GUIGeneState.ACTIVE_HOVER) { + setConnections(GUIGeneState.NEUTRAL); } } - }); - -// for (int c = 0; c < lines.length; c++) { -// final int i = c; -// node.connections().get(c).addListener(new ChangeListener<Connection>() { -// @Override -// public void changed(ObservableValue<? extends Connection> observable, -// Connection oldValue, Connection newValue) { -// updateLine(i); -// } -// }); -// } + break; + case NO_CHANGE_TARGET: + parent.setTarget(true); + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); + break; + case SOURCE: + mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR)); + break; + case TARGET: + parent.setTarget(true); + mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); + break; + default: + break; + } + super.setState(newState); } - + @Override public Connection getChangingConnection() { return node.getConnection(connectionIndex); } - private boolean isAllowed(GUIGene source, GUIGene target) { if (source instanceof GUINode) { // if the source is a node, all inputs and some nodes are valid @@ -381,6 +361,7 @@ public class GUINode extends GUIGene { /** * Updates the end of all lines to match the associated connections. */ + @Override public void updateLines() { for (int c = 0; c < lines.length; c++) { updateLine(c); @@ -420,9 +401,9 @@ public class GUINode extends GUIGene { @Override public void resetState() { if (locked > 0) { - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); } else { - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } @@ -431,7 +412,7 @@ public class GUINode extends GUIGene { @Override void setLocked(boolean value) { locked += value ? 1 : -1; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); for (int i = 0; i < lines.length; i++) { parent.getGuiGene(node.getConnection(i)).setLocked(value); @@ -441,7 +422,7 @@ public class GUINode extends GUIGene { @Override public void addLocks(int value) { locked += value; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); for (int i = 0; i < lines.length; i++) { parent.getGuiGene(node.getConnection(i)).addLocks(value); @@ -451,7 +432,7 @@ public class GUINode extends GUIGene { @Override public void removeLocks(int value) { locked -= value; - stateProperty.set(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); + setState(locked > 0 ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); for (int i = 0; i < lines.length; i++) { parent.getGuiGene(node.getConnection(i)).removeLocks(value); diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index 9c5dfa5..4a12385 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -1,23 +1,16 @@ package jcgp.gui.population; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; -import javafx.geometry.VPos; import javafx.scene.control.Label; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; -import javafx.scene.text.Font; -import javafx.scene.text.Text; -import javafx.scene.text.TextAlignment; import jcgp.backend.population.Connection; import jcgp.backend.population.Input; import jcgp.backend.population.Node; import jcgp.backend.population.Output; -import jcgp.gui.ChromosomePane; import jcgp.gui.GUI; public class GUIOutput extends GUIGene { @@ -27,7 +20,8 @@ public class GUIOutput extends GUIGene { private Output output; public GUIOutput(ChromosomePane parentRef, final Output output, Line line) { - + super(); + this.parent = parentRef; this.output = output; this.sourceLine = line; @@ -38,16 +32,7 @@ public class GUIOutput extends GUIGene { // set the line ends correctly updateLines(); - mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); - mainCircle.setStroke(Paint.valueOf("black")); - - text = new Text("O: " + output.getIndex()); - text.setFont(Font.font("Arial", 12)); - text.setTextOrigin(VPos.CENTER); - text.setTextAlignment(TextAlignment.CENTER); - text.setWrappingWidth(NODE_RADIUS * 2); - text.setX(-NODE_RADIUS); - text.setVisible(true); + text.setText("O: " + output.getIndex()); Circle socket = new Circle(-NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); socket.setId(String.valueOf(0)); @@ -62,7 +47,7 @@ public class GUIOutput extends GUIGene { * Mouse event handlers on sockets * */ - socket.addEventFilter(MouseDragEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() { + socket.addEventFilter(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { // the mouse has been dragged out of the socket, this means a full drag is in progress @@ -86,15 +71,15 @@ public class GUIOutput extends GUIGene { } }); - socket.addEventFilter(MouseDragEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() { + socket.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { // mouse was pressed on the socket - stateProperty.set(GUIGeneState.SOURCE); + setState(GUIGeneState.SOURCE); } }); - socket.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { + socket.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (!parent.isTarget()) { @@ -105,14 +90,14 @@ public class GUIOutput extends GUIGene { } }); - socket.addEventFilter(MouseDragEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { + socket.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (event.isStillSincePress()) { // mouse was released before dragging out of the socket updateLines(); - stateProperty.set(GUIGeneState.HOVER); - } else if (stateProperty.get() == GUIGeneState.SOURCE) { + setState(GUIGeneState.HOVER); + } else if (getState() == GUIGeneState.SOURCE) { // no connection has been made, fallback resetState(); updateLines(); @@ -129,7 +114,7 @@ public class GUIOutput extends GUIGene { @Override public void handle(MouseDragEvent event) { // the drag has entered this node, react appropriately - stateProperty.set(GUIGeneState.FORBIDDEN_TARGET); + setState(GUIGeneState.FORBIDDEN_TARGET); } }); @@ -140,9 +125,9 @@ public class GUIOutput extends GUIGene { // this happens even if we are the source of the drag if (event.isPrimaryButtonDown()) { if (event.getGestureSource() == event.getSource()) { - stateProperty.set(GUIGeneState.SOURCE); + setState(GUIGeneState.SOURCE); } else { - stateProperty.set(isLocked() ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); + setState(isLocked() ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); } } } @@ -164,7 +149,7 @@ public class GUIOutput extends GUIGene { } source.updateLines(); - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); } }); @@ -173,8 +158,8 @@ public class GUIOutput extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has entered this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.NEUTRAL) { - stateProperty.set(GUIGeneState.HOVER); + if (getState() == GUIGeneState.NEUTRAL) { + setState(GUIGeneState.HOVER); } } }); @@ -190,8 +175,8 @@ public class GUIOutput extends GUIGene { @Override public void handle(MouseEvent event) { // cursor has left this node without dragging, or it is dragging and this is the source - if (stateProperty.get() == GUIGeneState.HOVER && !isLocked()) { - stateProperty.set(GUIGeneState.NEUTRAL); + if (getState() == GUIGeneState.HOVER && !isLocked()) { + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } } @@ -199,52 +184,49 @@ public class GUIOutput extends GUIGene { getChildren().addAll(mainCircle, text, socket, connectionLabel); + + } - stateProperty.addListener(new ChangeListener<GUIGeneState>() { - @Override - public void changed(ObservableValue<? extends GUIGeneState> observable, GUIGeneState oldValue, GUIGeneState newValue) { - - - switch (newValue) { - case ACTIVE_HOVER: - break; - case FORBIDDEN_TARGET: - mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); - break; - case HOVER: - mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); - sourceLine.setVisible(true); - if (!isLocked()) { - setConnections(GUIGeneState.ACTIVE_HOVER); - } - break; - case INDIRECT_HOVER: - mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); - break; - case NEUTRAL: - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); - sourceLine.setVisible(false); - break; - case NO_CHANGE_TARGET: - mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); - break; - case SOURCE: - mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR)); - setConnections(GUIGeneState.NEUTRAL); - setConnections(GUIGeneState.INDIRECT_HOVER); - break; - case TARGET: - mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); - break; - default: - break; - - } + @Override + public void setState(GUIGeneState newState) { + super.setState(newState); + + switch (newState) { + case ACTIVE_HOVER: + break; + case FORBIDDEN_TARGET: + mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); + break; + case HOVER: + mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); + sourceLine.setVisible(true); + if (!isLocked()) { + setConnections(GUIGeneState.ACTIVE_HOVER); } - }); - + break; + case INDIRECT_HOVER: + mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); + break; + case NEUTRAL: + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); + sourceLine.setVisible(false); + break; + case NO_CHANGE_TARGET: + mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); + break; + case SOURCE: + mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR)); + setConnections(GUIGeneState.NEUTRAL); + setConnections(GUIGeneState.INDIRECT_HOVER); + break; + case TARGET: + mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); + break; + default: + break; + } } - + @Override public void updateLines() { if (output.getSource() instanceof Node) { @@ -272,10 +254,10 @@ public class GUIOutput extends GUIGene { @Override public void resetState() { if (locked > 0) { - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); setConnections(GUIGeneState.HOVER); } else { - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } } @@ -318,14 +300,14 @@ public class GUIOutput extends GUIGene { public void unlock() { if (isLocked()) { setLocked(false); - stateProperty.set(GUIGeneState.NEUTRAL); + setState(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } } public void lock() { if (!isLocked()) { - stateProperty.set(GUIGeneState.HOVER); + setState(GUIGeneState.HOVER); setLocked(true); } } diff --git a/src/jcgp/gui/population/PopulationPane.java b/src/jcgp/gui/population/PopulationPane.java new file mode 100644 index 0000000..3a83a86 --- /dev/null +++ b/src/jcgp/gui/population/PopulationPane.java @@ -0,0 +1,49 @@ +package jcgp.gui.population; + +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; +import jcgp.JCGP; +import jcgp.backend.population.Population; +import jcgp.backend.resources.Resources; + +public class PopulationPane extends TabPane { + + public PopulationPane(JCGP jcgp) { + super(); + + setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); + + remakeTabs(jcgp.getPopulation(), jcgp.getResources()); + } + + public void remakeTabs(Population population, Resources resources) { + getTabs().clear(); + + Tab tab; + ChromosomePane cp; + for (int i = 0; i < resources.getInt("popSize"); i++) { + cp = new ChromosomePane(population.getChromosome(i), resources); + tab = new Tab("Chr " + i); + tab.setContent(cp); + getTabs().add(tab); + } + } + + public void updateGenes() { + for (int i = 0; i < getChildrenUnmodifiable().size(); i++) { + ((ChromosomePane) getTabs().get(i).getContent()).updateGenes(); + } + } + + public void unlockOutputs() { + for (int i = 0; i < getChildrenUnmodifiable().size(); i++) { + ((ChromosomePane) getTabs().get(i).getContent()).unlockOutputs(); + } + } + + public void relockOutputs() { + for (int i = 0; i < getChildrenUnmodifiable().size(); i++) { + ((ChromosomePane) getTabs().get(i).getContent()).relockOutputs(); + } + } +} diff --git a/src/jcgp/gui/settings/SettingsPane.java b/src/jcgp/gui/settings/SettingsPane.java index cb066b7..8c707d3 100644 --- a/src/jcgp/gui/settings/SettingsPane.java +++ b/src/jcgp/gui/settings/SettingsPane.java @@ -5,12 +5,10 @@ import java.util.ArrayList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; -import javafx.scene.Cursor; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.ScrollPane; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -23,26 +21,21 @@ import jcgp.backend.function.FunctionSet; import jcgp.backend.modules.ea.EvolutionaryAlgorithm; import jcgp.backend.modules.fitness.FitnessFunction; import jcgp.backend.modules.mutator.Mutator; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; +import jcgp.backend.resources.parameters.Parameter; import jcgp.gui.GUI; -import jcgp.gui.settings.parameters.*; +import jcgp.gui.settings.parameters.GUIParameter; public class SettingsPane extends AnchorPane { private VBox mainContainer; - private VBox bpPane, eaPane, mutatorPane, ffPane, nfPane; + private VBox baseParameterPane, eaPane, mutatorPane, ffPane, nfPane; - private Button runPause; + private Button runPause = new Button("Run"), step = new Button("Step"), reset = new Button("Reset"); private ArrayList<GUIParameter> parameters = new ArrayList<GUIParameter>(); - private boolean dragging = false; - public SettingsPane(JCGP cgp, GUI gui) { super(); - - setResizeListeners(); mainContainer = new VBox(8); mainContainer.setPadding(new Insets(5, GUI.RESIZE_MARGIN, 0, 2)); @@ -50,15 +43,13 @@ public class SettingsPane extends AnchorPane { setMinWidth(GUI.SETTINGS_WIDTH); setPrefWidth(GUI.SETTINGS_WIDTH); - initialiseBaseParameters(cgp, gui); + initialiseBaseParameters(cgp); initialiseEAParameters(cgp); initialiseMutatorParameters(cgp); - initialiseFitnessFunctionParameters(cgp); - - initialiseNodeFunctionParameters(cgp); + initialiseProblemTypeParameters(cgp, gui); createControls(cgp, gui); @@ -75,35 +66,39 @@ public class SettingsPane extends AnchorPane { getChildren().add(scroll); } - private void initialiseBaseParameters(JCGP cgp, GUI gui) { - bpPane = new VBox(2); + private void initialiseBaseParameters(JCGP cgp) { + baseParameterPane = new VBox(2); Text header = new Text("Base Parameters"); header.setFont(Font.font("Arial", 14)); header.setUnderline(true); - bpPane.getChildren().add(header); - - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("rows"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("columns"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("inputs"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("outputs"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("levelsBack"), this)); - - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("popSize"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("generations"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("runs"), this)); - - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("currentGen"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("currentRun"), this)); - - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("maxFitness"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("seed"), this)); - parameters.add(GUIParameter.create((IntegerParameter) cgp.getResources().getParameter("report"), this)); - - bpPane.getChildren().addAll(parameters); - - mainContainer.getChildren().add(bpPane); + baseParameterPane.getChildren().add(header); + + parameters.add(GUIParameter.create(cgp.getResources().getParameter("rows"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("columns"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("inputs"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("outputs"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("levelsBack"), this)); + + GUIParameter gp = GUIParameter.create(cgp.getResources().getParameter("popSize"), this); + gp.setPadding(new Insets(0, 0, 10, 0)); + parameters.add(gp); + + parameters.add(GUIParameter.create(cgp.getResources().getParameter("currentGen"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("generations"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("currentRun"), this)); + + gp = GUIParameter.create(cgp.getResources().getParameter("runs"), this); + gp.setPadding(new Insets(0, 0, 10, 0)); + parameters.add(gp); + + parameters.add(GUIParameter.create(cgp.getResources().getParameter("seed"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("report"), this)); + parameters.add(GUIParameter.create(cgp.getResources().getParameter("maxFitness"), this)); + + baseParameterPane.getChildren().addAll(parameters); + mainContainer.getChildren().add(baseParameterPane); } private void initialiseEAParameters(final JCGP cgp) { @@ -172,10 +167,10 @@ public class SettingsPane extends AnchorPane { mainContainer.getChildren().add(mutatorPane); } - private void initialiseFitnessFunctionParameters(final JCGP cgp) { + private void initialiseProblemTypeParameters(final JCGP cgp, final GUI gui) { ffPane= new VBox(2); - Text header = new Text("Fitness Function"); + Text header = new Text("Problem type"); header.setFont(Font.font("Arial", 14)); header.setUnderline(true); @@ -200,36 +195,13 @@ public class SettingsPane extends AnchorPane { } }); - ffPane.getChildren().addAll(header, ffCBox, ffParameters); - mainContainer.getChildren().add(ffPane); - - } - - private void initialiseNodeFunctionParameters(JCGP cgp) { - nfPane = new VBox(2); - - Text header = new Text("Node Functions"); - header.setFont(Font.font("Arial", 14)); - header.setUnderline(true); - - final ComboBox<FunctionSet> nfCBox = new ComboBox<FunctionSet>(); - nfCBox.getItems().addAll(cgp.getResources().getFunctionSets()); - nfCBox.getSelectionModel().select(cgp.getResources().getFunctionSet()); - nfCBox.prefWidthProperty().bind(mainContainer.widthProperty()); - final VBox nfParameters = new VBox(); nfParameters.setSpacing(2); - refreshFunctions(cgp.getResources().getFunctionSet(), nfParameters); + refreshFunctions(cgp.getResources().getFunctionSet(), nfParameters, gui); - nfCBox.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent event) { - refreshFunctions(nfCBox.getSelectionModel().getSelectedItem(), nfParameters); - } - }); + ffPane.getChildren().addAll(header, ffCBox, ffParameters, nfParameters); + mainContainer.getChildren().add(ffPane); - nfPane.getChildren().addAll(header, nfCBox, nfParameters); - mainContainer.getChildren().add(nfPane); } private void createControls(final JCGP cgp, final GUI gui) { @@ -237,7 +209,6 @@ public class SettingsPane extends AnchorPane { controls.setFillWidth(true); final HBox flowButtons = new HBox(2); - runPause = new Button("Run"); runPause.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { @@ -245,7 +216,7 @@ public class SettingsPane extends AnchorPane { } }); - Button step = new Button("Step"); + step = new Button("Step"); step.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { @@ -253,7 +224,7 @@ public class SettingsPane extends AnchorPane { } }); - Button reset = new Button("Reset"); + reset = new Button("Reset"); reset.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { @@ -265,10 +236,8 @@ public class SettingsPane extends AnchorPane { HBox.setHgrow(runPause, Priority.ALWAYS); runPause.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(step, Priority.ALWAYS); step.setMaxWidth(Double.MAX_VALUE); - HBox.setHgrow(reset, Priority.ALWAYS); reset.setMaxWidth(Double.MAX_VALUE); @@ -293,7 +262,7 @@ public class SettingsPane extends AnchorPane { revalidateParameters(); } - private void refreshFunctions(final FunctionSet fs, VBox vb) { + private void refreshFunctions(final FunctionSet fs, VBox vb, final GUI gui) { vb.getChildren().clear(); CheckBox cb; for (int i = 0; i < fs.getTotalFunctionCount(); i++) { @@ -309,69 +278,30 @@ public class SettingsPane extends AnchorPane { } else { fs.disableFunction(index); } + GUI.updateFunctionSelector(); } }); vb.getChildren().add(cb); + + GUI.updateFunctionSelector(); } } - private void setResizeListeners() { - setOnMousePressed(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - if(isInDraggableZone(event)) { - dragging = true; - } - } - }); - setOnMouseDragged(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - if(dragging) { - double newWidth = getWidth() - event.getX(); - if (newWidth >= getMinWidth()) { - setPrefWidth(newWidth); - } else { - setPrefWidth(getMinWidth()); - } - } - } - }); - setOnMouseMoved(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - if(isInDraggableZone(event) || dragging) { - setCursor(Cursor.H_RESIZE); - } - else { - setCursor(Cursor.DEFAULT); - } - } - }); - setOnMouseReleased(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent event) { - dragging = false; - setCursor(Cursor.DEFAULT); - } - }); - } - - private boolean isInDraggableZone(MouseEvent event) { - return event.getX() < (GUI.RESIZE_MARGIN); - } + - public void disableSettings(boolean value) { - bpPane.setDisable(value); + public void runningMode(boolean value) { + baseParameterPane.setDisable(value); eaPane.setDisable(value); mutatorPane.setDisable(value); ffPane.setDisable(value); nfPane.setDisable(value); + step.setDisable(value); + reset.setDisable(value); + + runPause.setText(value ? "Pause" : "Run"); } /** - * Writes the GUI parameter value back to the resources parameter, - * if any changes were made. * * @return true if the experiment needs to be reset, false otherwise. */ @@ -394,8 +324,12 @@ public class SettingsPane extends AnchorPane { } public void revalidateParameters() { + runPause.setDisable(false); for (GUIParameter parameter : parameters) { parameter.validate(); + if (parameter.requiresReset()) { + runPause.setDisable(true); + } } } @@ -404,10 +338,4 @@ public class SettingsPane extends AnchorPane { parameter.applyValue(); } } - - public Button getRunButton() { - return runPause; - } - - } diff --git a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java index fc66e7d..4339562 100644 --- a/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIBooleanParameter.java @@ -3,9 +3,8 @@ package jcgp.gui.settings.parameters; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.control.CheckBox; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.GUI; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; public class GUIBooleanParameter extends GUIParameter { @@ -65,15 +64,15 @@ public class GUIBooleanParameter extends GUIParameter { */ private void setValidityStyle() { if (parameter.getStatus() == ParameterStatus.INVALID) { - valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_CHECKBOX_STYLE + INVALID_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setStyle(BASE_CHECKBOX_STYLE + WARNING_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else { - valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_CHECKBOX_STYLE + VALID_PARAMETER_STYLE); valueControl.setTooltip(null); } } diff --git a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java index 190f4b7..eecff2d 100644 --- a/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIDoubleParameter.java @@ -8,9 +8,8 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.text.Text; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.GUI; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; public class GUIDoubleParameter extends GUIParameter { @@ -26,13 +25,14 @@ public class GUIDoubleParameter extends GUIParameter { name = new Text(parameter.getName()); valueControl = new TextField(String.valueOf(parameter.get())); - valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setStyle(VALID_PARAMETER_STYLE); ((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT); setHgrow(valueControl, Priority.ALWAYS); + setHgrow(name, Priority.ALWAYS); - name.setWrappingWidth(GUI.WRAP_WIDTH); + name.wrappingWidthProperty().bind(widthProperty().divide(2)); ((TextField) valueControl).setEditable(!parameter.isMonitor()); @@ -41,6 +41,7 @@ public class GUIDoubleParameter extends GUIParameter { makeLightBinding(); } else { valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() { + @Override public void handle( KeyEvent t ) { char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; if (!((ch >= '0' && ch <= '9') || (ch == '.' && !((TextField) valueControl).getText().contains(".")))) { @@ -101,15 +102,15 @@ public class GUIDoubleParameter extends GUIParameter { private void setValidityStyle() { if (parameter.getStatus() == ParameterStatus.INVALID) { - valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else { - valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE); valueControl.setTooltip(null); } } diff --git a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java index a2040d7..9c84c6a 100644 --- a/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIIntegerParameter.java @@ -8,9 +8,8 @@ import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.text.Text; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.ParameterStatus; -import jcgp.gui.GUI; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.ParameterStatus; import jcgp.gui.settings.SettingsPane; public class GUIIntegerParameter extends GUIParameter { @@ -26,13 +25,14 @@ public class GUIIntegerParameter extends GUIParameter { name = new Text(parameter.getName()); valueControl = new TextField(String.valueOf(parameter.get())); - valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setStyle(VALID_PARAMETER_STYLE); ((TextField) valueControl).setAlignment(Pos.CENTER_RIGHT); setHgrow(valueControl, Priority.ALWAYS); + setHgrow(name, Priority.ALWAYS); - name.setWrappingWidth(GUI.WRAP_WIDTH); + name.wrappingWidthProperty().bind(widthProperty().divide(2)); ((TextField) valueControl).setEditable(!parameter.isMonitor()); @@ -41,6 +41,7 @@ public class GUIIntegerParameter extends GUIParameter { makeLightBinding(); } else { valueControl.addEventFilter(KeyEvent.KEY_TYPED, new EventHandler<KeyEvent>() { + @Override public void handle( KeyEvent t ) { char ch = t.getCharacter().toCharArray()[t.getCharacter().toCharArray().length - 1]; if (!(ch >= '0' && ch <= '9')) { @@ -101,15 +102,15 @@ public class GUIIntegerParameter extends GUIParameter { private void setValidityStyle() { if (parameter.getStatus() == ParameterStatus.INVALID) { - valueControl.setStyle(GUI.INVALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + INVALID_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else if (parameter.getStatus() == ParameterStatus.WARNING || parameter.getStatus() == ParameterStatus.WARNING_RESET) { - valueControl.setStyle(GUI.WARNING_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + WARNING_PARAMETER_STYLE); valueControl.setTooltip(tooltip); tooltip.setText(parameter.getStatus().getDetails()); } else { - valueControl.setStyle(GUI.VALID_PARAMETER_STYLE); + valueControl.setStyle(BASE_TEXT_STYLE + VALID_PARAMETER_STYLE); valueControl.setTooltip(null); } } diff --git a/src/jcgp/gui/settings/parameters/GUIParameter.java b/src/jcgp/gui/settings/parameters/GUIParameter.java index da2fe86..6bfdf30 100644 --- a/src/jcgp/gui/settings/parameters/GUIParameter.java +++ b/src/jcgp/gui/settings/parameters/GUIParameter.java @@ -10,14 +10,22 @@ import javafx.scene.control.Control; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.text.Text; -import jcgp.backend.parameters.BooleanParameter; -import jcgp.backend.parameters.DoubleParameter; -import jcgp.backend.parameters.IntegerParameter; -import jcgp.backend.parameters.Parameter; -import jcgp.backend.parameters.ParameterStatus; +import jcgp.backend.resources.parameters.BooleanParameter; +import jcgp.backend.resources.parameters.DoubleParameter; +import jcgp.backend.resources.parameters.IntegerParameter; +import jcgp.backend.resources.parameters.Parameter; +import jcgp.backend.resources.parameters.ParameterStatus; +import jcgp.gui.GUI; import jcgp.gui.settings.SettingsPane; public abstract class GUIParameter extends HBox { + + public static final String BASE_TEXT_STYLE = "-fx-border-color: #C9C9C9; -fx-border-radius: 2; -fx-padding: 0; "; + public static final String BASE_CHECKBOX_STYLE = "-fx-padding: 0; "; + + public static final String INVALID_PARAMETER_STYLE = "-fx-background-color: " + GUI.BAD_SELECTION_COLOUR; + public static final String WARNING_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_SELECTION_COLOUR; + public static final String VALID_PARAMETER_STYLE = "-fx-background-color: " + GUI.NEUTRAL_COLOUR; protected Parameter parameter; protected Text name; |