/* CVS: $Id: Action.java,v 1.30 2001/03/18 19:16:33 gvijf Exp $ */ package evolution.actions; import java.util.*; import java.lang.*; import evolution.*; import evolution.resources.*; import evolution.lands.*; import evolution.actions.*; /** * An action which humans can do. */ abstract public class Action { /** * Checks if this action can be performed by the human on the selected square of land. * If not, some exception will be thrown. */ protected void doChecks(Human human) throws NotEnoughResourcesException, IllegalLandTypeException, NotEnoughLandResourcesException { if(!hasEnoughResources()) throw new NotEnoughResourcesException(); if(!canBePerformedOn(human.getSquareOfLand())) throw new IllegalLandTypeException(); if(!human.getSquareOfLand().hasEnoughLandResources(getUsesLandResources())) throw new NotEnoughLandResourcesException(); } /** * Perform the action. * This is automatically triggered via Evolution. * @see _perform */ public void perform(Human human) throws NotEnoughResourcesException, IllegalLandTypeException, NotEnoughLandResourcesException { //System.err.println("Action.perform"); doChecks(human); // modify the world resources modifyResources(getProducesResources(), 1.0, true); modifyResources(getUsesResources(), -1.0, false); // can throw NotEnoughResourcesException // modify the land resources modifyLandResources(human.getSquareOfLand(), getProducesLandResources(), 1.0, true); modifyLandResources(human.getSquareOfLand(), getUsesLandResources(), -1.0, true); // can throw NotEnoughLandResourcesException _perform(human); } /** * Returns a map of the names of resources that are produced * by this action. */ protected Map getProducesResources() { return ActionKnowledgeCatalog.getInst().producesResources(getName()); } /** * Returns a map of the names of resources that are used * by this action. */ protected Map getUsesResources() { return ActionKnowledgeCatalog.getInst().usesResources(getName()); } /** * Returns a map of the names of landresources that are produced * by this action. */ protected Map getProducesLandResources() { return ActionKnowledgeCatalog.getInst().producesLandResources(getName()); } /** * Returns a map of the names of landresources that are used * by this action. */ protected Map getUsesLandResources() { return ActionKnowledgeCatalog.getInst().usesLandResources(getName()); } /** * Checks whether this action can be performed on the given square of land. */ protected boolean canBePerformedOn(SquareOfLand square) { return ActionKnowledgeCatalog.getInst().canBePerformedOn(getName(), square); } /** * Modify the resources that are mentioned in the given map. * mul has the value 1 when the */ protected void modifyResources(Map resources, double mul, boolean influencedBy) throws NotEnoughResourcesException { Iterator it = resources.keySet().iterator(); while(it.hasNext()) { String resName = (String) it.next(); if (influencedBy) ResourceKnowledgeCatalog.getInst().modResource(resName, getName(), mul * ((Double) resources.get(resName)).doubleValue()); else ResourceKnowledgeCatalog.getInst().modResource(resName, mul * ((Double) resources.get(resName)).doubleValue()); } } /** * Modify the landresources that are mentioned in the given map. */ protected void modifyLandResources(SquareOfLand square, Map resources, double mul, boolean influencedBy) throws NotEnoughLandResourcesException { Iterator it = resources.keySet().iterator(); while(it.hasNext()) { String resName = (String) it.next(); if (influencedBy) { double influencedValue = ResourceKnowledgeCatalog.getInst() .influencedByResources(getName(), ((Double) resources.get(resName)).doubleValue()); square.modResource(resName, mul * influencedValue); } else square.modResource(resName, mul * ((Double) resources.get(resName)).doubleValue()); } } /** * A special case of perform */ public void performEnergyBuffer(SquareOfLand square, Human human) throws IllegalLandTypeException { if(!canBePerformedOn(square)) throw new IllegalLandTypeException(); try { human.decreaseEnergyBuffer(10); modifyResources(ActionKnowledgeCatalog.getInst().producesResources(getName()), 1.0, false); // FIXME: should we call _perform here? //_perform(); } catch(EmptyEnergyBufferException e) { human.die(); } catch(NotEnoughResourcesException e) { } } /** * Extra things which should be done when this action's perform * method is called. * Template method. */ protected abstract void _perform(Human human) throws NotEnoughResourcesException, IllegalLandTypeException, NotEnoughLandResourcesException; /** * Return the name of this action. */ public String getName() { return ActionKnowledgeCatalog.getInst().stripPathFromClassName(this); } /** * Checks whether this action is an energybufferaction = nofoodaction in our * .prop files) */ public boolean isEnergyBufferAction() { return ActionKnowledgeCatalog.getInst().getEnergyBufferActionTypes().contains(getName()); } /** * Checks whether this action had enough worldresources to be performed. */ public boolean hasEnoughResources() { Map neededResources = getUsesResources(); Iterator it = neededResources.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); double resourceValue = ((Double) neededResources.get(key)).doubleValue(); try { if (!World.getInst().getResource(key).has(resourceValue)) return false; } catch (NoSuchResourceException e) { //can't happen } } return true; } /** * Get the square in the range of a certain position where it is the most * suitable to perform a given action. */ public SquareOfLand findSquare (SquareOfLand sq, int visionrange) { //System.err.println("findSquare"); boolean maximizing = ActionKnowledgeCatalog.getInst().isMaximizing(getName()); double optimum; if (maximizing) optimum = 0; else optimum = Long.MAX_VALUE; List optimums = new ArrayList(); List possible = new ArrayList(); Map neededResources = getUsesLandResources(); GameBoard gameBoard = World.getInst().getGameBoard(); List visibleSquares = gameBoard.getSurroundingSquares(sq, visionrange, true); Iterator it = visibleSquares.iterator(); while (it.hasNext()) { SquareOfLand visibleSquare = (SquareOfLand) it.next(); if(canBePerformedOn(visibleSquare) && (visibleSquare.getHuman() == null)) { possible.add(visibleSquare); double tempOptimum = calculateSquareValue(visibleSquare, neededResources, maximizing); if( (maximizing && (tempOptimum > optimum)) || (!maximizing && (tempOptimum < optimum)) ) { optimum = tempOptimum; optimums = new ArrayList(); optimums.add(visibleSquare); } else { if(tempOptimum == optimum) { optimums.add(visibleSquare); } } } } if(optimums.isEmpty()) { if(possible.isEmpty()) { return sq; } else { return (SquareOfLand) possible.get((int) (Math.random() * possible.size())); } } return (SquareOfLand) optimums.get((int) (Math.random() * optimums.size())); } /** * Calculate the value of the given square of land for this action. * This method is necessary for the vision of humans. */ protected double calculateSquareValue(SquareOfLand sq, Map neededResources, boolean maximizing) { double value = 0; Iterator it = neededResources.keySet().iterator(); while (it.hasNext()) { String resName = (String) it.next(); if (sq.containsLandResource(resName)) { double containsValue = sq.getLandResource(resName).getVisibleValue(); double usesValue = ((Double) neededResources.get(resName)).doubleValue(); value += containsValue*usesValue; } else if (maximizing) return value = 0; } return value; } /* protected boolean betterSquareValue(double value, double optimum, boolean maximizing) { if (maximizing) return (value > optimum); return (value < optimum); }*/ }