In this project, I created an Entity that knows its position and type and a Landscape that holds a 2D array and can manipulate Entities within it. A provided Simulation class created a Landscape, populated it with Entities, and then used its own instance methods to describe how each Entity should move for each iteration in the Landscape.
Entity was very straightforward since its methods were all accessor and mutator methods, except for toString() which simply returned the String representation of its type int.
Landscape was simple to initialize, as it only needed a 2D array to be created. I made the toString() method according to the algorithm on the assignment page. It said to loop over the array calling toString on Entities and using '.' for null objects, and put in a new line character at the end of each row.
Simulation was more interesting, though it's basic implementation was provided. In order to make Entities prefer others of different types, I only had to change the < to a > in the if statement that determined whether or not the Entity would move (instead of moving if alike>different, it needed to move if alike <different; using an '=' changes the implementation slightly). This change didn't seem desirable, however, because it makes the Entities maintain a rather random pattern so that the final state looks like an initial state.
Analysis of the behavior of Simulation using its EntityUpdateRule
These are the results of the original numbers (.4 filled, 4 types, attracted to like types), except that many more iterations were performed. They show that the Entities form groups.
These are the results for a long simulation that is 80% filled. I was surprised that the Entities were able to manuver so well within the tight space.
In a densly filled Landscape, when there are only two types of Entities, they form only a few very distinct groups
Since this reminded me so much of Gridworld, the AP CS Case Study, I decided to make a Critter (named FlockingEntity) that acted like an Entity so that I could see the movements. In order to enable the Critters to reach an end state, I removed the chance that a happy Critter would move. I added a count variable to the FlockingEntity class so that each critter would keep track of how many times it had a chance to move (the number of iterations). I also created a static variable that would detect when all the Critters had stopped moving. I could then run a program that ran 10 trials, stopping each one when equilibrium was reached, and then find the average number of iterations it took to reach that point. I also counted how many groups were formed by looking at the resulting images.
This is a sample image of the result for percentFilled=.4, numTypes=4, 30X15. It took about 1000 iterations, though many of them were the time it took for a single yellow Critter to find a group.
The average number of steps for this type of simulation is 558.9 iterations, and the average number of groups is 22. This means that the simulation above is abnormal because it has slightly fewer groups and it took much longer for the critters to form groups. Perhaps the two are related, but it could just have been bad luck on the one yellow critter's part.
I decided to make different types of Entities have different rules. I gave each type of Entity a ban on moving in a particular direction: 0's could not move down (or down diagonally), 1's couldn't move left, 2's couldn't move up, 3's couldn't move right. I achieved this by creating two ArrayLists: one for valid x-movements, and one for valid y-movements. Using a switch statement, I altered the ArrayLists to hold only valid movements. For an Entity that would be able to move anywhere, both ArrayLists would hold the values-1,0,1; representing the ability to change its row and column values by 1 or none. Each type of Entity removed one of these values. I used the values in the ArrayLists to determine where the Entity would move to in the statement "int xCor = e.Col()+xDirections.get(spinner.nextInt(xDirections.size()));" and the corresponding yCor statement. I made this extension in a class named Simulation2 which extended Simulation and overrode the EntitiyUpdateRule(Entity e, Landscape scape). Then all I had to do to test it was change the main() in Simulation to make sim an instance of Simulation2.
A sample run with 2500 iterations yielded
I also implemented this action in Gridworld
It was interesting to see the results of changing the percent filled and number of types, especially that rough groups were formed even when there were only a few open spaces. I also was glad that I could use Gridworld to watch the step-by-step movements, even though I had to set it very fast in order to see it finish. I found it odd that the EntityUpdateRule was a part of the simulation, rather than Entity. If it were a method in Entity, it would be easier to have it treat different types differently. It seems out of place in Simulation, but that is probably because I am used to the structure of Gridworld which encourages extensions of Actor (of which Critter is one) more than World, which corresponds in some ways to Simulation.