Unexpected Results Using Repast Simphony

I need to develop a Java version of the Iterated Prisoner dilemma using Repast Simphony as a simulator.

The idea is that each Player is an agent, and we have a nxn Player grid that cannot be migrated. Each Player must play with 4 neighbors (north, south, west and east), finding the optimal strategy based on the outcome of 4 different games in each round.

Since there is no built-in system for exchanging messages between agents in Repast Simphony, I had to implement some workaround for working with agent synchronization (A vs B and B vs A should be considered the same round, which is why they need to be synchronized).

This is done by viewing each round as:

  • Player I choose the next move for each of the 4 enemies
  • Player I send the right move to each of the 4 enemies
  • Player I look forward to hearing from each of the 4 enemies.

In my understanding of Repast Simphony, the planned methods are sequential (without the parallelism agent level), which means that I have to wait in a different way from the sender (scheduled with a lower rating, to ensure that all shipments are completed before the wait starts).

The problem is that, despite receiving all 4 expected messages (at least this is what is being printed), after starting the wait method, it reports less than 4 received elements.

Here's the code taken from the Player class:

 // myPoint is the location inside the grid (unique, agents can't move and only one per cell is allowed) public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((myPoint == null) ? 0 : myPoint.hashCode()); return result; } // Returns enemy choice in the previous round private byte getLastPlay(Player enemy) { return (neighbors.get(enemy)[1]) ? COOPERATE : DEFECT; } // Elements are saved as (player, choice) private void receivePlay(Player enemy, byte play) { System.out.println(this + " receives (" + play + ") from " + enemy); while (!playSharedQueue.add(new Object[] { enemy, play })){ // This doesn't get printed, meaning that the insertion is successful! System.out.println(this + " failed inserting"); } } @ScheduledMethod(start = 1, interval = 1, priority = 10) public void play() { System.out.println(this + " started playing"); // Clear previous plays playSharedQueue.clear(); for (Player enemy : neighbors.keySet()) { // properties[0] = true if we already played together // properties[1] = true if enemy choose to cooperate on the previous round Boolean[] properties = neighbors.get(enemy); // Choose which side we take this time byte myPlay; if (properties[0]) { // First time that we play, use memory-less strategy myPlay = (Math.random() <= strategy[0]) ? COOPERATE : DEFECT; // Report that we played properties[0] = false; neighbors.put(enemy, properties); } else { // We already had a round, use strategy with memory byte enemyLastPlay = enemy.getLastPlay(this); // Choose which side to take based on enemy previous decision myPlay = (Math.random() <= strategy[(enemyLastPlay) == COOPERATE ? 1 : 2]) ? COOPERATE : DEFECT; } // Send my choice to the enemy System.out.println(this + " sent (" + myPlay + ") to " + enemy); enemy.receivePlay(this, myPlay); } } // Waits for the results and processes them @ScheduledMethod(start = 1, interval = 1, priority = 5) public void waitResults() { // Clear previous score lastPayoff = 0; System.out.println(this + " waits for results [" + playSharedQueue.size() + "]"); if (playSharedQueue.size() != 4) { // Well, this happens on the first agent :( System.exit(1); } // ... process ... } 

Here's the console output so you can see that everything seems to be sent and received without problems (using a 3 x 3 grid):

 Player[2, 0] started playing Player[2, 0] sent (0) to Player[2, 1] Player[2, 1] receives (0) from Player[2, 0] Player[2, 0] sent (0) to Player[2, 2] Player[2, 2] receives (0) from Player[2, 0] Player[2, 0] sent (0) to Player[0, 0] Player[0, 0] receives (0) from Player[2, 0] Player[2, 0] sent (0) to Player[1, 0] Player[1, 0] receives (0) from Player[2, 0] Player[1, 2] started playing Player[1, 2] sent (1) to Player[2, 2] Player[2, 2] receives (1) from Player[1, 2] Player[1, 2] sent (1) to Player[0, 2] Player[0, 2] receives (1) from Player[1, 2] Player[1, 2] sent (1) to Player[1, 0] Player[1, 0] receives (1) from Player[1, 2] Player[1, 2] sent (1) to Player[1, 1] Player[1, 1] receives (1) from Player[1, 2] Player[0, 2] started playing Player[0, 2] sent (1) to Player[2, 2] Player[2, 2] receives (1) from Player[0, 2] Player[0, 2] sent (1) to Player[0, 0] Player[0, 0] receives (1) from Player[0, 2] Player[0, 2] sent (1) to Player[0, 1] Player[0, 1] receives (1) from Player[0, 2] Player[0, 2] sent (1) to Player[1, 2] Player[1, 2] receives (1) from Player[0, 2] Player[0, 1] started playing Player[0, 1] sent (1) to Player[2, 1] Player[2, 1] receives (1) from Player[0, 1] Player[0, 1] sent (1) to Player[0, 0] Player[0, 0] receives (1) from Player[0, 1] Player[0, 1] sent (1) to Player[0, 2] Player[0, 2] receives (1) from Player[0, 1] Player[0, 1] sent (1) to Player[1, 1] Player[1, 1] receives (1) from Player[0, 1] Player[1, 0] started playing Player[1, 0] sent (0) to Player[2, 0] Player[2, 0] receives (0) from Player[1, 0] Player[1, 0] sent (0) to Player[0, 0] Player[0, 0] receives (0) from Player[1, 0] Player[1, 0] sent (0) to Player[1, 1] Player[1, 1] receives (0) from Player[1, 0] Player[1, 0] sent (0) to Player[1, 2] Player[1, 2] receives (0) from Player[1, 0] Player[1, 1] started playing Player[1, 1] sent (0) to Player[2, 1] Player[2, 1] receives (0) from Player[1, 1] Player[1, 1] sent (0) to Player[0, 1] Player[0, 1] receives (0) from Player[1, 1] Player[1, 1] sent (0) to Player[1, 0] Player[1, 0] receives (0) from Player[1, 1] Player[1, 1] sent (0) to Player[1, 2] Player[1, 2] receives (0) from Player[1, 1] Player[2, 2] started playing Player[2, 2] sent (0) to Player[2, 0] Player[2, 0] receives (0) from Player[2, 2] Player[2, 2] sent (0) to Player[2, 1] Player[2, 1] receives (0) from Player[2, 2] Player[2, 2] sent (0) to Player[0, 2] Player[0, 2] receives (0) from Player[2, 2] Player[2, 2] sent (0) to Player[1, 2] Player[1, 2] receives (0) from Player[2, 2] Player[0, 0] started playing Player[0, 0] sent (1) to Player[2, 0] Player[2, 0] receives (1) from Player[0, 0] Player[0, 0] sent (1) to Player[0, 1] Player[0, 1] receives (1) from Player[0, 0] Player[0, 0] sent (1) to Player[0, 2] Player[0, 2] receives (1) from Player[0, 0] Player[0, 0] sent (1) to Player[1, 0] Player[1, 0] receives (1) from Player[0, 0] Player[2, 1] started playing Player[2, 1] sent (1) to Player[2, 0] Player[2, 0] receives (1) from Player[2, 1] Player[2, 1] sent (1) to Player[2, 2] Player[2, 2] receives (1) from Player[2, 1] Player[2, 1] sent (1) to Player[0, 1] Player[0, 1] receives (1) from Player[2, 1] Player[2, 1] sent (1) to Player[1, 1] Player[1, 1] receives (1) from Player[2, 1] Player[2, 2] waits for results [1] 

As you can see in the last line, playSharedQueue.size() is 1 , and I really don't understand why.

If the method calls are sequential, waitResults () methos is invoked after the 9 play () `execute, and given that each of them sends 4 messages correctly, I cannot find the reason why this size is still 1.

Of course, everything consistent means that there are no problems with synchronization , even if I had the same problem using LinkedBlockingQueue instead of HashSet .

Do you have any hints of this?

+7
java repast-simphony
source share
1 answer

After a while, I reopened the code, and I found out that I was making a simple but serious mistake:

 @ScheduledMethod(start = 1, interval = 1, priority = 10) public void play() { System.out.println(this + " started playing"); // Clear previous plays playSharedQueue.clear(); 

Running playSharedQueue.clear(); to clear the previous results, but since consecutive calls are secondary, the second player will call him after the first has sent him his game, so that the game will be discarded.

Moving this line at the end of waitResults resolved it.

+2
source share

All Articles