/*
* Copyright (C) 2010 Markus Echterhoff <tam@edu.uni-klu.ac.at>
*
* This file is part of EvoPaint.
*
* EvoPaint is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EvoPaint. If not, see <http://www.gnu.org/licenses/>.
*/
package evopaint.pixel.rulebased;
import evopaint.pixel.rulebased.targeting.IHaveTarget;
import evopaint.Configuration;
import evopaint.gui.util.AutoSelectOnFocusSpinner;
import evopaint.interfaces.IRandomNumberGenerator;
import evopaint.pixel.rulebased.actions.AssimilationAction;
import evopaint.pixel.rulebased.actions.ChangeEnergyAction;
import evopaint.pixel.rulebased.actions.CopyAction;
import evopaint.pixel.rulebased.actions.MoveAction;
import evopaint.pixel.rulebased.actions.PartnerProcreationAction;
import evopaint.pixel.rulebased.actions.SetColorAction;
import evopaint.pixel.rulebased.targeting.ActionMetaTarget;
import evopaint.pixel.rulebased.targeting.ActionSingleTarget;
import evopaint.pixel.rulebased.targeting.IActionTarget;
import evopaint.pixel.rulebased.targeting.ITarget;
import evopaint.pixel.rulebased.targeting.Target;
import evopaint.util.mapping.RelativeCoordinate;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
*
* @author Markus Echterhoff <tam@edu.uni-klu.ac.at>
*/
public abstract class Action implements IHaveTarget {
protected final static int ASSIMILATION = 0;
protected final static int CHANGE_ENERGY = 1;
protected final static int COPY = 2;
protected final static int MOVE = 3;
protected final static int PARTNER_PROCREATION = 4;
protected final static int SET_COLOR = 5;
private static final int NUM_ACTIONS = 6;
protected int energyChange;
private IActionTarget target;
protected Action(int energyChange, ActionMetaTarget target) {
this.energyChange = energyChange;
this.target = target;
}
protected Action() {
this.target = new ActionSingleTarget();
}
protected Action(Action action) {
this.energyChange = action.energyChange;
this.target = action.target;
}
public static Action copy(Action action) {
int type = action.getType();
switch (type) {
case Action.ASSIMILATION:
return new AssimilationAction(
(AssimilationAction)action);
case Action.CHANGE_ENERGY:
return new ChangeEnergyAction(
(ChangeEnergyAction)action);
case Action.COPY:
return new CopyAction(
(CopyAction)action);
case Action.MOVE:
return new MoveAction(
(MoveAction)action);
case Action.PARTNER_PROCREATION:
return new PartnerProcreationAction(
(PartnerProcreationAction)action);
case Action.SET_COLOR:
return new SetColorAction(
(SetColorAction)action);
default: assert (false);
return null;
}
}
public abstract int getType();
public int countGenes() {
return target.countGenes(); // energy change is not mutable, so not counted
}
public void mutate(int mutatedGene, IRandomNumberGenerator rng) {
int targetType = target.getType();
switch (targetType) {
case Target.META_TARGET: target = new ActionMetaTarget((ActionMetaTarget)target);
break;
case Target.SINGLE_TARGET: target = new ActionSingleTarget((ActionSingleTarget)target);
break;
default: assert (false);
}
target.mutate(mutatedGene, rng);
}
public void mixWith(Action theirAction, float theirShare, IRandomNumberGenerator rng) {
if (target.getType() == theirAction.target.getType()) {
Target newTarget = null;
int targetType = target.getType();
switch (targetType) {
case Target.META_TARGET: newTarget = new ActionMetaTarget((ActionMetaTarget)target);
break;
case Target.SINGLE_TARGET: newTarget = new ActionSingleTarget((ActionSingleTarget)target);
break;
default: assert (false);
}
newTarget.mixWith((Target)theirAction.target, theirShare, rng);
target = (IActionTarget)newTarget;
} else {
if (rng.nextFloat() < theirShare) {
target = theirAction.target;
}
}
}
public int getEnergyChange() {
return energyChange;
}
public void setEnergyChange(int energyChange) {
this.energyChange = energyChange;
}
public IActionTarget getTarget() {
return target;
}
public void setTarget(ITarget target) {
this.target = (IActionTarget)target;
}
@Override
public String toString() {
String ret = new String();
ret += getName();
ret += " (";
Map<String, String> parametersMap = addParametersString(new LinkedHashMap<String, String>());
for (Iterator<String> ii = parametersMap.keySet().iterator(); ii.hasNext();) {
String parameterName = ii.next();
ret += parameterName + ": " + parametersMap.get(parameterName);
if (ii.hasNext()) {
ret += ", ";
}
}
ret += ")";
return ret;
}
public String toHTML() {
String ret = new String();
ret += "<b>" + getName() + "</b>";
ret += "(";
Map<String, String> parametersMap = addParametersHTML(new LinkedHashMap<String, String>());
for (Iterator<String> ii = parametersMap.keySet().iterator(); ii.hasNext();) {
String parameterName = ii.next();
ret += "<span style='color: #777777;'>" + parameterName + ":</span> " +
parametersMap.get(parameterName);
if (ii.hasNext()) {
ret += ", ";
}
}
ret += ")";
return ret;
}
public int execute(RuleBasedPixel actor, Configuration configuration) {
if (actor.getEnergy() + energyChange < 0) { // die trying
return energyChange;
}
return target.execute(this, actor, configuration);
}
public LinkedHashMap<String, JComponent> addParametersGUI(LinkedHashMap<String, JComponent> parametersMap) {
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(energyChange, Integer.MIN_VALUE, Integer.MAX_VALUE, 1);
JSpinner energyChangeSpinner = new AutoSelectOnFocusSpinner(spinnerModel);
//energyChangeSpinner.setEditor(new JSpinner.NumberEditor(energyChangeSpinner, "+#;-#"));
energyChangeSpinner.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
energyChange = (Integer)((JSpinner)e.getSource()).getValue();
}
});
parametersMap.put("My Energy Change", energyChangeSpinner);
return parametersMap;
}
public Map<String, String> addParametersHTML(Map<String, String> parametersMap) {
if (energyChange > 0) {
parametersMap.put("my reward", Integer.toString(energyChange));
}
else if (energyChange < 0) {
parametersMap.put("my cost", Integer.toString((-1) * energyChange));
}
return parametersMap;
}
public Map<String, String> addParametersString(Map<String, String> parametersMap) {
if (energyChange > 0) {
parametersMap.put("my reward", Integer.toString(energyChange));
}
else if (energyChange < 0) {
parametersMap.put("my cost", Integer.toString((-1) * energyChange));
}
return parametersMap;
}
public abstract int execute(RuleBasedPixel actor, RelativeCoordinate direction, Configuration configuration);
}