code: package ontology.sprites.npc;
  
  import java.awt.Dimension;
  import java.awt.Rectangle;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.Random;
  
  import core.vgdl.VGDLRegistry;
  import core.vgdl.VGDLSprite;
  import core.content.SpriteContent;
  import core.game.Game;
  import ontology.Types;
  import tools.Direction;
  import tools.Utils;
  import tools.Vector2d;
  
  /**
   * Created with IntelliJ IDEA.
   * User: Diego
   * Date: 21/10/13
   * Time: 18:14
   * This is a Java port from Tom Schaul's VGDL - https://github.com/schaul/py-vgdl
   */
  public class AlternateChaser extends RandomNPC
  {
      public boolean fleeing;
      public String stype1;
      public String stype2;
      public int[] itype1;
      public int[] itype2;
  
      ArrayList<VGDLSprite> targets;
      ArrayList<Direction> actions;
  
      public AlternateChaser(){}
  
      public AlternateChaser(Vector2d position, Dimension size, SpriteContent cnt)
      {
          //Init the sprite
          this.init(position, size);
  
          //Specific class default parameter values.
          loadDefaults();
  
          //Parse the arguments.
          this.parseParameters(cnt);
      }
  
      protected void loadDefaults()
      {
          super.loadDefaults();
          fleeing = false;
          targets = new ArrayList<VGDLSprite>();
          actions = new ArrayList<Direction>();
      }
  
      public void postProcess()
      {
          super.postProcess();
          //Define actions here.
          String[] stypes1 = stype1.split(",");
          itype1 = new int[stypes1.length];
          String[] stypes2 = stype1.split(",");
          itype2 = new int[stypes2.length];
  
          for (int i = 0; i<stypes1.length; i++) {
              itype1[i] = VGDLRegistry.GetInstance().getRegisteredSpriteValue(stypes1[i]);
          }
  
          for (int i = 0; i<stypes2.length; i++) {
              itype2[i] = VGDLRegistry.GetInstance().getRegisteredSpriteValue(stypes2[i]);
          }
  
      }
  
      public void update(Game game)
      {
          actions.clear();
  
          //passive moment.
          super.updatePassive();
  
          //Get the closest targets
          closestTargets(game, false);
          for(VGDLSprite target : targets)
          {
              //Update the list of actions that moves me towards each target
              movesToward(target);
          }
  
          //Choose randomly an action among the ones that allows me to chase.
          Direction act;
          if(actions.size() == 0)
          {
              //unless, no actions really take me closer to anybody!
              act = (Direction) Utils.choice(Types.DBASEDIRS,game.getRandomGenerator());
          }else{
              act = Utils.choiceDir(actions, game.getRandomGenerator());
          }
  
          //Apply the action to move.
          this.physics.activeMovement(this, act, this.speed);
      }
  
      protected void movesToward(VGDLSprite target)
      {
          double distance = this.physics.distance(rect, target.rect);
          for(Direction act : Types.DBASEDIRS)
          {
              //Calculate the distance if I'd apply this move.
              Rectangle r = new Rectangle(this.rect);
              r.translate((int)act.x(), (int)act.y());
              double newDist = this.physics.distance(r, target.rect);
  
              //depending on getting me closer/farther, if I'm fleeing/chasing, add move:
              if(fleeing && distance<newDist)
                  actions.add(act);
              if(!fleeing && distance>newDist)
                  actions.add(act);
          }
      }
  
      /**
       * Sets a list with the closest targets (sprites with the type 'stype'), by distance
       * @param game game to access all sprites
       * @param randomTarget if true, a random target is added to 'targets'. Otherwise, only the closest one is.
       */
      protected void closestTargets(Game game, boolean randomTarget)
      {
          targets.clear();
          double bestDist = Double.MAX_VALUE;
  
          int targetSpriteId = -1;
          int numChasing = 0;
          for (int i = 0; i < itype1.length; i++)
              numChasing += game.getNumSprites(itype1[i]);
          int numFleeing = 0;
          for (int i = 0; i < itype2.length; i++)
              numChasing += game.getNumSprites(itype2[i]);
  
          if(numChasing > numFleeing)
          {
              targetSpriteId = itype1[new Random().nextInt(itype1.length)];
              fleeing = false;
          }else if(numFleeing > numChasing)
          {
              targetSpriteId = itype2[new Random().nextInt(itype2.length)];
              fleeing = true;
          }
  
          if(targetSpriteId != -1)
          {
              Iterator<VGDLSprite> spriteIt = game.getSubSpritesGroup(targetSpriteId);
              if(spriteIt != null) while(spriteIt.hasNext())
              {
                  VGDLSprite s = spriteIt.next();
  
                  if(randomTarget)
                  {
                      targets.add(s);
                  }else{
                      double distance = this.physics.distance(rect, s.rect);
                      if(distance < bestDist)
                      {
                          bestDist = distance;
                          targets.clear();
                          targets.add(s);
                      }else if(distance == bestDist){
                          targets.add(s);
                      }
                  }
              }
          }
  
          if(randomTarget)
          {
              VGDLSprite sel = targets.get(game.getRandomGenerator().nextInt(targets.size()));
              targets.clear();
              targets.add(sel);
          }
      }
  
  
      public VGDLSprite copy()
      {
          AlternateChaser newSprite = new AlternateChaser();
          this.copyTo(newSprite);
          return newSprite;
      }
  
      public void copyTo(VGDLSprite target)
      {
          AlternateChaser targetSprite = (AlternateChaser) target;
          targetSprite.fleeing = this.fleeing;
          targetSprite.stype1 = this.stype1;
          targetSprite.stype2 = this.stype2;
          targetSprite.itype1 = this.itype1.clone();
          targetSprite.itype2 = this.itype2.clone();
          targetSprite.targets = new ArrayList<VGDLSprite>();
          targetSprite.actions = new ArrayList<Direction>();
          super.copyTo(targetSprite);
      }
  
      @Override
      public ArrayList<String> getDependentSprites(){
      	ArrayList<String> result = new ArrayList<String>();
          String[] stypes1 = stype1.split(",");
          String[] stypes2 = stype2.split(",");
  
      	if(stypes1.length > 0) {
      	    for (String s : stypes1)
      	        result.add(s);
          }
      	if(stypes2.length > 0) {
              for (String s : stypes2)
                  result.add(s);
          }
      	
      	return result;
      }
      
  }
  