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

sim.app.antsforage.Ant Maven / Gradle / Ivy

Go to download

MASON is a fast discrete-event multiagent simulation library core in Java, designed to be the foundation for large custom-purpose Java simulations, and also to provide more than enough functionality for many lightweight simulation needs. MASON contains both a model library and an optional suite of visualization tools in 2D and 3D.

The newest version!
/*
  Copyright 2006 by Sean Luke and George Mason University
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/

package sim.app.antsforage;

import sim.field.grid.*;
import sim.portrayal.*;
import sim.portrayal.simple.*;
import sim.util.*;
import sim.engine.*;
import java.awt.*;

public /*strictfp*/ class Ant extends OvalPortrayal2D implements Steppable
    {

    public static final int ADD_PHEROMONE = 0;
    public static final int MAX_PHEROMONE = 1;
    public static final int LOCAL_PHEROMONE = 2;

    public static final int PHEROMONE_TYPE = LOCAL_PHEROMONE;

    public static final boolean TOROIDAL_WORLD = false;

    // type of Ant
    public static final int ORIENTED_ANT = 0;
    public static final int NSEW_ANT = 1;
    public static final int EIGHT_NEIGHBOURS_ANT = 2;

    public static final int ANT_TYPE = ORIENTED_ANT;

    public static final boolean GREEDY_REPOSITIONING = true;

    public static final boolean GREEDY_EXPLORATION = true;

    public static final int N = 0;
    public static final int NE = 1;
    public static final int E = 2;
    public static final int SE = 3;
    public static final int S = 4;
    public static final int SW = 5;
    public static final int W = 6;
    public static final int NW = 7;

    public double pheromoneToLeaveBehind;
    public double minPheromone;
    public double maxPheromone;
    public int timeToLive;

    double subtractingRatio;
    double pheromoneRatio;

    int orientation;

    public boolean getHasFoodItem() { return hasFoodItem; }
    public void setHasFoodItem(boolean val) { hasFoodItem = val; }
    public boolean hasFoodItem;

    public static final double ANT_K = 0.001;
    public static final double ANT_N = 10.0;

    boolean justCreated;

    public Ant( int orientation,
        double pheromoneToLeaveBehind,
        double minPheromone,
        double maxPheromone,
        int timeToLive ) 
        {
        this.orientation = orientation;
        this.pheromoneToLeaveBehind = pheromoneToLeaveBehind;
        this.minPheromone = minPheromone;
        this.maxPheromone = maxPheromone;
        this.timeToLive = timeToLive;

        subtractingRatio = ( 1.0 / timeToLive ) * maxPheromone;
        pheromoneRatio = 1.0 * maxPheromone;

        hasFoodItem = false;
        justCreated = true;
        }
        
    protected void addInformation( final SimState state, int x, int y, final int orientation )
        {
        final AntsForage af = (AntsForage)state;
        final DecisionInfo di = af.decisionInfo;
        final DecisionMaker decisionMaker = af.decisionMaker;

        if( TOROIDAL_WORLD )
            {
            x = (x+AntsForage.GRID_WIDTH)%AntsForage.GRID_WIDTH;
            y = (y+AntsForage.GRID_HEIGHT)%AntsForage.GRID_HEIGHT;
            }
        else
            {
            if( x < 0 || x >= AntsForage.GRID_WIDTH || y < 0 || y >= AntsForage.GRID_HEIGHT )
                return;
            }
        if( ( af.buggrid.getObjectsAtLocation(x,y) == null ||
                af.buggrid.getObjectsAtLocation(x,y).numObjs < AntsForage.MAX_ANTS_PER_LOCATION ) &&
            af.obstacles.field[x][y] <= 0.5 )
            {
            // toroidal coordinates!
            di.position.x = x;
            di.position.y = y;
            di.orientation = orientation;

//            di.homePheromoneAmount = /*Strict*/Math.pow( ANT_K + ((AntsForage)state).toHomeGrid.field[di.position.x][di.position.y], ANT_N) ;
//            di.foodPheromoneAmount = /*Strict*/Math.pow( ANT_K + ((AntsForage)state).toFoodGrid.field[di.position.x][di.position.y], ANT_N );;
            di.homePheromoneAmount = 0.001 + af.toHomeGrid.field[di.position.x][di.position.y];
            di.foodPheromoneAmount = 0.001 + af.toFoodGrid.field[di.position.x][di.position.y];
            decisionMaker.addInfo( di );
            }
        }

    public DecisionInfo decideAction( final SimState state, final int myx, final int myy, final int orientation )
        {

        final AntsForage af = (AntsForage)state;
        final DecisionMaker decisionMaker = af.decisionMaker;

        decisionMaker.reset();

        // collect the sensory information for the new grid model
        // this should be done separately in the model, but whatever....

        switch( ANT_TYPE )
            {
            case ORIENTED_ANT:
                switch( orientation )
                    {
                    case 0: addInformation( state, myx-1, myy+1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx,   myy+1, orientation );        // forward
                        addInformation( state, myx+1, myy+1, (orientation+1)%8 );  // forward-right
                        break;
                    case 1: addInformation( state, myx,   myy+1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx+1, myy+1, orientation );        // forward
                        addInformation( state, myx+1, myy,   (orientation+1)%8 );  // forward-right
                        break;
                    case 2: addInformation( state, myx+1, myy+1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx+1, myy,   orientation );        // forward
                        addInformation( state, myx+1, myy-1, (orientation+1)%8 );  // forward-right
                        break;
                    case 3: addInformation( state, myx+1, myy,   (orientation+7)%8 );  // forward-left
                        addInformation( state, myx+1, myy-1, orientation );        // forward
                        addInformation( state, myx,   myy-1, (orientation+1)%8 );  // forward-right
                        break;
                    case 4: addInformation( state, myx+1, myy-1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx,   myy-1, orientation );        // forward
                        addInformation( state, myx-1, myy-1, (orientation+1)%8 );  // forward-right
                        break;
                    case 5: addInformation( state, myx,   myy-1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx-1, myy-1, orientation );        // forward
                        addInformation( state, myx-1, myy,   (orientation+1)%8 );  // forward-right
                        break;
                    case 6: addInformation( state, myx-1, myy-1, (orientation+7)%8 );  // forward-left
                        addInformation( state, myx-1, myy,   orientation );        // forward
                        addInformation( state, myx-1, myy+1, (orientation+1)%8 );  // forward-right
                        break;
                    case 7: addInformation( state, myx-1, myy,   (orientation+7)%8 );  // forward-left
                        addInformation( state, myx-1, myy+1, orientation );        // forward
                        addInformation( state, myx,   myy+1, (orientation+1)%8 );  // forward-right
                        break;
                    }
                break;
            case NSEW_ANT:
                addInformation( state, myx-1, myy,   (orientation+7)%8 ); // N
                addInformation( state, myx+1, myy,   (orientation+7)%8 ); // S
                addInformation( state, myx, myy-1,   (orientation+7)%8 ); // E
                addInformation( state, myx, myy+1,   (orientation+7)%8 ); // W
                break;
            case EIGHT_NEIGHBOURS_ANT:
                addInformation( state, myx-1, myy-1,   (orientation+7)%8 ); // N
                addInformation( state, myx-1, myy,   (orientation+7)%8 ); // N
                addInformation( state, myx-1, myy+1,   (orientation+7)%8 ); // N
                addInformation( state, myx+1, myy-1,   (orientation+7)%8 ); // S
                addInformation( state, myx+1, myy,   (orientation+7)%8 ); // S
                addInformation( state, myx+1, myy+1,   (orientation+7)%8 ); // S
                addInformation( state, myx-1, myy,   (orientation+7)%8 ); // E
                addInformation( state, myx+1, myy,   (orientation+7)%8 ); // W
                break;
            }

        if( hasFoodItem )
            return decisionMaker.getHomeGreedyDecision( state );
        else
            {
            if( GREEDY_EXPLORATION )
                return decisionMaker.getFoodGreedyDecision( state );
            else
                return decisionMaker.getFoodDecision( state );
            }
        }

    public void addPheromone(DoubleGrid2D grid, int x, int y, double pheromone)
        {
        switch( PHEROMONE_TYPE )
            {
            case ADD_PHEROMONE:
                grid.field[x][y] += /*Strict*/Math.abs(pheromoneToLeaveBehind);
                if (grid.field[x][y] > maxPheromone)
                    grid.field[x][y] = maxPheromone;
                break;
            case MAX_PHEROMONE:
                grid.field[x][y] = /*Strict*/Math.max( grid.field[x][y], pheromone );
                break;
            case LOCAL_PHEROMONE:
                double amount = /*Strict*/Math.max( grid.field[x][y], pheromone );
                if( x > 0 && y > 0 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x-1][y-1]-subtractingRatio, minPheromone ), amount );
                if( x > 0)
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x-1][y]-subtractingRatio, minPheromone ), amount );
                if( x > 0 && y < grid.field[x].length-1 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x-1][y+1]-subtractingRatio, minPheromone ), amount );
                if( y > 0 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x][y-1]-subtractingRatio, minPheromone ), amount );
                if( y < grid.field.length-1 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x][y+1]-subtractingRatio, minPheromone ), amount );
                if( x < grid.field.length-1 && y > 0 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x+1][y-1]-subtractingRatio, minPheromone ), amount );
                if( x < grid.field.length-1 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x+1][y]-subtractingRatio, minPheromone ), amount );
                if( x < grid.field.length-1 && y < grid.field[x].length-1 )
                    amount = /*Strict*/Math.max( /*Strict*/Math.max( grid.field[x+1][y+1]-subtractingRatio, minPheromone ), amount );
                grid.field[x][y] = amount;
                pheromoneRatio = amount;
                break;
            }
        }

    public DecisionInfo decideGreedyAction( final SimState state, final int myx, final int myy, final int orientation )
        {

        final AntsForage af = (AntsForage)state;
        final DecisionMaker decisionMaker = af.decisionMaker;

        decisionMaker.reset();

        // add all neighboring cells and move to one of them
        addInformation( state, myx,   myy+1, 0 );
        addInformation( state, myx+1, myy+1, 1 );
        addInformation( state, myx+1, myy,   2 );
        addInformation( state, myx+1, myy-1, 3 );
        addInformation( state, myx,   myy-1, 4 );
        addInformation( state, myx-1, myy-1, 5 );
        addInformation( state, myx-1, myy,   6 );
        addInformation( state, myx-1, myy+1, 7 );

        if( hasFoodItem )
            return decisionMaker.getHomeGreedyDecision( state );
        else
            return decisionMaker.getFoodGreedyDecision( state );

        }

    public void step( final SimState state )
        {
        final AntsForage af = (AntsForage)state;
        final DecisionMaker decisionMaker = af.decisionMaker;
        
        Int2D location = af.buggrid.getObjectLocation(this);
        int myx = location.x;
        int myy = location.y;

        if( justCreated )
            {
            DecisionInfo temp = decideGreedyAction( state, myx, myy, orientation );
            if( temp == null )
                return;
            orientation = temp.orientation;
            justCreated = false;
            }

        // final int START=-1;
        int bestx, besty, besto;

        DecisionInfo movingDecision = null;
        if( hasFoodItem )
            movingDecision = decideGreedyAction( state, myx, myy, orientation );
        else
            {
            decisionMaker.reset();
            addInformation( state, myx,   myy+1, 0 );
            addInformation( state, myx+1, myy+1, 1 );
            addInformation( state, myx+1, myy,   2 );
            addInformation( state, myx+1, myy-1, 3 );
            addInformation( state, myx,   myy-1, 4 );
            addInformation( state, myx-1, myy-1, 5 );
            addInformation( state, myx-1, myy,   6 );
            addInformation( state, myx-1, myy+1, 7 );
            int max = 0;
            int howMany = 1;
            for( int i = 1 ; i < decisionMaker.numInfos ; i++ )
                if( decisionMaker.info[max].foodPheromoneAmount ==
                    decisionMaker.info[i].foodPheromoneAmount )
                    {
                    howMany++;
                    }
                else if( decisionMaker.info[max].foodPheromoneAmount <
                    decisionMaker.info[i].foodPheromoneAmount )
                    {
                    max = i;
                    howMany = 1;
                    }
            if( howMany == 1 )
                movingDecision = decideGreedyAction( state, myx, myy, orientation );
            else
                movingDecision = decideAction( state, myx, myy, orientation );
            }
        if( movingDecision == null )
            {
            movingDecision = decideGreedyAction( state, myx, myy, orientation );
            if( movingDecision == null )
                {
                bestx = myx;
                besty = myy;
                besto = orientation;
                }
            else
                {
                bestx = movingDecision.position.x;
                besty = movingDecision.position.y;
                besto = movingDecision.orientation;
                }
            }
        else
            {
            bestx = movingDecision.position.x;
            besty = movingDecision.position.y;
            besto = movingDecision.orientation;
            }

        if( ( bestx != myx || besty != myy ))
            {
            // add some pheromones
            if( hasFoodItem )
                {
                addPheromone(af.toFoodGrid,myx,myy,(/*pheromoneToLeaveBehind*/pheromoneRatio));
                }
            else
                {
                addPheromone(af.toHomeGrid,myx,myy,(/*pheromoneToLeaveBehind*/pheromoneRatio));
                }
            if( bestx != myx && besty != myy )
                pheromoneRatio -= subtractingRatio*1.4142;
            else
                pheromoneRatio -= subtractingRatio;
            if( pheromoneRatio < 0 )
                {
                die( state );
                return;
                }


            // adjust the position of the agent, and then deposit the "to food" and "to home" pheromones
            af.buggrid.setObjectLocation(this,bestx,besty);
            orientation = besto;
            if( ( besty >= AntsForage.HOME_YMIN ) && ( besty <= AntsForage.HOME_YMAX ) &&
                ( bestx >= AntsForage.HOME_XMIN ) && ( bestx <= AntsForage.HOME_XMAX ) )
                {
                if( hasFoodItem )
                    {
                    af.foodCollected++;
                    hasFoodItem = false;
                    pheromoneRatio = 1.0 * maxPheromone;
                    if( GREEDY_REPOSITIONING )
                        {
                        // pick greediest orientation!
                        DecisionInfo temp = decideGreedyAction( state, myx, myy, orientation );
                        if( temp != null )
                            orientation = temp.orientation;
                        else
                            orientation = (orientation+4)%8;
                        }
                    else
                        orientation = (orientation+4)%8; // rotate 180
                    }
                }
            else if(  ( besty >= AntsForage.FOOD_YMIN ) && ( besty <= AntsForage.FOOD_YMAX ) &&
                ( bestx >= AntsForage.FOOD_XMIN ) && ( bestx <= AntsForage.FOOD_XMAX ) )
                {
                if( !hasFoodItem )
                    {
                    hasFoodItem = true;
                    pheromoneRatio = 1.0 * maxPheromone;
                    if( GREEDY_REPOSITIONING )
                        {
                        // pick greediest orientation!
                        DecisionInfo temp = decideGreedyAction( state, myx, myy, orientation );
                        if( temp != null )
                            orientation = temp.orientation;
                        else
                            orientation = (orientation+4)%8;
                        }
                    else
                        orientation = (orientation+4)%8; // rotate 180
                    }
                }

            timeToLive--;
            if( timeToLive <= 0 )
                {
                die( state );
                return;
                }
            }
        }

    // a few tweaks by Sean
    private Color noFoodColor = Color.black;
    private Color foodColor = Color.red;
    public final void draw(Object object, Graphics2D graphics, DrawInfo2D info)
        {
        if( hasFoodItem )
            graphics.setColor( foodColor );
        else
            graphics.setColor( noFoodColor );

        // this code was stolen from OvalPortrayal2D
        int x = (int)(info.draw.x - info.draw.width / 2.0);
        int y = (int)(info.draw.y - info.draw.height / 2.0);
        int width = (int)(info.draw.width);
        int height = (int)(info.draw.height);
        graphics.fillOval(x,y,width, height);

        }
    
    public Stoppable toDiePointer = null;
    public void die( final SimState state )
        {
        AntsForage antsforage = (AntsForage)state;
        antsforage.numberOfAnts--;
        antsforage.buggrid.remove( this );
        if(toDiePointer!=null) toDiePointer.stop();
        }

    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy