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 enemiesPlayer I send the right move to each of the 4 enemiesPlayer 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?