Java – unexpected results using repast Simphony
I need to use repast Simphony as a simulator to develop the Java version of the iterative prisoner's dilemma
The idea is that each player is an agent and we have an n x n player grid that can't be moved Each player must play with 4 neighbors (North, South, West and East) to find the best strategy according to the results of 4 different games in each round
Since there is no built-in system to exchange messages between agents in repast Simphony, I have to implement some solution to deal with agent synchronization (a vs B and B vs a should be counted as the same round, which is why they need to synchronize)
This is done by treating each round as:
>Player I select the next action for each of the 4 enemies > player I send the correct action to each of the 4 enemies > player I wait for each of the 4 enemies to reply
According to my understanding of repast Simphony, the scheduled method is sequential (there is no agent level parallelism), which means that I am forced to wait in a different way from the sending method (arranged with a lower priority to ensure that all transmissions are completed) before starting the waiting)
The problem here is that although all four expected messages are received (at least this is the printed content), once the waiting method starts, it reports fewer than four receiving elements
This is the code obtained 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's 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's 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,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 ... }
This is 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,2] Player[2,2] receives (0) from Player[2,0] sent (0) to Player[0,0] Player[0,0] receives (0) from Player[2,0] sent (0) to Player[1,0] Player[1,2] started playing Player[1,2] sent (1) to Player[2,2] receives (1) from Player[1,2] Player[1,2] sent (1) to Player[0,2] Player[0,2] sent (1) to Player[1,0] receives (1) from Player[1,1] Player[1,1] receives (1) from Player[1,2] started playing Player[0,2] receives (1) from Player[0,0] receives (1) from Player[0,1] Player[0,1] receives (1) from Player[0,1] started playing Player[0,1] sent (1) to Player[2,1] sent (1) to Player[0,1] sent (1) to Player[1,0] started playing Player[1,0] receives (0) from Player[1,1] receives (0) from Player[1,2] receives (0) from Player[1,1] started playing Player[1,1] sent (0) to Player[2,1] sent (0) to Player[0,1] sent (0) to Player[1,2] started playing Player[2,2] sent (0) to Player[2,2] sent (0) to Player[0,2] sent (0) to Player[1,0] started playing Player[0,0] sent (1) to Player[2,0] sent (1) to Player[0,0] sent (1) to Player[1,1] started playing Player[2,0] receives (1) from Player[2,2] receives (1) from Player[2,1] receives (1) from Player[2,2] waits for results [1]
As you can see in the last line, playsharedqueue Size () is 1. I really don't understand why
If the method call is sequential, then the waitResults () method is called after 9play () 'execution, and assuming that each of the 4 messages is sent correctly, I can not find the reason that the size is still 1.
Of course, all sequences mean no synchronization problems, even if I use linkedblockingqueue instead of HashSet
Do you have any hints?
Solution
After a while, I opened the code again and found that I made a simple and serious error:
@ScheduledMethod(start = 1,priority = 10) public void play() { System.out.println(this + " started playing"); // Clear prevIoUs plays playSharedQueue.clear();
playSharedQueue. clear(); The execution is done to clear the previous result, but since the call is continuous, the second game player will call the game player after he sends his game to him, so as to abandon the game.
Moving the row at the end of waitresults resolves it