[virtualcommons-svn] commit/foraging: alllee: persisting entire trust game log, adding trust game u
Status: Beta
Brought to you by:
alllee
From: Bitbucket <com...@bi...> - 2012-01-17 03:24:03
|
1 new commit in foraging: https://bitbucket.org/virtualcommons/foraging/changeset/469558236f7e/ changeset: 469558236f7e user: alllee date: 2012-01-17 04:23:32 summary: persisting entire trust game log, adding trust game update events for client and facilitator, and some refactoring of trust game calculation algorithm. the second player is now randomly selected from all the participants that have already played the trust game, not just the first one. Also fixed a bug where the second time a player participated in the trust game would give them additional earnings and another trust log entry. affected #: 5 files diff -r f7ce903339bbb1d0a6e967b86221ed4a3ad647fb -r 469558236f7e014f837f5fb345504aa5bb6c975e src/main/java/edu/asu/commons/foraging/event/TrustGameResultsClientEvent.java --- /dev/null +++ b/src/main/java/edu/asu/commons/foraging/event/TrustGameResultsClientEvent.java @@ -0,0 +1,20 @@ +package edu.asu.commons.foraging.event; + +import edu.asu.commons.event.AbstractEvent; +import edu.asu.commons.foraging.model.ClientData; + +/** + * Used to update clients with their trust game log. + */ +public class TrustGameResultsClientEvent extends AbstractEvent { + private static final long serialVersionUID = -9129989958983083574L; + + private final ClientData clientData; + public TrustGameResultsClientEvent(ClientData clientData, String log) { + super(clientData.getId(), log); + this.clientData = clientData; + } + public ClientData getClientData() { + return clientData; + } +} diff -r f7ce903339bbb1d0a6e967b86221ed4a3ad647fb -r 469558236f7e014f837f5fb345504aa5bb6c975e src/main/java/edu/asu/commons/foraging/event/TrustGameResultsFacilitatorEvent.java --- /dev/null +++ b/src/main/java/edu/asu/commons/foraging/event/TrustGameResultsFacilitatorEvent.java @@ -0,0 +1,37 @@ +package edu.asu.commons.foraging.event; + +import java.util.List; +import java.util.Map; + +import edu.asu.commons.event.AbstractPersistableEvent; +import edu.asu.commons.foraging.model.ClientData; +import edu.asu.commons.net.Identifier; + +/** + * Persistable event usd to update the facilitator and store all the trust game results for the given round. + */ +public class TrustGameResultsFacilitatorEvent extends AbstractPersistableEvent { + + private static final long serialVersionUID = 5834548819829135618L; + private List<String> allTrustGameResults; + private Map<Identifier, ClientData> clientDataMap; + public TrustGameResultsFacilitatorEvent(Identifier facilitatorId, + Map<Identifier, ClientData> clientDataMap, List<String> allTrustGameResults) { + super(facilitatorId); + this.allTrustGameResults = allTrustGameResults; + this.clientDataMap = clientDataMap; + } + public List<String> getAllTrustGameResults() { + return allTrustGameResults; + } + public void setAllTrustGameResults(List<String> allTrustGameResults) { + this.allTrustGameResults = allTrustGameResults; + } + public Map<Identifier, ClientData> getClientDataMap() { + return clientDataMap; + } + public void setClientDataMap(Map<Identifier, ClientData> clientDataMap) { + this.clientDataMap = clientDataMap; + } + +} diff -r f7ce903339bbb1d0a6e967b86221ed4a3ad647fb -r 469558236f7e014f837f5fb345504aa5bb6c975e src/main/java/edu/asu/commons/foraging/model/ClientData.java --- a/src/main/java/edu/asu/commons/foraging/model/ClientData.java +++ b/src/main/java/edu/asu/commons/foraging/model/ClientData.java @@ -585,7 +585,7 @@ } public String toString() { - return String.format("id (%s) assigned number (%d)", id, assignedNumber); + return String.format("[%s #%d]", id, assignedNumber); } public void addTrustGameEarnings(double trustGameEarnings) { diff -r f7ce903339bbb1d0a6e967b86221ed4a3ad647fb -r 469558236f7e014f837f5fb345504aa5bb6c975e src/main/java/edu/asu/commons/foraging/model/ServerDataModel.java --- a/src/main/java/edu/asu/commons/foraging/model/ServerDataModel.java +++ b/src/main/java/edu/asu/commons/foraging/model/ServerDataModel.java @@ -414,8 +414,11 @@ double amountSent = 1.0d - playerOneAmountToKeep; StringBuilder builder = new StringBuilder(); builder.append(String.format("%s (Player 1) sent %s to %s (Player 2)\n", playerOne, CURRENCY_FORMATTER.format(amountSent), playerTwo)); + + double playerOneEarnings = playerOneAmountToKeep; + double playerTwoEarnings = 0.0d; + double amountReturnedToPlayerOne = 0.0d; if (amountSent > 0) { - double playerTwoEarnings = 0.0d; int index = 0; if (amountSent == 0.25d) { index = 0; @@ -428,29 +431,43 @@ } playerTwoEarnings = playerTwoAmountsToKeep[index]; double totalAmountSent = 3 * amountSent; - double amountReturnedToP1 = totalAmountSent - playerTwoEarnings; - double playerOneEarnings = playerOneAmountToKeep + amountReturnedToP1; - String playerOneLog = String.format("%s (Player 1) kept %s and received %s back from Player two for a total of %s", - playerOne, - CURRENCY_FORMATTER.format(playerOneAmountToKeep), - CURRENCY_FORMATTER.format(amountReturnedToP1), - CURRENCY_FORMATTER.format(playerOneEarnings)); - builder.append(playerOneLog).append("\n"); - playerOne.logTrustGame(playerOneLog); - playerOne.addTrustGameEarnings(playerOneAmountToKeep + amountReturnedToP1); - String playerTwoLog = String.format("%s (Player 2) and earned %s", playerTwo, playerTwoEarnings); - builder.append(playerTwoLog).append("\n"); - playerTwo.logTrustGame(playerTwoLog); - playerTwo.addTrustGameEarnings(playerTwoEarnings); + amountReturnedToPlayerOne = totalAmountSent - playerTwoEarnings; + playerOneEarnings += amountReturnedToPlayerOne; } - else { - String playerOneLog = String.format("%s (Player 1) sent nothing to Player 2 and earned %s", playerOne, playerOneAmountToKeep); - playerOne.logTrustGame(playerOneLog); - playerOne.addTrustGameEarnings(playerOneAmountToKeep); - playerTwo.logTrustGame(playerOneLog + " - you were player two and didn't receive anything."); + String playerOneLog = String.format("Player 1 kept %s and received %s back from Player 2 for a total of %s", + CURRENCY_FORMATTER.format(playerOneAmountToKeep), + CURRENCY_FORMATTER.format(amountReturnedToPlayerOne), + CURRENCY_FORMATTER.format(playerOneEarnings)); + playerOne.logTrustGame("You were player one. " + playerOneLog); + playerOne.addTrustGameEarnings(playerOneEarnings); + builder.append(playerOne).append(playerOneLog).append("\n"); + if (shouldLogPlayerTwo(playerOne, playerTwo)) { + String playerTwoLog = String.format("Player 2 received %s and sent back %s to Player 1, earning %s", + CURRENCY_FORMATTER.format(amountSent), + CURRENCY_FORMATTER.format(amountSent - playerTwoEarnings), + CURRENCY_FORMATTER.format(playerTwoEarnings)); + playerTwo.logTrustGame(playerTwoLog); + playerTwo.addTrustGameEarnings(playerTwoEarnings); + builder.append(playerTwoLog).append("\n"); } return builder.toString(); } + + /** + * Returns true if player two has not yet participated in the trust game as a player one, in which case their trust game log + * would already have an entry and so its size would be equivalent to the size of the player one log. In most cases it should + * always be 1 smaller than the player one trust game log + * + * @param playerOne + * @param playerTwo + * @return + */ + private boolean shouldLogPlayerTwo(ClientData playerOne, ClientData playerTwo) { + logger.info(String.format("%s (P1) log: %s vs. %s (P2) log: %s", playerOne, playerOne.getTrustGameLog(), + playerTwo, playerTwo.getTrustGameLog())); + // is this sufficient? + return playerTwo.getTrustGameLog().size() < playerOne.getTrustGameLog().size(); + } @Override public List<Identifier> getAllClientIdentifiers() { diff -r f7ce903339bbb1d0a6e967b86221ed4a3ad647fb -r 469558236f7e014f837f5fb345504aa5bb6c975e src/main/java/edu/asu/commons/foraging/server/ForagingServer.java --- a/src/main/java/edu/asu/commons/foraging/server/ForagingServer.java +++ b/src/main/java/edu/asu/commons/foraging/server/ForagingServer.java @@ -7,10 +7,11 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.logging.FileHandler; import java.util.logging.Handler; @@ -62,6 +63,8 @@ import edu.asu.commons.foraging.event.ShowRequest; import edu.asu.commons.foraging.event.SurveyIdSubmissionRequest; import edu.asu.commons.foraging.event.SynchronizeClientEvent; +import edu.asu.commons.foraging.event.TrustGameResultsClientEvent; +import edu.asu.commons.foraging.event.TrustGameResultsFacilitatorEvent; import edu.asu.commons.foraging.event.TrustGameSubmissionEvent; import edu.asu.commons.foraging.event.TrustGameSubmissionRequest; import edu.asu.commons.foraging.event.UnlockResourceRequest; @@ -123,6 +126,8 @@ // private Duration chatDuration; private volatile boolean experimentStarted; + + private Random random = new Random(); // FIXME: add the ability to reconfigure an already instantiated server public ForagingServer() { @@ -247,7 +252,9 @@ public void handle(ConnectionEvent event) { // handle incoming connections if (experimentStarted) { - // do not allow any new connections + // currently not allowing any new connections + // FIXME: would be nice to allow for reconnection / reassociation of clients to ids and data. + // should be logged however so we can remember the context of the data transmit(new ClientMessageEvent(event.getId(), "The experiment has already started, we cannot add you at this time.")); return; } @@ -647,37 +654,17 @@ } // FIXME: groups have not been assigned in the transition between practice round and this round.. if (numberOfSubmissions >= clients.size()) { - // once all clients have submitted their decisions, execute the trust game. - for (GroupDataModel group : serverDataModel.getGroups()) { - LinkedList<ClientData> clientList = new LinkedList<ClientData>(group.getClientDataMap().values()); - Collections.shuffle(clientList); - logger.info("TRUST GAME shuffled client list: " + clientList); - ClientData first = clientList.getFirst(); - for (Iterator<ClientData> iter = clientList.iterator(); iter.hasNext();) { - ClientData playerOne = iter.next(); - ClientData playerTwo = first; - if (iter.hasNext()) { - playerTwo = iter.next(); - } - logger.info("TRUST GAME: about to pair " + playerOne + " with " + playerTwo); - String trustGameResults = serverDataModel.calculateTrustGame(playerOne, playerTwo); - sendFacilitatorMessage(String.format("Pairing %s with %s for trust game resulted in:\n\t %s", playerOne, playerTwo, - trustGameResults)); - } - } - transmit(new FacilitatorEndRoundEvent(facilitatorId, serverDataModel)); + // once all clients have submitted their decisions, execute the trust game. + processTrustGame(); numberOfSubmissions = 0; } } + }); addEventProcessor(new EventTypeProcessor<BeginChatRoundRequest>(BeginChatRoundRequest.class) { public void handle(BeginChatRoundRequest request) { if (getCurrentRoundConfiguration().isChatEnabled()) { - // FIXME: need to handle properly corner case where chat is enabled before the first round - // - at that point the clients haven't been added to any groups yet. - // another way to handle this is to have the clients added - // to groups when the show instructions request is handled.. sendFacilitatorMessage("Sending begin chat round request to all participants"); for (Map.Entry<Identifier, ClientData> entry : clients.entrySet()) { Identifier id = entry.getKey(); @@ -689,6 +676,45 @@ }); // FIXME: handle reconfiguration requests from facilitator } + + protected void processTrustGame() { + List<String> allTrustGameResults = new ArrayList<String>(); + for (GroupDataModel group : serverDataModel.getGroups()) { + LinkedList<ClientData> clientList = new LinkedList<ClientData>(group.getClientDataMap().values()); + Collections.shuffle(clientList); + logger.info("TRUST GAME shuffled client list: " + clientList); + ClientData first = clientList.getFirst(); + + // using an iterator to consume both players and ensure that a player doesn't + // have the trust game calculated on them twice (except as a player 2 selection) + + boolean lastRound = getConfiguration().isLastRound(); + for (ListIterator<ClientData> iter = clientList.listIterator(); iter.hasNext();) { + ClientData playerOne = iter.next(); + ClientData playerTwo = first; + if (iter.hasNext()) { + playerTwo = iter.next(); + } + else { + // clumsy, see if we can express this differently + // why doesn't listIterator offer a currentIndex() method as well? + playerTwo = clientList.get(random.nextInt(iter.previousIndex() + 1)); + } + logger.info("TRUST GAME: about to pair " + playerOne + " with " + playerTwo); + String trustGameResult = serverDataModel.calculateTrustGame(playerOne, playerTwo); + allTrustGameResults.add(trustGameResult); + if (lastRound) { + transmit(new TrustGameResultsClientEvent(playerOne, trustGameResult)); + transmit(new TrustGameResultsClientEvent(playerTwo, trustGameResult)); + } + + sendFacilitatorMessage(String.format("Pairing %s with %s for trust game resulted in:\n\t %s", playerOne, playerTwo, + trustGameResult)); + } + } + // FIXME: update facilitator AND clients if it is the last round of the experiment + transmit(new TrustGameResultsFacilitatorEvent(facilitatorId, serverDataModel.getClientDataMap(), allTrustGameResults)); + } protected boolean isReadyToStartRound() { if (getCurrentRoundConfiguration().isQuizEnabled()) { @@ -772,6 +798,7 @@ // persister MUST be initialized early so that we store pre-round events like QuizResponseEvent, ChatEvent, and the various Ranking // requests. setupRound(); + initializeGroups(); sendFacilitatorMessage("Ready to show instructions and the start next round."); if (getCurrentRoundConfiguration().isQuizEnabled()) { getLogger().info("Waiting for all quizzes to be submitted."); @@ -782,6 +809,9 @@ startRound(); break; case WAITING_FOR_CONNECTIONS: + // while waiting for connections we must defer group initialization till all clients + // are connected (which is unknown, we allow clients to connect until the experiment has started) + setupRound(); sendFacilitatorMessage("Ready to show instructions and the start next round."); if (getCurrentRoundConfiguration().isQuizEnabled()) { getLogger().info("Waiting for all quizzes to be submitted."); @@ -789,7 +819,7 @@ } // then wait for the signal from the facilitator to actually start the round (a chat session might occur or a voting session). Utils.waitOn(roundSignal); - setupRound(); + initializeGroups(); startRound(); break; default: @@ -799,7 +829,6 @@ private void setupRound() { persister.initialize(getCurrentRoundConfiguration()); - initializeGroups(); } private void stopRound() { @@ -870,7 +899,7 @@ // for (GroupDataModel group : serverDataModel.getGroups()) { // for (ClientData clientData : group.getClientDataMap().values()) { // // ask each client if it wants to grab a token, wherever it is. -// clientData.collectToken(); +// group.collectToken(clientData); // } // } for (GroupDataModel group : serverDataModel.getGroups()) { Repository URL: https://bitbucket.org/virtualcommons/foraging/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. |