Methods
[Agent practical 3 of 9]


Ok, so now we'll shift the behaviour for the agents from Model.java into Agent.java.


 

Looking at Model class, you should have something like this:

for (int i = 0; i < numberOfIterations; i++) {
   for (int j = 0; j < numberOfAgents; j++) {
      // Code to move agents[j].x.
      // Code to move agents[j].y.
      // Code to eat world.data at agents[j].x, agents[j].y.
      System.out.println("value at " + x + " " + y + " is " + world.data[y][x]);
   }
}

This is ok, but it isn't well structured. One of the key ideas with Object Orientated Programming is that the functionality associated with a class should be inside that class. This makes classes easier to reuse and to swap in and out of code. Given this, we'd really like to put the agent-centred code in Agent, and get this code down to:

for (int i = 0; i < numberOfIterations; i++) {
   for (int j = 0; j < numberOfAgents; j++) {
      agents[j].run();
      System.out.println("value at " + agents[j].x + " " + agents[j].y + " is " + world.data[agents[j].y][agents[j].x]);
   }
}

This is also much cleaner, simplifying our Model.java to the core functions we expect of it and keeping the complicated stuff hidden from this class and anyone reading it -- they'll know to look in Agent.java if they need more detail.

In order to implement the above, we need to shift the removed code into a new "run()" method within Agent.java:

public class Agent {

   int x = 50;
   int y = 150;
   private Environment world = null;

   public Agent(Environment worldIn) {

      world = worldIn;

   }

   public void run() {

      // Code to move agents[j].x.
      // Code to move agents[j].y.
      // Code to eat world.data at agents[j].y, agents[j].x.
   }

}

Before you do this, however, note a couple of things. Firstly, because we're now inside the Agent class, not the loops inside Model, agents[j].x won't work -- neither agents[] nor j exist as labels inside Agent.java, let alone as *these* labels, which are specific to Model.java. In each case, you need to replace the agent[j] with "this". "this" is a Java keyword refering to the object which you are inside. It is mainly used when you have a method variable with the same name as an instance variable; this.x then refers to the instance variable x, rather than the method variable x, which otherwise takes precedence:

// Don't need this yet - just an example.

int x = 0;
void setX(int x) {
   this.x = x;
}

is the same as:

int x = 0;
void setX(int xIn) {
   x = xIn;
}

Anyhow, in our code, we currently have agents[j] referring to the agent, but now we're inside the agent, we need to change it to this. For example,

int x = agents[j].x;

in Model.java, becomes:

int x = this.x;

in Agent.java. Note that because we've used the same label for the Environment object world, we don't need to rename that here.

Secondly, note that we've let the instance variables x and y as not private for the moment, so they can still be directly accessed by Model.java.

It may seem a bit confusing to have two x and y values in our agent's run() method (x and this.x). However, they are different variables doing different jobs. The class-level instance variable this.x is keeping track of where we are now, and the method-level variable x is holding where we are considering going. We could rename the latter, so either of the following bits of code would be fine:

int x = 0;

do {
   x = this.x;
   double randomNumber = Math.random();
   if (randomNumber < 0.33) x--;
   if (randomNumber > 0.66) x++;
} while ((x < 0) || (x > world.getWidth() - 1));

this.x = x;

int nextX = 0;

do {
   nextX = this.x;
   double randomNumber = Math.random();
   if (randomNumber < 0.33) nextX--;
   if (randomNumber > 0.66) nextX++;
} while ((nextX < 0) || (nextX > world.getWidth() - 1));

this.x = nextX;

or, indeed, as we no longer have a local "x", we could just do the following, though it seems to reverse everything we've done before, rather confusingly!

int nextX = 0;

do {
   nextX = x;
   double randomNumber = Math.random();
   if (randomNumber < 0.33) nextX--;
   if (randomNumber > 0.66) nextX++;
} while ((nextX < 0) || (nextX > world.getWidth() - 1));

x = nextX;

With these caveats in mind, have a go at implementing the above run() method, and getting Model.java to use it.


Once you've got that working, we'll finish off by tidying up the code.