All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.bdi.examples.hunterprey_classic.environment.Environment Maven / Gradle / Ivy

Go to download

The Jadex BDI applications package contain several example applications, benchmarks and testcases using BDI agents.

The newest version!
package jadex.bdi.examples.hunterprey_classic.environment;

import jadex.bdi.examples.hunterprey_classic.Creature;
import jadex.bdi.examples.hunterprey_classic.Food;
import jadex.bdi.examples.hunterprey_classic.Hunter;
import jadex.bdi.examples.hunterprey_classic.IEnvironment;
import jadex.bdi.examples.hunterprey_classic.Location;
import jadex.bdi.examples.hunterprey_classic.Observer;
import jadex.bdi.examples.hunterprey_classic.Obstacle;
import jadex.bdi.examples.hunterprey_classic.Prey;
import jadex.bdi.examples.hunterprey_classic.RequestMove;
import jadex.bdi.examples.hunterprey_classic.TaskInfo;
import jadex.bdi.examples.hunterprey_classic.Vision;
import jadex.bdi.examples.hunterprey_classic.WorldObject;
import jadex.commons.SUtil;
import jadex.commons.SimplePropertyChangeSupport;
import jadex.commons.collection.MultiCollection;
import jadex.xml.bean.JavaReader;
import jadex.xml.bean.JavaWriter;

import jadex.commons.beans.PropertyChangeListener;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 *  The environment is the container all objects and creatures.
 */
public class Environment implements IEnvironment
{
	//-------- constants --------

	/** The default number of lease ticks. */
	public static final int	DEFAULT_LEASE_TICKS	= 50;

	//-------- attributes --------

	/** The singleton instance. */
	protected static Environment instance;

	/** The creatures. */
	protected Map creatures;

	/** The obstacles. */
	protected Set obstacles;

	/** The prey food. */
	protected Set food;

	/** All world objects accessible per location. */
	public MultiCollection world;

	/** The horizontal size. */
	protected int sizex;

	/** The vertictal size. */
	protected int sizey;

	/** The monitor. */
	protected Object monitor;

	/** The list for move and eat requests. */
	protected List tasklist;

	/** The helper object for bean events. */
	protected SimplePropertyChangeSupport pcs;

	/** The radnom number generator. */
	protected Random	rand;
	
	/** The highscore location. */
	protected List highscore;

	/** The last time the highscore was saved. */
	protected long	savetime;

	/** The interval between saves of highscore (-1 for autosave off). */
	protected long	saveinterval;
	
	/** The foodrate determines how often new food pops up. */
	protected int foodrate;
	
	/** The world age. */
	protected int age;
	
	//-------- constructors --------

	/**
	 *  Create a new environment.
	 */
	public Environment()
	{
		this.creatures = new HashMap();
		this.obstacles = new HashSet();
		this.food = new HashSet();
		this.world = new MultiCollection();
		this.monitor = new Object();
		this.tasklist = new ArrayList();
		this.pcs = new SimplePropertyChangeSupport(this);
		this.sizex = 30;
		this.sizey = 30;
		this.rand	= new Random(12345678);
		this.saveinterval	= 5000;
		this.foodrate = 5;

		// Hack! only for testing
		for(int i=0; i<125; i++)
			addObstacle(new Obstacle(getEmptyLocation()));
		for(int i=0; i<10; i++)
			addFood(new Food(getEmptyLocation()));

		// Read highscore list.
		InputStream is = null;
		try
		{
//			InputStream tmp = SUtil.getResource("highscore.dmp", Environment.class.getClassLoader());
//			ObjectInputStream is = new ObjectInputStream(tmp);
//			this.highscore = SUtil.arrayToList(is.readObject());
//			is.close();
			
			is = SUtil.getResource("highscore.dmp", Environment.class.getClassLoader());
			StringBuffer out = new StringBuffer();
			byte[] b = new byte[4096];
			for(int n; (n = is.read(b)) != -1;) 
			{
				out.append(new String(b, 0, n));
			}
			highscore = SUtil.arrayToList(JavaReader.objectFromXML(out.toString(), Environment.class.getClassLoader()));
		}
		catch(Exception e)
		{
//			e.printStackTrace();
//			System.out.println(e);
			highscore = new ArrayList();
		}
		finally
		{
			if(is!=null)
			{
				try
				{
					is.close();
				}
				catch(Exception e)
				{
				}
			}
		}
	}

	/**
	 *  Get the singleton.
	 *  @return The environment.
	 */
	public static Environment getInstance()
	{
		if(instance==null)
			instance = new Environment();
		return instance;
	}

	//-------- interface methods --------

	/**
	 *  Move one field upwards. The method will block
	 *  until the current simulation step has finished.
	 *  @return True, when the operation succeeded.
	 */
	public boolean moveUp(Creature me)
	{
		return move(me, RequestMove.DIRECTION_UP);
	}

	/**
	 *  Move one field downwards. The method will block
	 *  until the current simulation step has finished.
	 *  @return True, when the operation succeeded.
	 */
	public boolean moveDown(Creature me)
	{
		return move(me, RequestMove.DIRECTION_DOWN);
	}

	/**
	 *  Move one field to the left. The method will block
	 *  until the current simulation step has finished.
	 *  @return True, when the operation succeeded.
	 */
	public boolean moveLeft(Creature me)
	{
		return move(me, RequestMove.DIRECTION_LEFT);
	}

	/**
	 *  Move one field to the right. The method will block
	 *  until the current simulation step has finished.
	 *  @return True, when the operation succeeded.
	 */
	public boolean moveRight(Creature me)
	{
		return move(me, RequestMove.DIRECTION_RIGHT);
	}

	/**
	 *  Eat some object. The object has to be at the same location.
	 *  This method does not block, and can be called multiple
	 *  times during each simulation step.
	 *  @param food The object.
	 *  @return True, when the operation succeeded.
	 */
	public boolean eat(Creature me, WorldObject food)
	{
		boolean ret = false;
		int	points	= 0;
		me = getCreature(me);
		me.setLeaseticks(DEFAULT_LEASE_TICKS);
		if(food instanceof Food)
		{
			if(me.getLocation().equals(food.getLocation()))
			{
				ret	= removeFood((Food)food);
				points	= 1;
			}
		}
		else if(me instanceof Hunter && food instanceof Prey)
		{
			// Get actual creature from world model.
			Creature	creat	= getCreature((Creature)food);
			if(me.getLocation().equals(creat.getLocation()))
			{
				ret	= removeCreature(creat);
				points	= 5;
			}
		}

		if(ret)
		{
			me.setPoints(me.getPoints()+points);
		}
		
		{
			// This happens if the creature compute the next step on the old vision.
			// The "move" plan resumes execution on "notasks" condition trigger. The creature
			// agent will be informed and the next step is computed. The current vision is updated
			// via the SimTickerPlan AFTER the condition has triggered. In single core machines this
			// seems to happen not very often. But in multicore machines this is a very bad race condition
			// that happens very very very very often.
//			System.out.println("Creature tried to cheat: '"+me.getName()+"' Do we have a multicore problem?");
		}
		//block(); todo: make blocking for local case
		return ret;
	}

	/**
	 *  Add a move or eat action to the queue.
	 */
	public TaskInfo addEatTask(Creature me, WorldObject obj)
	{
		TaskInfo ret = new TaskInfo(new Object[]{"eat", me, obj});
		tasklist.add(ret);
		this.pcs.firePropertyChange("taskSize", tasklist.size()-1, tasklist.size());
		return ret;
	}

	/**
	 *  Add a move or eat action to the queue.
	 */
	public TaskInfo addMoveTask(Creature me, String dir)
	{
		TaskInfo ret = new TaskInfo(new Object[]{"move", me, dir});
		tasklist.add(ret);
		this.pcs.firePropertyChange("taskSize", tasklist.size()-1, tasklist.size());
		return ret;
	}
	
	/**
	 * Clear the TaskList
	 * HACK! Should be done in executeStep method, but that leads to problems with
	 * other Agents. They compute their next step with the old vision. :-( 
	 * This is a race condition too! Tasks can be lost.
	 */
	protected void clearTaskList()
	{
		int length = tasklist.size();
		tasklist.clear();
		this.pcs.firePropertyChange("taskSize", length, tasklist.size());
	}

	/**
	 *  Get the current vision (without updating the creatures leaseticks).
	 *  @param me The creature.
	 */
	public Vision internalGetVision(Creature me)
	{
		Vision ret = new Vision();
		me = getCreature(me);
		WorldObject[] wos;
		if(me instanceof Observer)
		{
			wos	= getAllObjects();
		}
		else
		{
			wos = getNearObjects(me.getLocation(), me.getVisionRange());
		}
		ret.setObjects(wos);
		return ret;
	}

	/**
	 *  Get the current vision. This method does not block,
	 *  and can be called multiple times during each simulation step.
	 *  @param me The creature.
	 */
	public Vision getVision(Creature me)
	{
		me = getCreature(me);
		me.setLeaseticks(DEFAULT_LEASE_TICKS);
		return internalGetVision(me);
	}

	/**
	 *  Get the width of the world.
	 */
	public int	getWidth()
	{
		return sizex;
	}

	/**
	 *  Get the height of the world.
	 */
	public int	getHeight()
	{
		return sizey;
	}

	//-------- management methods --------

	/**
	 *  Add a new prey food to the world.
	 *  @param nfood The new food.
	 */
	public void addFood(Food nfood)
	{
		this.food.add(nfood);
		this.world.put(nfood.getLocation(), nfood);
	}

	/**
	 *  remove a prey food to the world.
	 *  @param nfood Thefood.
	 */
	public boolean removeFood(Food nfood)
	{
		this.world.remove(nfood.getLocation(), nfood);
		return this.food.remove(nfood);
	}

	/**
	 *  Add a new obstacle to the world.
	 *  @param obstacle The new obstacle.
	 */
	public void addObstacle(Obstacle obstacle)
	{
		this.obstacles.add(obstacle);
		this.world.put(obstacle.getLocation(), obstacle);
	}

	/**
	 *  Remove a  obstacle to the world.
	 *  @param obstacle The obstacle.
	 */
	public boolean removeObstacle(Obstacle obstacle)
	{
		this.world.remove(obstacle.getLocation(), obstacle);
		return this.obstacles.remove(obstacle);
	}

	/**
	 *  Add a new creature to the world.
	 *  @param creature The creature.
	 */
	public Creature addCreature(Creature creature)
	{
		Creature copy;
		if(!creatures.containsKey(creature))
		{
			copy = (Creature)creature.clone();
			copy.setLeaseticks(DEFAULT_LEASE_TICKS);
			copy.setWorldWidth(getWidth());
			copy.setWorldHeight(getHeight());
			this.creatures.put(copy, copy);

			if(!(copy instanceof Observer))
			{
				copy.setAge(0);
				copy.setPoints(0);
				copy.setLocation(getEmptyLocation());
//				if(copy instanceof Hunter)
//					copy.setVisionRange(5);
//				else
					copy.setVisionRange(3);					
				this.world.put(copy.getLocation(), copy);
				this.highscore.add(copy);
			}
			//System.out.println("Environment, creature added: "+copy.getName()+" "+copy.getLocation());
		}
		else
		{
			throw new RuntimeException("Creature already exists: "+creature);
		}
		return copy;
	}

	/**
	 *  Remove a creature to the world.
	 *  @param creature The creature.
	 */
	public boolean removeCreature(Creature creature)
	{
		// Remove tasks of this creature.
		int	tasks	= tasklist.size();
		for(Iterator it=tasklist.iterator(); it.hasNext(); )
		{
			TaskInfo	task	= (TaskInfo) it.next();
			Object[] params = (Object[])task.getAction();
			if(creature.equals(params[1]))
			{
//				System.out.println("Removed: "+task);
				it.remove();
			}
		}
		this.pcs.firePropertyChange("taskSize", tasks, tasklist.size());
		if(this.world.containsKey(creature.getLocation()))
			this.world.remove(creature.getLocation(), creature);
		return this.creatures.remove(creature)!=null;
	}

	/**
	 *  Execute a step.
	 */
	public void executeStep()
	{
		// Creatures that already acted in this step.
		Set	acted	= new HashSet();

		Creature[]	creatures	= getCreatures();
		for(int i=0; i=0 && savetime+saveinterval<=time)
		{
			saveHighscore();
			savetime	= time;
		}
		
		age++;
	}

	/**
	 *  Get the world age.
	 *  @return The age of the world.
	 */
	public int getWorldAge()
	{
	    return age;
	}
	
	/**
	 *  Get the foodrate.
	 *  @return The foodrate.
	 */
	public int getFoodrate()
	{
	    return foodrate;
	}
	
	/**
	 *  Set the foodrate. 
	 *  @param foodrate The foodrate.
	 */
	public void setFoodrate(int foodrate)
	{
	    this.foodrate = foodrate;
	}
	
	/**
	 *  Perform a move.
	 *  @param me The creature.
	 *  @param dir The direction.
	 */
	public boolean move(Creature me, String dir)
	{
		boolean ret = true;
		me	= getCreature(me);
		me.setLeaseticks(DEFAULT_LEASE_TICKS);
		Location newloc = createLocation(me.getLocation(), dir);
		
		Collection col = world.getCollection(newloc);
		if(col!=null && col.size()==1 && col.iterator().next() instanceof Obstacle)
		{
			ret = false;
		}
		else
		{
			// Move creature.
			try
			{
				world.remove(me.getLocation(), me);
				me.setLocation(newloc);
				world.put(me.getLocation(), me);
			}
			catch(Exception e)
			{
				//System.out.println("!!! "+me);
				System.out.println(world+" "+me);
				e.printStackTrace();
			}
		}
		//block(); todo: make blocking for local case
		return ret;
	}

	/**
	 *  Get the creatures.
	 *  @return The creatures.
	 */
	public Creature[] getCreatures()
	{
		return (Creature[])creatures.values().toArray(new Creature[creatures.size()]);
	}

	/**
	 *  Get the obstacles.
	 *  @return The obstacles.
	 */
	public Obstacle[] getObstacles()
	{
		return (Obstacle[])obstacles.toArray(new Obstacle[obstacles.size()]);
	}

	/**
	 *  Get the obstacles.
	 *  @return The obstacles.
	 */
	public Food[] getFood()
	{
		return (Food[])food.toArray(new Food[food.size()]);
	}

	/**
	 *  Get all objects in the world (obstacles, food, and creature).
	 */
	public WorldObject[]	getAllObjects()
	{
		// Add obstacles and food to return set.
		ArrayList	ret	= new ArrayList();
		ret.addAll(obstacles);
		ret.addAll(food);

		// Add creatures.
		ret.addAll(creatures.values());

		// Convert to array and return.
		return (WorldObject[])ret.toArray(new WorldObject[ret.size()]);
	}

	/**
	 *  Create a location.
	 *  @param loc The location.
	 *  @param dir The direction.
	 *  @return The new location.
	 */
	protected Location createLocation(Location loc, String dir)
	{
		int x = loc.getX();
		int y = loc.getY();

		if(RequestMove.DIRECTION_UP.equals(dir))
		{
			y = (sizey+y-1)%sizey;
		}
		else if(RequestMove.DIRECTION_DOWN.equals(dir))
		{
			y = (y+1)%sizey;
		}
		else if(RequestMove.DIRECTION_LEFT.equals(dir))
		{
			x = (sizex+x-1)%sizex;
		}
		else if(RequestMove.DIRECTION_RIGHT.equals(dir))
		{
			x = (x+1)%sizex;
		}

		return new Location(x, y);
	}

	/**
	 *  Get an empty location.
	 *  @return The location.
	 */
	protected Location getEmptyLocation()
	{
		Location	ret	= null;
		while(ret==null)
		{
			ret	= new Location(rand.nextInt(sizex), rand.nextInt(sizey));
			if(world.containsKey(ret))
			{
				ret	= null;
			}
		}

		return ret;
	}

	/**
	 *  Return the size of the task list.
	 *  @return The task size.
	 */
	public int getTaskSize()
	{
		return tasklist.size();
	}

	/**
	 *  Get the internal representation of a creature.
	 *  If the creature is unknown it gets added to the environment.
	 *  @param creature The creature.
	 *  @return The creature as known in the environment.
	 */
	protected Creature getCreature(Creature creature)
	{
		Creature ret = (Creature)creatures.get(creature);
		if(ret==null)
		{
			ret = addCreature(creature);
		}
		return ret;
	}

	/**
	 *  Get objects near a position.
	 *  @param loc The location.
	 *  @param range The range.
	 */
	protected WorldObject[] getNearObjects(Location loc, int range)
	{
		Collection ret = new ArrayList();
		int x = loc.getX();
		int y = loc.getY();

		for(int i=x-range; i<=x+range; i++)
		{
			for(int j=y-range; j<=y+range; j++)
			{
				Collection tmp = world.getCollection(new Location((i+sizex)%sizex, (j+sizey)%sizey));
				if(tmp!=null)
					ret.addAll(tmp);
			}
		}

		return (WorldObject[])ret.toArray(new WorldObject[ret.size()]);
	}

	/**
	 *  Get the current highscore.
	 *  @return The 10 best creatures.
	 */
	public Creature[] getHighscore()
	{
		try {
			Collections.sort(highscore, new Comparator()
			{
				public int	compare(Object o1, Object o2)
				{
					return ((Creature)o2).getPoints()
							- ((Creature)o1).getPoints();
				}
			});
			List copy = highscore.subList(0, Math.min(highscore.size(), 10));
			return (Creature[])copy.toArray(new Creature[copy.size()]);
		}
		catch (ClassCastException cce)
		{
			return new Creature[0];
		}
	}

	/**
	 *  Save the highscore to a file.
	 */
	public void saveHighscore()
	{
		OutputStreamWriter os = null;
		try
		{
			String outputFile = "highscore.dmp";
			
			// write as serialized object
			//ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(outputFile));
			//os.writeObject(getHighscore());
			//os.close();
			
			// write as xml file
			os = new OutputStreamWriter(new FileOutputStream(outputFile), "UTF-8");
			os.write(JavaWriter.objectToXML(getHighscore(),this.getClass().getClassLoader()));
			os.close();

//			System.out.println("Saved highscore.");
		}
		catch(Exception e)
		{
			System.out.println("Error writing hunterprey highscore 'highscore.dmp'.");
			e.printStackTrace();
		}
		finally
		{
			if(os!=null)
			{
				try
				{
					os.close();
				}
				catch(Exception e)
				{
				}
			}
		}
	}

	/**
	 *  Set the highscore save interval (-1 for autosave off).
	 */
	public void	setSaveInterval(long saveinterval)
	{
		this.saveinterval	= saveinterval;
	}

	/**
	 *  Get the highscore save interval (-1 for autosave off).
	 */
	public long	getSaveInterval()
	{
		return this.saveinterval;
	}

	//-------- property methods --------

	/**
     *  Add a PropertyChangeListener to the listener list.
     *  The listener is registered for all properties.
     *  @param listener  The PropertyChangeListener to be added.
     */
    public void addPropertyChangeListener(PropertyChangeListener listener)
	{
		pcs.addPropertyChangeListener(listener);
    }

    /**
     *  Remove a PropertyChangeListener from the listener list.
     *  This removes a PropertyChangeListener that was registered
     *  for all properties.
     *  @param listener  The PropertyChangeListener to be removed.
     */
    public void removePropertyChangeListener(PropertyChangeListener listener)
	{
		pcs.removePropertyChangeListener(listener);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy