This site requires JavaScript, please enable it in your browser!
Greenfoot back
xixEmilyxix
xixEmilyxix wrote ...

2021/12/4

Object moving towards another object

xixEmilyxix xixEmilyxix

2021/12/4

#
Ive looked at the ant simulation and copied the code into mine and edited some to see how it would work and ive run into some issues: 1. The forager continuously move in a circle and does not move around the screen 2. The pheromones just get put in one place which is in the middle of where the forager bees are going round This is the code for the bee class, forager class and flower class
public class Bee extends Actor
{
    /** The maximum movement speed of the ant. */
    private static final int SPEED = 3;

    /** Current movement. Defined as the offset in x and y direction moved in each step. */
    private int deltaX;
    private int deltaY;

    /** The home ant hill. */
    private Hive home;

    /**
     * Crtae a new creature with neutral movement (movement speed is zero).
     */
    public Bee()
    {
        deltaX = 0;
        deltaY = 0;
    }
    
    /**
     * Set the home hill of this creature.
     */
    public void setHomeHive(Hive homeHive)
    {
        home = homeHive;
    }
    
    /**
     * Get the home hill of this creature.
     */
    public Hive getHomeHive()
    {
        return home;
    }
    
    /**
     * Walk around randomly (random direction and speed).
     */
    public void randomWalk()
    {
        if (randomChance(25)) {  
            deltaX = adjustSpeed(deltaX);
            deltaY = adjustSpeed(deltaY);
        }
        walk();
    }

    /**
     * Try to walk home. Sometimes creatures get distracted or encounter small obstacles, so
     * they occasionally head in a different direction for a moment.
     */
    public void walkTowardsHome()
    {
        if(home == null) {
            //if we do not have a home, we can not go there.
            return;
        }
        if (randomChance(2)) {
            randomWalk();  // cannot always walk straight. 2% chance to turn off course.
        }
        else {
            headRoughlyTowards(home);
            walk();
        }
    }
    
    /**
     * Try to walk away from home. (Goes occasionally off course a little.)
     */
    public void walkAwayFromHome()
    {
        if(home == null) {
            //if we do not have a home, we can not head away from it.
            return;
        }
        if (randomChance(2)) {
            randomWalk();  // cannot always walk straight. 2% chance to turn off course.
        }
        else {
            headRoughlyTowards(home);   // first head towards home...
            deltaX = -deltaX;           // ...then turn 180 degrees
            deltaY = -deltaY;
            walk();
        }
    }

    /**
     * Adjust the walking direction to head towards the given co-ordinates.
     */
    public void headTowards(Actor target)
    {
        deltaX = capSpeed(target.getX() - getX());
        deltaY = capSpeed(target.getY() - getY());
    }
    
    /**
     * Walk forward in the current direction with the current speed. 
     * (Does not change direction or speed.)
     */
    public void walk()
    {
        setLocation(getX() + deltaX, getY() + deltaY);
        setRotation((int) (180 * Math.atan2(deltaY, deltaX) / Math.PI));
    }

    /**
     * Adjust the walking direction to head somewhat towards the given co-ordinates. This does not 
     * always head in the same direction. The heading is slightly random (but likely to be somewhat
     * towards the target) to make it look more natural.
     */
    private void headRoughlyTowards(Actor target)
    {
        int distanceX = Math.abs(getX() - target.getX());
        int distanceY = Math.abs(getY() - target.getY());
        boolean moveX = (distanceX > 0) && (Greenfoot.getRandomNumber(distanceX + distanceY) < distanceX);
        boolean moveY = (distanceY > 0) && (Greenfoot.getRandomNumber(distanceX + distanceY) < distanceY);

        deltaX = computeHomeDelta(moveX, getX(), target.getX());
        deltaY = computeHomeDelta(moveY, getY(), target.getY());
    }
    
    /**
     * Compute and return the direction (delta) that we should steer in when
     * we're on our way home.
     */
    private int computeHomeDelta(boolean move, int current, int home)
    {
        if (move) {
            if (current > home)
                return -SPEED;
            else
                return SPEED;
        }
        else
            return 0;
    }

    /**
     * Adjust the speed randomly (start moving, continue or slow down). The
     * speed returned is in the range [-SPEED .. SPEED].
     */
    private int adjustSpeed(int speed)
    {
        speed = speed + Greenfoot.getRandomNumber(2 * SPEED - 1) - SPEED + 1;
        return capSpeed(speed);
    }

    /**
     * Make sure the speed returned is in the range [-SPEED .. SPEED].
     */
    private int capSpeed(int speed)
    {
        if (speed < -SPEED)
            return -SPEED;
        else if (speed > SPEED)
            return SPEED;
        else
            return speed;
    }

    /**
     * Return 'true' in exactly 'percent' number of calls. That is: a call
     * randomChance(25) has a 25% chance to return true.
     */
    private boolean randomChance(int percent)
    {
        return Greenfoot.getRandomNumber(100) < percent;
    }
public class Forager extends Bee
{
        /** Every how many steps can we place a pheromone drop. */
    private static final int MAX_PH_LEVEL = 18;

    /** How long do we keep direction after finding pheromones. */
    private static final int PH_TIME = 30;

    /** Indicate whether we have any food with us. */
    private boolean carryingFlower = false;

    /** How much pheromone do we have right now. */
    private int pheromoneLevel = MAX_PH_LEVEL;

    /** How well do we remember the last pheromone - larger number: more recent */
    private int foundLastPheromone = 0;

    /**
     * Create an ant with a given home hill. The initial speed is zero (not moving).
     */
    public Forager(Hive home)
    {
        setHomeHive(home);
        //set image of forager
        setImage(new GreenfootImage("foragerBee.png"));
        GreenfootImage image = getImage();  
        image.scale(image.getWidth() - 1300, image.getHeight() - 700);  
        setImage(image);
    }

    /**
     * Do what an ant's gotta do.
     */
    public void act()
    {
        if (carryingFlower) {
            walkTowardsHome();
            handlePheromoneDrop();
            checkHome();
        }
        else {
            searchForFlower();
        }
    }

    /**
     * Walk around in search of food.
     */
    private void searchForFlower()
    {
        if (foundLastPheromone > 0) { // if we can still remember...
            foundLastPheromone--;
            walkAwayFromHome();
        }
        else if (smellPheromone()) {
            walkTowardsPheromone();
        }
        else {
            randomWalk();
        }
        checkFlower();
    }

    /**
     * Are we home? Drop the food if we are, and start heading back out.
     */
    private void checkHome()
    {
        if (atHome()) {
            dropFlower();
        }
    }

    /**
     * Are we home?
     */
    private boolean atHome()
    {
        if (getHomeHive() != null) {
            return (Math.abs(getX() - getHomeHive().getX()) < 4) && (Math.abs(getY() - getHomeHive().getY()) < 4);
        }
        else {
            return false;
        }
        
    }

    /**
     * Is there any food here where we are? If so, take some!.
     */
    public void checkFlower()
    {
        Flower flower = (Flower) getOneIntersectingObject(Flower.class);
        if (flower != null) {
            takeFlower(flower);
        }
    }

    /**
     * Take some food from a fool pile.
     */
    private void takeFlower(Flower flower)
    {
        carryingFlower = true;        
        //setImage("ant-with-food.gif");
    }

    /**
     * Drop our food in the ant hill.
     */
    private void dropFlower()
    {
        carryingFlower = false;
        getHomeHive().countFlower();
        //setImage("ant.gif");
    }

    /**
     * Check whether we can drop some pheromone yet. If we can, do it.
     */
    private void handlePheromoneDrop()
    {
        if (pheromoneLevel == MAX_PH_LEVEL) {
            Pheromone ph = new Pheromone();
            getWorld().addObject(ph, getX(), getY());
            pheromoneLevel = 0;
        }
        else {
            pheromoneLevel++;
        }
    }
    
    /**
     * Check whether we can smell pheromones. If we can, return true, otherwise return false.
     */
    public boolean smellPheromone()
    {
        Actor ph = getOneIntersectingObject(Pheromone.class);
        return (ph != null);
    }

    /**
     * If we can smell some pheromone, walk towards it. If not, do nothing.
     */
    public void walkTowardsPheromone()
    {
        Actor ph = getOneIntersectingObject(Pheromone.class);
        if (ph != null) {
            headTowards(ph);
            walk();
            if (ph.getX() == getX() && ph.getY() == getY()) {
                foundLastPheromone = PH_TIME;
            }
        }
    }

}
public class Flower extends Actor
{
    /**
     * Create a pile of food with an image depicting the amount.
     */
    public Flower()
    {
        //set image of this type of flower
        setImage(new GreenfootImage("pinkFlower.png"));
        GreenfootImage image = getImage();  
        image.scale(image.getWidth() - 500, image.getHeight() - 500);  
        setImage(image);
    }
}
Does anyone have any idea how to make them move around normally?
Spock47 Spock47

2021/12/4

#
Well, I tested it a little bit (under the assumption that Pheromone and Hive classes do not contain any methods (apart from countFlower). 1. If there is one forager and no flower, the forager flies around more or less randomly to find a flower. That looks correct. 2. If there is one forager and one flower, again the forager flies around to find a flower. Once it spotted the flower, it returns to hive and leaves pheromones on the way and will then continuously move the path between the flower and the hive. That also looks correct. (The question is whether you want to change the source code in a way that the flower should get depleted at some point). 3. If there are two foragers and two flowers (starting at different sides), I found that both foragers will find a flower and upon returning to the hive, both will continue to harvest the same flower. Over time a big amount of pheromones pile up on the path and sometimes a bee gets "confused" because there is so much pheromones that it does not understand in which direction to fly, effectively ending up in a circular movement around some pheromone. Also, because of the partly random behavior, a forager sometimes (but very rarely) breaks out of the pheromone path and instead searches for a new flower. This is also true for "breaking out of the confusion circle". Here, probably the pheromones should also wear out after some time (giving them a counter and delete them when the counter reaches 0). I found that the probability for a confusion circle raises with the amount of pheromones. So, having the pheromones disappear after e.g. 1000 cycles also made the foragers break the confusion cycle eventually.
xixEmilyxix wrote...
Does anyone have any idea how to make them move around normally?
I hope the idea above is fixing the problem for you. Otherwise, I have to ask: What does "move around normally" mean? Live long and prosper, Spock47
xixEmilyxix xixEmilyxix

2021/12/5

#
Oh yeah i see that because when i run my simulation there are two foragers on each side and one is going in a circle. By normally I meant that foragers will fly around and look for flowers and then go to them. I think i also need to make it that the flowers disappear so this doesnt happen? Although, im not really sure how to fix it because my beeworld World class runs the simulation and im not sure how that is affecting it but i think it is also causing the issue. I have code in the hive class and the pheromone class which is shown below.
public class Hive extends Actor
{
    /** Number of ants that have come out so far. */
    private int foragers = 0;
    
    /** Total number of ants in this hill. */
    private int maxForagers = 2;

    /** Counter to show how much food have been collected so far. */
    private Counter flowerCounter;
    
    /**
     * Constructor for ant hill with default number of ants (40).
     */
    public Hive()
    {
    }

    /**
     * Construct an ant hill with a given number of ants.
     */
    public Hive(int numberOfForagers)
    {
        maxForagers = numberOfForagers;
    }

    /**
     * Act: If there are still ants left inside, see whether one should come out.
     */
    public void act()
    {
        if(foragers < maxForagers) 
        {
            if(Greenfoot.getRandomNumber(100) < 10) 
            {
                getWorld().addObject(new Forager(this), getX(), getY());
                foragers++;
            }
        }
    }

    /**
     * Record that we have collected another bit of food.
     */
    public void countFlower()
    {
        if(flowerCounter == null) 
        {
            flowerCounter = new Counter("Flower: ");
            int x = getX();
            int y = getY() + getImage().getWidth()/2 + 8;

            getWorld().addObject(flowerCounter, x, y);
        }        
        flowerCounter.increment();
    }
   
}
public class Pheromone extends Actor
{
     private final static int MAX_INTENSITY = 180;
    private int intensity;

    /**
     * Create a new drop of pheromone with full intensity.
     */
    public Pheromone()
    {
        intensity = MAX_INTENSITY;
        updateImage();
    }

    /**
     * The pheromone decreases the intensity. When the intensity reaches zero, it disappears.
     */
    public void act()
    {
        intensity -= 1;
        if (intensity <= 0) {
            getWorld().removeObject(this);
        }
        else {
            if ((intensity % 4) == 0) {     // every four steps...
                updateImage();
            }
        }
    }

    /**
     * Make the image. The size and transparency are proportional to the intensity.
     */
    private void updateImage()
    {
        int size = intensity / 3 + 5;
        GreenfootImage image = new GreenfootImage(size + 1, size + 1);
        int alpha = intensity / 3;
        image.setColor(new Color(255, 255, 255, alpha));
        image.fillOval(0, 0, size, size);
        image.setColor(Color.DARK_GRAY);
        image.fillRect(size / 2, size / 2, 2, 2);   // small dot in the middle
        setImage(image);
    }

}
This is the part that runs in my simulation when i hit start because the weather value is 0:
       public void runSimulation()
    {
        if(weatherValue >= -40 && weatherValue <= 15)
         {
             //if the weather value is between -40 and 15 then the low temp simulation will run
             lowTemperature();
         } else if(weatherValue >= 16 && weatherValue <= 27)
         {
             //if the weather value is between 16 and 27 then the regular temp simulation will run
             regularTemperature();
         } else if(weatherValue >= 28 && weatherValue <= 40)
         {
             //if the weather value is between 28 and 40 then the high temp simulation will run
             highTemperature();
         }     
    }
    
    public void lowTemperature()
    {
        //add flowers to the bee world
        PinkFlower PinkFlower = new PinkFlower ();
        YellowFlower YellowFlower = new YellowFlower ();
        PurpleFlower PurpleFlower = new PurpleFlower ();
        addObject(new PinkFlower(), 500, 500);
        addObject(new PurpleFlower(), 100, 300);
        addObject(new YellowFlower(), 200, 400);
        addObject(new PinkFlower(), 100, 100);
        addObject(new PinkFlower(), 700, 400);
        addObject(new PurpleFlower(), 800, 200);
        
        //add bees to the bee world
        //Forager Forager = new Forager();
        Worker Worker = new Worker ();
        Queen Queen = new Queen ();
        addObject(new Queen(), 500, 250);
        
        //add the hive to the bee world
        Hive Hive = new Hive ();
        addObject(new Hive(), 650, 400);
    }
xixEmilyxix xixEmilyxix

2021/12/5

#
And to add, im not sure why there are two foragers spawning
Spock47 Spock47

2021/12/6

#
I ran it again with the given source code (but I deleted the queen and only used simple flowers) and it still looks ok for me. Ok means: sometimes the foragers get confused for a bit, but as soon as the pheromones wear off, they get back to normal. 1. Does the queen or the flower subclasses interact in any way with the rest that could trigger the unwanted behavior of the foragers? 2. Did you check what happens when you set the speed to maximum and wait for a minute? Apart from that, I am out of good ideas here. I mean one could add some additional mechanism that the bee "remembers" where it was in the last seconds and makes sure, that it does not stay in the same place forever. To stay true to the scenario, these could be done with an "anti-pheromone". But I guess that probably would only add unnecessary complexity - it is just a little bit difficult to solve the problem when not seeing it happen on my computer.
xixEmilyxix wrote...
And to add, im not sure why there are two foragers spawning
The reason for that can be found in the act method the Hive class:
    public void act()
    {
        if(foragers < maxForagers) 
        {
            if(Greenfoot.getRandomNumber(100) < 10) 
            {
                getWorld().addObject(new Forager(this), getX(), getY());
                foragers++;
            }
        }
    }
1. In the beginning there is no forager: "foragers = 0" (Hive, line 4) and the max number of foragers is defined as "maxForagers = 2" (Hive, line 7) 2. Now, in line 3 above (Hive, line 32), the if-check is true (0 < 2), therefore going into the if-block. 3. The inner condition in line 5 above (Hive, line 34) is randomly true in 10 % of cases, therefore after some iterations (round about 10 times), the inner if-block will be executed: 4. That means, that in line 7 above (Hive, line 36); a forager is added to the world - and in the next line, the foragers attribute is increased to 1. 5. 1 is still smaller than 2, so the check from line 3 is still true, so points 2. through 4. will repeat the same way until a second forager is added to the world. 6. Now, the value of foragers is 2. and the expression "2 < 2" is false, which means that the if-block will never be executed again. That is, why the hive will spawn two foragers over time. Live long and prosper, Spock47
Spock47 Spock47

2021/12/6

#
Note: I additionally tested the scenario with having the hive in the middle of the world:
addObject(new Hive(), 320, 200);
Maybe that additional free space in all directions helps the bees to fly less confused.
xixEmilyxix xixEmilyxix

2021/12/6

#
thank you
You need to login to post a reply.