/* * Copyright 2007-2013 * Licensed under GNU Lesser General Public License * * This file is part of EpochX: genetic programming software for research * * EpochX is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EpochX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with EpochX. If not, see <http://www.gnu.org/licenses/>. * * The latest version is available from: http://www.epochx.org */ package org.epochx.cfg.benchmark.ant; import static org.epochx.Config.Template.TEMPLATE; import java.awt.Point; import java.util.ArrayList; import java.util.List; import org.epochx.Config; import org.epochx.Config.ConfigKey; import org.epochx.Individual; import org.epochx.cfg.CFGIndividual; import org.epochx.cfg.fitness.CFGFitnessFunction; import org.epochx.event.ConfigEvent; import org.epochx.event.EventManager; import org.epochx.event.Listener; import org.epochx.fitness.DoubleFitness; import org.epochx.interpret.Interpreter; import org.epochx.interpret.MalformedProgramException; import org.epochx.tools.ant.Ant; import org.epochx.tools.ant.AntLandscape; /** * A fitness function for <code>GEIndividual</code>s that calculates and assigns * <code>DoubleFitness.Minimise</code> scores. The program is executed using an interpreter * until the maximum number of timesteps are used. The fitness score returned is the number * of food items that are <b>not</b> consumed by the controlled ant. * * When using this fitness function the {@link #FOOD_LOCATIONS} and * {@link #MAXIMUM_TIMESTEPS} config options must be set, or the same values set using the * mutator methods provided. * * @since 2.0 */ public class FoodLocationCount extends CFGFitnessFunction implements Listener<ConfigEvent> { /** * The key for setting the food locations in the landscape */ public static final ConfigKey<List<Point>> FOOD_LOCATIONS = new ConfigKey<List<Point>>(); /** * The key for setting the maximum number of timesteps allowed for an ant */ public static final ConfigKey<Integer> MAXIMUM_TIMESTEPS = new ConfigKey<Integer>(); // The ant environment private AntLandscape landscape; private Ant ant; // Configuration settings private Interpreter<CFGIndividual> interpreter; private List<Point> foodLocations; private Integer timesteps; private Double malformedPenalty; /** * Constructs a <code>AntFitnessFunction</code> fitness function with control parameters * automatically loaded from the config. * * @param ant the ant that is being controlled * @param landscape the ant landscape that the ant is navigating */ public FoodLocationCount(Ant ant, AntLandscape landscape) { this(ant, landscape, true); } /** * Constructs a <code>AntFitnessFunction</code> fitness function with control parameters * initially loaded from the config. If the <code>autoConfig</code> argument is set to * <code>true</code> then the configuration will be automatically updated when the config * is modified. * * @param ant the ant that is being controlled * @param landscape the ant landscape that the ant is navigating * @param autoConfig whether this fitness function should automatically update its * configuration settings from the config */ public FoodLocationCount(Ant ant, AntLandscape landscape, boolean autoConfig) { this.ant = ant; this.landscape = landscape; // Default config values malformedPenalty = Double.MAX_VALUE; setup(); if (autoConfig) { EventManager.getInstance().add(ConfigEvent.class, this); } } /** * Sets up this operator with the appropriate configuration settings. * This method is called whenever a <code>ConfigEvent</code> occurs for a * change in any of the following configuration parameters: * <ul> * <li>{@link #FOOD_LOCATIONS} * <li>{@link #MAXIMUM_TIMESTEPS} * <li>{@link CFGFitnessFunction#INTERPRETER} * <li>{@link CFGFitnessFunction#MALFORMED_PENALTY} * </ul> */ protected void setup() { foodLocations = Config.getInstance().get(FOOD_LOCATIONS); timesteps = Config.getInstance().get(MAXIMUM_TIMESTEPS); interpreter = Config.getInstance().get(INTERPRETER); malformedPenalty = Config.getInstance().get(MALFORMED_PENALTY, malformedPenalty); } /** * Receives configuration events and triggers this fitness function to * configure its parameters if the <code>ConfigEvent</code> is for one of * its required parameters. * * @param event {@inheritDoc} */ @Override public void onEvent(ConfigEvent event) { if (event.isKindOf(TEMPLATE, FOOD_LOCATIONS, MAXIMUM_TIMESTEPS, INTERPRETER, MALFORMED_PENALTY)) { setup(); } } /** * Calculates the fitness of the given individual. This fitness function only operates * on <code>GEIndividual</code>s. The fitness returned will be an instance of * <code>DoubleFitness.Minimise</code>. The fitness score is a count of the number of * food items left in the landscape after the given individual is executed for the number * of timesteps specified by the {@link #MAXIMUM_TIMESTEPS} config key. * * @param individual the program to evaluate * @return the fitness of the given individual * @throws IllegalArgumentException if the individual is not a GEIndividual */ @Override public DoubleFitness.Minimise evaluate(Individual individual) { if (!(individual instanceof CFGIndividual)) { throw new IllegalArgumentException("Unsupported representation"); } CFGIndividual program = (CFGIndividual) individual; landscape.setFoodLocations(new ArrayList<Point>(foodLocations)); ant.reset(timesteps, landscape); try { while (ant.getTimesteps() < ant.getMaxMoves()) { interpreter.exec(program, new String[]{}, new Object[][]{}); } } catch (MalformedProgramException e) { return new DoubleFitness.Minimise(malformedPenalty); } return new DoubleFitness.Minimise(foodLocations.size() - ant.getFoodEaten()); } /** * Returns the location of the food items in the ant landscape * * @return the location of the food items */ public List<Point> getFoodLocations() { return foodLocations; } /** * Sets the location of food items in the ant landscape. * * If automatic configuration is enabled then any value set here will be * overwritten by the {@link #FOOD_LOCATIONS} configuration setting on the * next config event. * * @param foodLocations the location of food items in the ant landscape */ public void setFoodLocations(List<Point> foodLocations) { this.foodLocations = foodLocations; } /** * Returns the maximum number of timesteps the ant will be allowed * * @return the maximum number of timesteps */ public int getMaximumTimesteps() { return timesteps; } /** * Sets the maximum number of timesteps the ant will be allowed. * * If automatic configuration is enabled then any value set here will be * overwritten by the {@link #MAXIMUM_TIMESTEPS} configuration setting on the * next config event. * * @param timesteps the maximum number of timesteps */ public void setMaximumTimesteps(int timesteps) { this.timesteps = timesteps; } /** * Returns the fitness score that will be assigned to individuals that are * malformed * * @return the penalty to be assigned to malformed individuals */ public Double getMalformedProgramPenalty() { return malformedPenalty; } /** * Sets the fitness score to be assigned to individuals that are malformed. * * If automatic configuration is enabled then any value set here will be * overwritten by the {@link #MALFORMED_PENALTY} configuration setting on the * next config event. * * @param malformedPenalty the penalty to be assigned to malformed individuals */ public void setMalformedProgramPenalty(Double malformedPenalty) { this.malformedPenalty = malformedPenalty; } /** * Returns the interpreter being used to execute individuals * * @return the interpreter in use */ public Interpreter<CFGIndividual> getInterpreter() { return interpreter; } /** * Sets the interpreter to use to execute individuals * * @param interpreter the interpreter to execute individuals with */ public void setInterpreter(Interpreter<CFGIndividual> interpreter) { this.interpreter = interpreter; } }